fixed minor bug with doors and envelope requests
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   /* ---------- initialize game engine snapshots ---------------------------- */
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     game.snapshot.last_action[i] = 0;
3041   game.snapshot.changed_action = FALSE;
3042   game.snapshot.mode =
3043     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3044      SNAPSHOT_MODE_EVERY_STEP :
3045      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3046      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3047 }
3048
3049 int get_num_special_action(int element, int action_first, int action_last)
3050 {
3051   int num_special_action = 0;
3052   int i, j;
3053
3054   for (i = action_first; i <= action_last; i++)
3055   {
3056     boolean found = FALSE;
3057
3058     for (j = 0; j < NUM_DIRECTIONS; j++)
3059       if (el_act_dir2img(element, i, j) !=
3060           el_act_dir2img(element, ACTION_DEFAULT, j))
3061         found = TRUE;
3062
3063     if (found)
3064       num_special_action++;
3065     else
3066       break;
3067   }
3068
3069   return num_special_action;
3070 }
3071
3072
3073 /*
3074   =============================================================================
3075   InitGame()
3076   -----------------------------------------------------------------------------
3077   initialize and start new game
3078   =============================================================================
3079 */
3080
3081 void InitGame()
3082 {
3083   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3084   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3085
3086   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3087   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3088   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3089   int initial_move_dir = MV_DOWN;
3090   int i, j, x, y;
3091
3092   game_status = GAME_MODE_PLAYING;
3093
3094   StopAnimation();
3095
3096   if (!game.restart_level)
3097     CloseDoor(DOOR_CLOSE_1);
3098
3099   if (level_editor_test_game)
3100     FadeSkipNextFadeIn();
3101   else
3102     FadeSetEnterScreen();
3103
3104   FadeOut(REDRAW_FIELD);
3105
3106   /* needed if different viewport properties defined for playing */
3107   ChangeViewportPropertiesIfNeeded();
3108
3109   DrawCompleteVideoDisplay();
3110
3111   InitGameEngine();
3112   InitGameControlValues();
3113
3114   /* don't play tapes over network */
3115   network_playing = (options.network && !tape.playing);
3116
3117   for (i = 0; i < MAX_PLAYERS; i++)
3118   {
3119     struct PlayerInfo *player = &stored_player[i];
3120
3121     player->index_nr = i;
3122     player->index_bit = (1 << i);
3123     player->element_nr = EL_PLAYER_1 + i;
3124
3125     player->present = FALSE;
3126     player->active = FALSE;
3127     player->mapped = FALSE;
3128
3129     player->killed = FALSE;
3130     player->reanimated = FALSE;
3131
3132     player->action = 0;
3133     player->effective_action = 0;
3134     player->programmed_action = 0;
3135
3136     player->score = 0;
3137     player->score_final = 0;
3138
3139     player->gems_still_needed = level.gems_needed;
3140     player->sokobanfields_still_needed = 0;
3141     player->lights_still_needed = 0;
3142     player->friends_still_needed = 0;
3143
3144     for (j = 0; j < MAX_NUM_KEYS; j++)
3145       player->key[j] = FALSE;
3146
3147     player->num_white_keys = 0;
3148
3149     player->dynabomb_count = 0;
3150     player->dynabomb_size = 1;
3151     player->dynabombs_left = 0;
3152     player->dynabomb_xl = FALSE;
3153
3154     player->MovDir = initial_move_dir;
3155     player->MovPos = 0;
3156     player->GfxPos = 0;
3157     player->GfxDir = initial_move_dir;
3158     player->GfxAction = ACTION_DEFAULT;
3159     player->Frame = 0;
3160     player->StepFrame = 0;
3161
3162     player->initial_element = player->element_nr;
3163     player->artwork_element =
3164       (level.use_artwork_element[i] ? level.artwork_element[i] :
3165        player->element_nr);
3166     player->use_murphy = FALSE;
3167
3168     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3169     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3170
3171     player->gravity = level.initial_player_gravity[i];
3172
3173     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3174
3175     player->actual_frame_counter = 0;
3176
3177     player->step_counter = 0;
3178
3179     player->last_move_dir = initial_move_dir;
3180
3181     player->is_active = FALSE;
3182
3183     player->is_waiting = FALSE;
3184     player->is_moving = FALSE;
3185     player->is_auto_moving = FALSE;
3186     player->is_digging = FALSE;
3187     player->is_snapping = FALSE;
3188     player->is_collecting = FALSE;
3189     player->is_pushing = FALSE;
3190     player->is_switching = FALSE;
3191     player->is_dropping = FALSE;
3192     player->is_dropping_pressed = FALSE;
3193
3194     player->is_bored = FALSE;
3195     player->is_sleeping = FALSE;
3196
3197     player->frame_counter_bored = -1;
3198     player->frame_counter_sleeping = -1;
3199
3200     player->anim_delay_counter = 0;
3201     player->post_delay_counter = 0;
3202
3203     player->dir_waiting = initial_move_dir;
3204     player->action_waiting = ACTION_DEFAULT;
3205     player->last_action_waiting = ACTION_DEFAULT;
3206     player->special_action_bored = ACTION_DEFAULT;
3207     player->special_action_sleeping = ACTION_DEFAULT;
3208
3209     player->switch_x = -1;
3210     player->switch_y = -1;
3211
3212     player->drop_x = -1;
3213     player->drop_y = -1;
3214
3215     player->show_envelope = 0;
3216
3217     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3218
3219     player->push_delay       = -1;      /* initialized when pushing starts */
3220     player->push_delay_value = game.initial_push_delay_value;
3221
3222     player->drop_delay = 0;
3223     player->drop_pressed_delay = 0;
3224
3225     player->last_jx = -1;
3226     player->last_jy = -1;
3227     player->jx = -1;
3228     player->jy = -1;
3229
3230     player->shield_normal_time_left = 0;
3231     player->shield_deadly_time_left = 0;
3232
3233     player->inventory_infinite_element = EL_UNDEFINED;
3234     player->inventory_size = 0;
3235
3236     if (level.use_initial_inventory[i])
3237     {
3238       for (j = 0; j < level.initial_inventory_size[i]; j++)
3239       {
3240         int element = level.initial_inventory_content[i][j];
3241         int collect_count = element_info[element].collect_count_initial;
3242         int k;
3243
3244         if (!IS_CUSTOM_ELEMENT(element))
3245           collect_count = 1;
3246
3247         if (collect_count == 0)
3248           player->inventory_infinite_element = element;
3249         else
3250           for (k = 0; k < collect_count; k++)
3251             if (player->inventory_size < MAX_INVENTORY_SIZE)
3252               player->inventory_element[player->inventory_size++] = element;
3253       }
3254     }
3255
3256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3257     SnapField(player, 0, 0);
3258
3259     player->LevelSolved = FALSE;
3260     player->GameOver = FALSE;
3261
3262     player->LevelSolved_GameWon = FALSE;
3263     player->LevelSolved_GameEnd = FALSE;
3264     player->LevelSolved_PanelOff = FALSE;
3265     player->LevelSolved_SaveTape = FALSE;
3266     player->LevelSolved_SaveScore = FALSE;
3267     player->LevelSolved_CountingTime = 0;
3268     player->LevelSolved_CountingScore = 0;
3269
3270     map_player_action[i] = i;
3271   }
3272
3273   network_player_action_received = FALSE;
3274
3275 #if defined(NETWORK_AVALIABLE)
3276   /* initial null action */
3277   if (network_playing)
3278     SendToServer_MovePlayer(MV_NONE);
3279 #endif
3280
3281   ZX = ZY = -1;
3282   ExitX = ExitY = -1;
3283
3284   FrameCounter = 0;
3285   TimeFrames = 0;
3286   TimePlayed = 0;
3287   TimeLeft = level.time;
3288   TapeTime = 0;
3289
3290   ScreenMovDir = MV_NONE;
3291   ScreenMovPos = 0;
3292   ScreenGfxPos = 0;
3293
3294   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3295
3296   AllPlayersGone = FALSE;
3297
3298   game.no_time_limit = (level.time == 0);
3299
3300   game.yamyam_content_nr = 0;
3301   game.robot_wheel_active = FALSE;
3302   game.magic_wall_active = FALSE;
3303   game.magic_wall_time_left = 0;
3304   game.light_time_left = 0;
3305   game.timegate_time_left = 0;
3306   game.switchgate_pos = 0;
3307   game.wind_direction = level.wind_direction_initial;
3308
3309   game.lenses_time_left = 0;
3310   game.magnify_time_left = 0;
3311
3312   game.ball_state = level.ball_state_initial;
3313   game.ball_content_nr = 0;
3314
3315   game.envelope_active = FALSE;
3316
3317   /* set focus to local player for network games, else to all players */
3318   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3319   game.centered_player_nr_next = game.centered_player_nr;
3320   game.set_centered_player = FALSE;
3321
3322   if (network_playing && tape.recording)
3323   {
3324     /* store client dependent player focus when recording network games */
3325     tape.centered_player_nr_next = game.centered_player_nr_next;
3326     tape.set_centered_player = TRUE;
3327   }
3328
3329   for (i = 0; i < NUM_BELTS; i++)
3330   {
3331     game.belt_dir[i] = MV_NONE;
3332     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3333   }
3334
3335   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3336     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3337
3338 #if DEBUG_INIT_PLAYER
3339   if (options.debug)
3340   {
3341     printf("Player status at level initialization:\n");
3342   }
3343 #endif
3344
3345   SCAN_PLAYFIELD(x, y)
3346   {
3347     Feld[x][y] = level.field[x][y];
3348     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3349     ChangeDelay[x][y] = 0;
3350     ChangePage[x][y] = -1;
3351     CustomValue[x][y] = 0;              /* initialized in InitField() */
3352     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3353     AmoebaNr[x][y] = 0;
3354     WasJustMoving[x][y] = 0;
3355     WasJustFalling[x][y] = 0;
3356     CheckCollision[x][y] = 0;
3357     CheckImpact[x][y] = 0;
3358     Stop[x][y] = FALSE;
3359     Pushed[x][y] = FALSE;
3360
3361     ChangeCount[x][y] = 0;
3362     ChangeEvent[x][y] = -1;
3363
3364     ExplodePhase[x][y] = 0;
3365     ExplodeDelay[x][y] = 0;
3366     ExplodeField[x][y] = EX_TYPE_NONE;
3367
3368     RunnerVisit[x][y] = 0;
3369     PlayerVisit[x][y] = 0;
3370
3371     GfxFrame[x][y] = 0;
3372     GfxRandom[x][y] = INIT_GFX_RANDOM();
3373     GfxElement[x][y] = EL_UNDEFINED;
3374     GfxAction[x][y] = ACTION_DEFAULT;
3375     GfxDir[x][y] = MV_NONE;
3376     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3377   }
3378
3379   SCAN_PLAYFIELD(x, y)
3380   {
3381     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3382       emulate_bd = FALSE;
3383     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3384       emulate_sb = FALSE;
3385     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3386       emulate_sp = FALSE;
3387
3388     InitField(x, y, TRUE);
3389
3390     ResetGfxAnimation(x, y);
3391   }
3392
3393   InitBeltMovement();
3394
3395   for (i = 0; i < MAX_PLAYERS; i++)
3396   {
3397     struct PlayerInfo *player = &stored_player[i];
3398
3399     /* set number of special actions for bored and sleeping animation */
3400     player->num_special_action_bored =
3401       get_num_special_action(player->artwork_element,
3402                              ACTION_BORING_1, ACTION_BORING_LAST);
3403     player->num_special_action_sleeping =
3404       get_num_special_action(player->artwork_element,
3405                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3406   }
3407
3408   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3409                     emulate_sb ? EMU_SOKOBAN :
3410                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3411
3412   /* initialize type of slippery elements */
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414   {
3415     if (!IS_CUSTOM_ELEMENT(i))
3416     {
3417       /* default: elements slip down either to the left or right randomly */
3418       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3419
3420       /* SP style elements prefer to slip down on the left side */
3421       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3422         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3423
3424       /* BD style elements prefer to slip down on the left side */
3425       if (game.emulation == EMU_BOULDERDASH)
3426         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3427     }
3428   }
3429
3430   /* initialize explosion and ignition delay */
3431   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432   {
3433     if (!IS_CUSTOM_ELEMENT(i))
3434     {
3435       int num_phase = 8;
3436       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3437                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3438                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3439       int last_phase = (num_phase + 1) * delay;
3440       int half_phase = (num_phase / 2) * delay;
3441
3442       element_info[i].explosion_delay = last_phase - 1;
3443       element_info[i].ignition_delay = half_phase;
3444
3445       if (i == EL_BLACK_ORB)
3446         element_info[i].ignition_delay = 1;
3447     }
3448   }
3449
3450   /* correct non-moving belts to start moving left */
3451   for (i = 0; i < NUM_BELTS; i++)
3452     if (game.belt_dir[i] == MV_NONE)
3453       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3454
3455 #if USE_NEW_PLAYER_ASSIGNMENTS
3456   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3457   /* choose default local player */
3458   local_player = &stored_player[0];
3459
3460   for (i = 0; i < MAX_PLAYERS; i++)
3461     stored_player[i].connected = FALSE;
3462
3463   local_player->connected = TRUE;
3464   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3465
3466   if (tape.playing)
3467   {
3468     for (i = 0; i < MAX_PLAYERS; i++)
3469       stored_player[i].connected = tape.player_participates[i];
3470   }
3471   else if (game.team_mode && !options.network)
3472   {
3473     /* try to guess locally connected team mode players (needed for correct
3474        assignment of player figures from level to locally playing players) */
3475
3476     for (i = 0; i < MAX_PLAYERS; i++)
3477       if (setup.input[i].use_joystick ||
3478           setup.input[i].key.left != KSYM_UNDEFINED)
3479         stored_player[i].connected = TRUE;
3480   }
3481
3482 #if DEBUG_INIT_PLAYER
3483   if (options.debug)
3484   {
3485     printf("Player status after level initialization:\n");
3486
3487     for (i = 0; i < MAX_PLAYERS; i++)
3488     {
3489       struct PlayerInfo *player = &stored_player[i];
3490
3491       printf("- player %d: present == %d, connected == %d, active == %d",
3492              i + 1,
3493              player->present,
3494              player->connected,
3495              player->active);
3496
3497       if (local_player == player)
3498         printf(" (local player)");
3499
3500       printf("\n");
3501     }
3502   }
3503 #endif
3504
3505 #if DEBUG_INIT_PLAYER
3506   if (options.debug)
3507     printf("Reassigning players ...\n");
3508 #endif
3509
3510   /* check if any connected player was not found in playfield */
3511   for (i = 0; i < MAX_PLAYERS; i++)
3512   {
3513     struct PlayerInfo *player = &stored_player[i];
3514
3515     if (player->connected && !player->present)
3516     {
3517       struct PlayerInfo *field_player = NULL;
3518
3519 #if DEBUG_INIT_PLAYER
3520       if (options.debug)
3521         printf("- looking for field player for player %d ...\n", i + 1);
3522 #endif
3523
3524       /* assign first free player found that is present in the playfield */
3525
3526       /* first try: look for unmapped playfield player that is not connected */
3527       for (j = 0; j < MAX_PLAYERS; j++)
3528         if (field_player == NULL &&
3529             stored_player[j].present &&
3530             !stored_player[j].mapped &&
3531             !stored_player[j].connected)
3532           field_player = &stored_player[j];
3533
3534       /* second try: look for *any* unmapped playfield player */
3535       for (j = 0; j < MAX_PLAYERS; j++)
3536         if (field_player == NULL &&
3537             stored_player[j].present &&
3538             !stored_player[j].mapped)
3539           field_player = &stored_player[j];
3540
3541       if (field_player != NULL)
3542       {
3543         int jx = field_player->jx, jy = field_player->jy;
3544
3545 #if DEBUG_INIT_PLAYER
3546         if (options.debug)
3547           printf("- found player %d\n", field_player->index_nr + 1);
3548 #endif
3549
3550         player->present = FALSE;
3551         player->active = FALSE;
3552
3553         field_player->present = TRUE;
3554         field_player->active = TRUE;
3555
3556         /*
3557         player->initial_element = field_player->initial_element;
3558         player->artwork_element = field_player->artwork_element;
3559
3560         player->block_last_field       = field_player->block_last_field;
3561         player->block_delay_adjustment = field_player->block_delay_adjustment;
3562         */
3563
3564         StorePlayer[jx][jy] = field_player->element_nr;
3565
3566         field_player->jx = field_player->last_jx = jx;
3567         field_player->jy = field_player->last_jy = jy;
3568
3569         if (local_player == player)
3570           local_player = field_player;
3571
3572         map_player_action[field_player->index_nr] = i;
3573
3574         field_player->mapped = TRUE;
3575
3576 #if DEBUG_INIT_PLAYER
3577         if (options.debug)
3578           printf("- map_player_action[%d] == %d\n",
3579                  field_player->index_nr + 1, i + 1);
3580 #endif
3581       }
3582     }
3583
3584     if (player->connected && player->present)
3585       player->mapped = TRUE;
3586   }
3587
3588 #if DEBUG_INIT_PLAYER
3589   if (options.debug)
3590   {
3591     printf("Player status after player assignment (first stage):\n");
3592
3593     for (i = 0; i < MAX_PLAYERS; i++)
3594     {
3595       struct PlayerInfo *player = &stored_player[i];
3596
3597       printf("- player %d: present == %d, connected == %d, active == %d",
3598              i + 1,
3599              player->present,
3600              player->connected,
3601              player->active);
3602
3603       if (local_player == player)
3604         printf(" (local player)");
3605
3606       printf("\n");
3607     }
3608   }
3609 #endif
3610
3611 #else
3612
3613   /* check if any connected player was not found in playfield */
3614   for (i = 0; i < MAX_PLAYERS; i++)
3615   {
3616     struct PlayerInfo *player = &stored_player[i];
3617
3618     if (player->connected && !player->present)
3619     {
3620       for (j = 0; j < MAX_PLAYERS; j++)
3621       {
3622         struct PlayerInfo *field_player = &stored_player[j];
3623         int jx = field_player->jx, jy = field_player->jy;
3624
3625         /* assign first free player found that is present in the playfield */
3626         if (field_player->present && !field_player->connected)
3627         {
3628           player->present = TRUE;
3629           player->active = TRUE;
3630
3631           field_player->present = FALSE;
3632           field_player->active = FALSE;
3633
3634           player->initial_element = field_player->initial_element;
3635           player->artwork_element = field_player->artwork_element;
3636
3637           player->block_last_field       = field_player->block_last_field;
3638           player->block_delay_adjustment = field_player->block_delay_adjustment;
3639
3640           StorePlayer[jx][jy] = player->element_nr;
3641
3642           player->jx = player->last_jx = jx;
3643           player->jy = player->last_jy = jy;
3644
3645           break;
3646         }
3647       }
3648     }
3649   }
3650 #endif
3651
3652 #if 0
3653   printf("::: local_player->present == %d\n", local_player->present);
3654 #endif
3655
3656   if (tape.playing)
3657   {
3658     /* when playing a tape, eliminate all players who do not participate */
3659
3660 #if USE_NEW_PLAYER_ASSIGNMENTS
3661
3662     if (!game.team_mode)
3663     {
3664       for (i = 0; i < MAX_PLAYERS; i++)
3665       {
3666         if (stored_player[i].active &&
3667             !tape.player_participates[map_player_action[i]])
3668         {
3669           struct PlayerInfo *player = &stored_player[i];
3670           int jx = player->jx, jy = player->jy;
3671
3672 #if DEBUG_INIT_PLAYER
3673           if (options.debug)
3674             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3675 #endif
3676
3677           player->active = FALSE;
3678           StorePlayer[jx][jy] = 0;
3679           Feld[jx][jy] = EL_EMPTY;
3680         }
3681       }
3682     }
3683
3684 #else
3685
3686     for (i = 0; i < MAX_PLAYERS; i++)
3687     {
3688       if (stored_player[i].active &&
3689           !tape.player_participates[i])
3690       {
3691         struct PlayerInfo *player = &stored_player[i];
3692         int jx = player->jx, jy = player->jy;
3693
3694         player->active = FALSE;
3695         StorePlayer[jx][jy] = 0;
3696         Feld[jx][jy] = EL_EMPTY;
3697       }
3698     }
3699 #endif
3700   }
3701   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3702   {
3703     /* when in single player mode, eliminate all but the first active player */
3704
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706     {
3707       if (stored_player[i].active)
3708       {
3709         for (j = i + 1; j < MAX_PLAYERS; j++)
3710         {
3711           if (stored_player[j].active)
3712           {
3713             struct PlayerInfo *player = &stored_player[j];
3714             int jx = player->jx, jy = player->jy;
3715
3716             player->active = FALSE;
3717             player->present = FALSE;
3718
3719             StorePlayer[jx][jy] = 0;
3720             Feld[jx][jy] = EL_EMPTY;
3721           }
3722         }
3723       }
3724     }
3725   }
3726
3727   /* when recording the game, store which players take part in the game */
3728   if (tape.recording)
3729   {
3730 #if USE_NEW_PLAYER_ASSIGNMENTS
3731     for (i = 0; i < MAX_PLAYERS; i++)
3732       if (stored_player[i].connected)
3733         tape.player_participates[i] = TRUE;
3734 #else
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736       if (stored_player[i].active)
3737         tape.player_participates[i] = TRUE;
3738 #endif
3739   }
3740
3741 #if DEBUG_INIT_PLAYER
3742   if (options.debug)
3743   {
3744     printf("Player status after player assignment (final stage):\n");
3745
3746     for (i = 0; i < MAX_PLAYERS; i++)
3747     {
3748       struct PlayerInfo *player = &stored_player[i];
3749
3750       printf("- player %d: present == %d, connected == %d, active == %d",
3751              i + 1,
3752              player->present,
3753              player->connected,
3754              player->active);
3755
3756       if (local_player == player)
3757         printf(" (local player)");
3758
3759       printf("\n");
3760     }
3761   }
3762 #endif
3763
3764   if (BorderElement == EL_EMPTY)
3765   {
3766     SBX_Left = 0;
3767     SBX_Right = lev_fieldx - SCR_FIELDX;
3768     SBY_Upper = 0;
3769     SBY_Lower = lev_fieldy - SCR_FIELDY;
3770   }
3771   else
3772   {
3773     SBX_Left = -1;
3774     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3775     SBY_Upper = -1;
3776     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3777   }
3778
3779   if (full_lev_fieldx <= SCR_FIELDX)
3780     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3781   if (full_lev_fieldy <= SCR_FIELDY)
3782     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3783
3784   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3785     SBX_Left--;
3786   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3787     SBY_Upper--;
3788
3789   /* if local player not found, look for custom element that might create
3790      the player (make some assumptions about the right custom element) */
3791   if (!local_player->present)
3792   {
3793     int start_x = 0, start_y = 0;
3794     int found_rating = 0;
3795     int found_element = EL_UNDEFINED;
3796     int player_nr = local_player->index_nr;
3797
3798     SCAN_PLAYFIELD(x, y)
3799     {
3800       int element = Feld[x][y];
3801       int content;
3802       int xx, yy;
3803       boolean is_player;
3804
3805       if (level.use_start_element[player_nr] &&
3806           level.start_element[player_nr] == element &&
3807           found_rating < 4)
3808       {
3809         start_x = x;
3810         start_y = y;
3811
3812         found_rating = 4;
3813         found_element = element;
3814       }
3815
3816       if (!IS_CUSTOM_ELEMENT(element))
3817         continue;
3818
3819       if (CAN_CHANGE(element))
3820       {
3821         for (i = 0; i < element_info[element].num_change_pages; i++)
3822         {
3823           /* check for player created from custom element as single target */
3824           content = element_info[element].change_page[i].target_element;
3825           is_player = ELEM_IS_PLAYER(content);
3826
3827           if (is_player && (found_rating < 3 ||
3828                             (found_rating == 3 && element < found_element)))
3829           {
3830             start_x = x;
3831             start_y = y;
3832
3833             found_rating = 3;
3834             found_element = element;
3835           }
3836         }
3837       }
3838
3839       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3840       {
3841         /* check for player created from custom element as explosion content */
3842         content = element_info[element].content.e[xx][yy];
3843         is_player = ELEM_IS_PLAYER(content);
3844
3845         if (is_player && (found_rating < 2 ||
3846                           (found_rating == 2 && element < found_element)))
3847         {
3848           start_x = x + xx - 1;
3849           start_y = y + yy - 1;
3850
3851           found_rating = 2;
3852           found_element = element;
3853         }
3854
3855         if (!CAN_CHANGE(element))
3856           continue;
3857
3858         for (i = 0; i < element_info[element].num_change_pages; i++)
3859         {
3860           /* check for player created from custom element as extended target */
3861           content =
3862             element_info[element].change_page[i].target_content.e[xx][yy];
3863
3864           is_player = ELEM_IS_PLAYER(content);
3865
3866           if (is_player && (found_rating < 1 ||
3867                             (found_rating == 1 && element < found_element)))
3868           {
3869             start_x = x + xx - 1;
3870             start_y = y + yy - 1;
3871
3872             found_rating = 1;
3873             found_element = element;
3874           }
3875         }
3876       }
3877     }
3878
3879     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3880                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3881                 start_x - MIDPOSX);
3882
3883     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3884                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3885                 start_y - MIDPOSY);
3886   }
3887   else
3888   {
3889     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3891                 local_player->jx - MIDPOSX);
3892
3893     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 local_player->jy - MIDPOSY);
3896   }
3897
3898   /* !!! FIX THIS (START) !!! */
3899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3900   {
3901     InitGameEngine_EM();
3902   }
3903   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3904   {
3905     InitGameEngine_SP();
3906   }
3907   else
3908   {
3909     DrawLevel(REDRAW_FIELD);
3910     DrawAllPlayers();
3911
3912     /* after drawing the level, correct some elements */
3913     if (game.timegate_time_left == 0)
3914       CloseAllOpenTimegates();
3915   }
3916
3917   /* blit playfield from scroll buffer to normal back buffer for fading in */
3918   BlitScreenToBitmap(backbuffer);
3919
3920   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3921   /* !!! FIX THIS (END) !!! */
3922
3923   FadeIn(REDRAW_FIELD);
3924
3925 #if 1
3926   // full screen redraw is required at this point in the following cases:
3927   // - special editor door undrawn when game was started from level editor
3928   // - drawing area (playfield) was changed and has to be removed completely
3929   redraw_mask = REDRAW_ALL;
3930   BackToFront();
3931 #endif
3932
3933   if (!game.restart_level)
3934   {
3935     /* copy default game door content to main double buffer */
3936
3937     /* !!! CHECK AGAIN !!! */
3938     SetPanelBackground();
3939     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3940     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3941   }
3942
3943   SetPanelBackground();
3944   SetDrawBackgroundMask(REDRAW_DOOR_1);
3945
3946   UpdateAndDisplayGameControlValues();
3947
3948   if (!game.restart_level)
3949   {
3950     UnmapGameButtons();
3951     UnmapTapeButtons();
3952     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3953     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3954     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3955     MapGameButtons();
3956     MapTapeButtons();
3957
3958     /* copy actual game door content to door double buffer for OpenDoor() */
3959     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3960
3961     OpenDoor(DOOR_OPEN_ALL);
3962
3963     PlaySound(SND_GAME_STARTING);
3964
3965     if (setup.sound_music)
3966       PlayLevelMusic();
3967
3968     KeyboardAutoRepeatOffUnlessAutoplay();
3969
3970 #if DEBUG_INIT_PLAYER
3971     if (options.debug)
3972     {
3973       printf("Player status (final):\n");
3974
3975       for (i = 0; i < MAX_PLAYERS; i++)
3976       {
3977         struct PlayerInfo *player = &stored_player[i];
3978
3979         printf("- player %d: present == %d, connected == %d, active == %d",
3980                i + 1,
3981                player->present,
3982                player->connected,
3983                player->active);
3984
3985         if (local_player == player)
3986           printf(" (local player)");
3987
3988         printf("\n");
3989       }
3990     }
3991 #endif
3992   }
3993
3994   UnmapAllGadgets();
3995
3996   MapGameButtons();
3997   MapTapeButtons();
3998
3999   if (!game.restart_level && !tape.playing)
4000   {
4001     LevelStats_incPlayed(level_nr);
4002
4003     SaveLevelSetup_SeriesInfo();
4004   }
4005
4006   game.restart_level = FALSE;
4007
4008   SaveEngineSnapshotToListInitial();
4009 }
4010
4011 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4012 {
4013   /* this is used for non-R'n'D game engines to update certain engine values */
4014
4015   /* needed to determine if sounds are played within the visible screen area */
4016   scroll_x = actual_scroll_x;
4017   scroll_y = actual_scroll_y;
4018 }
4019
4020 void InitMovDir(int x, int y)
4021 {
4022   int i, element = Feld[x][y];
4023   static int xy[4][2] =
4024   {
4025     {  0, +1 },
4026     { +1,  0 },
4027     {  0, -1 },
4028     { -1,  0 }
4029   };
4030   static int direction[3][4] =
4031   {
4032     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4033     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4034     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4035   };
4036
4037   switch (element)
4038   {
4039     case EL_BUG_RIGHT:
4040     case EL_BUG_UP:
4041     case EL_BUG_LEFT:
4042     case EL_BUG_DOWN:
4043       Feld[x][y] = EL_BUG;
4044       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4045       break;
4046
4047     case EL_SPACESHIP_RIGHT:
4048     case EL_SPACESHIP_UP:
4049     case EL_SPACESHIP_LEFT:
4050     case EL_SPACESHIP_DOWN:
4051       Feld[x][y] = EL_SPACESHIP;
4052       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4053       break;
4054
4055     case EL_BD_BUTTERFLY_RIGHT:
4056     case EL_BD_BUTTERFLY_UP:
4057     case EL_BD_BUTTERFLY_LEFT:
4058     case EL_BD_BUTTERFLY_DOWN:
4059       Feld[x][y] = EL_BD_BUTTERFLY;
4060       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4061       break;
4062
4063     case EL_BD_FIREFLY_RIGHT:
4064     case EL_BD_FIREFLY_UP:
4065     case EL_BD_FIREFLY_LEFT:
4066     case EL_BD_FIREFLY_DOWN:
4067       Feld[x][y] = EL_BD_FIREFLY;
4068       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4069       break;
4070
4071     case EL_PACMAN_RIGHT:
4072     case EL_PACMAN_UP:
4073     case EL_PACMAN_LEFT:
4074     case EL_PACMAN_DOWN:
4075       Feld[x][y] = EL_PACMAN;
4076       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4077       break;
4078
4079     case EL_YAMYAM_LEFT:
4080     case EL_YAMYAM_RIGHT:
4081     case EL_YAMYAM_UP:
4082     case EL_YAMYAM_DOWN:
4083       Feld[x][y] = EL_YAMYAM;
4084       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4085       break;
4086
4087     case EL_SP_SNIKSNAK:
4088       MovDir[x][y] = MV_UP;
4089       break;
4090
4091     case EL_SP_ELECTRON:
4092       MovDir[x][y] = MV_LEFT;
4093       break;
4094
4095     case EL_MOLE_LEFT:
4096     case EL_MOLE_RIGHT:
4097     case EL_MOLE_UP:
4098     case EL_MOLE_DOWN:
4099       Feld[x][y] = EL_MOLE;
4100       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4101       break;
4102
4103     default:
4104       if (IS_CUSTOM_ELEMENT(element))
4105       {
4106         struct ElementInfo *ei = &element_info[element];
4107         int move_direction_initial = ei->move_direction_initial;
4108         int move_pattern = ei->move_pattern;
4109
4110         if (move_direction_initial == MV_START_PREVIOUS)
4111         {
4112           if (MovDir[x][y] != MV_NONE)
4113             return;
4114
4115           move_direction_initial = MV_START_AUTOMATIC;
4116         }
4117
4118         if (move_direction_initial == MV_START_RANDOM)
4119           MovDir[x][y] = 1 << RND(4);
4120         else if (move_direction_initial & MV_ANY_DIRECTION)
4121           MovDir[x][y] = move_direction_initial;
4122         else if (move_pattern == MV_ALL_DIRECTIONS ||
4123                  move_pattern == MV_TURNING_LEFT ||
4124                  move_pattern == MV_TURNING_RIGHT ||
4125                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4126                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4127                  move_pattern == MV_TURNING_RANDOM)
4128           MovDir[x][y] = 1 << RND(4);
4129         else if (move_pattern == MV_HORIZONTAL)
4130           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4131         else if (move_pattern == MV_VERTICAL)
4132           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4133         else if (move_pattern & MV_ANY_DIRECTION)
4134           MovDir[x][y] = element_info[element].move_pattern;
4135         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4136                  move_pattern == MV_ALONG_RIGHT_SIDE)
4137         {
4138           /* use random direction as default start direction */
4139           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4140             MovDir[x][y] = 1 << RND(4);
4141
4142           for (i = 0; i < NUM_DIRECTIONS; i++)
4143           {
4144             int x1 = x + xy[i][0];
4145             int y1 = y + xy[i][1];
4146
4147             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4148             {
4149               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4150                 MovDir[x][y] = direction[0][i];
4151               else
4152                 MovDir[x][y] = direction[1][i];
4153
4154               break;
4155             }
4156           }
4157         }                
4158       }
4159       else
4160       {
4161         MovDir[x][y] = 1 << RND(4);
4162
4163         if (element != EL_BUG &&
4164             element != EL_SPACESHIP &&
4165             element != EL_BD_BUTTERFLY &&
4166             element != EL_BD_FIREFLY)
4167           break;
4168
4169         for (i = 0; i < NUM_DIRECTIONS; i++)
4170         {
4171           int x1 = x + xy[i][0];
4172           int y1 = y + xy[i][1];
4173
4174           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4175           {
4176             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4177             {
4178               MovDir[x][y] = direction[0][i];
4179               break;
4180             }
4181             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4182                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4183             {
4184               MovDir[x][y] = direction[1][i];
4185               break;
4186             }
4187           }
4188         }
4189       }
4190       break;
4191   }
4192
4193   GfxDir[x][y] = MovDir[x][y];
4194 }
4195
4196 void InitAmoebaNr(int x, int y)
4197 {
4198   int i;
4199   int group_nr = AmoebeNachbarNr(x, y);
4200
4201   if (group_nr == 0)
4202   {
4203     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4204     {
4205       if (AmoebaCnt[i] == 0)
4206       {
4207         group_nr = i;
4208         break;
4209       }
4210     }
4211   }
4212
4213   AmoebaNr[x][y] = group_nr;
4214   AmoebaCnt[group_nr]++;
4215   AmoebaCnt2[group_nr]++;
4216 }
4217
4218 static void PlayerWins(struct PlayerInfo *player)
4219 {
4220   player->LevelSolved = TRUE;
4221   player->GameOver = TRUE;
4222
4223   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4224                          level.native_em_level->lev->score : player->score);
4225
4226   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4227                                       TimeLeft);
4228   player->LevelSolved_CountingScore = player->score_final;
4229 }
4230
4231 void GameWon()
4232 {
4233   static int time, time_final;
4234   static int score, score_final;
4235   static int game_over_delay_1 = 0;
4236   static int game_over_delay_2 = 0;
4237   int game_over_delay_value_1 = 50;
4238   int game_over_delay_value_2 = 50;
4239
4240   if (!local_player->LevelSolved_GameWon)
4241   {
4242     int i;
4243
4244     /* do not start end game actions before the player stops moving (to exit) */
4245     if (local_player->MovPos)
4246       return;
4247
4248     local_player->LevelSolved_GameWon = TRUE;
4249     local_player->LevelSolved_SaveTape = tape.recording;
4250     local_player->LevelSolved_SaveScore = !tape.playing;
4251
4252     if (!tape.playing)
4253     {
4254       LevelStats_incSolved(level_nr);
4255
4256       SaveLevelSetup_SeriesInfo();
4257     }
4258
4259     if (tape.auto_play)         /* tape might already be stopped here */
4260       tape.auto_play_level_solved = TRUE;
4261
4262     TapeStop();
4263
4264     game_over_delay_1 = game_over_delay_value_1;
4265     game_over_delay_2 = game_over_delay_value_2;
4266
4267     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4268     score = score_final = local_player->score_final;
4269
4270     if (TimeLeft > 0)
4271     {
4272       time_final = 0;
4273       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4274     }
4275     else if (game.no_time_limit && TimePlayed < 999)
4276     {
4277       time_final = 999;
4278       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4279     }
4280
4281     local_player->score_final = score_final;
4282
4283     if (level_editor_test_game)
4284     {
4285       time = time_final;
4286       score = score_final;
4287
4288       local_player->LevelSolved_CountingTime = time;
4289       local_player->LevelSolved_CountingScore = score;
4290
4291       game_panel_controls[GAME_PANEL_TIME].value = time;
4292       game_panel_controls[GAME_PANEL_SCORE].value = score;
4293
4294       DisplayGameControlValues();
4295     }
4296
4297     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4298     {
4299       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4300       {
4301         /* close exit door after last player */
4302         if ((AllPlayersGone &&
4303              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4304               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4305               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4306             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4307             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4308         {
4309           int element = Feld[ExitX][ExitY];
4310
4311           Feld[ExitX][ExitY] =
4312             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4313              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4314              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4315              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4316              EL_EM_STEEL_EXIT_CLOSING);
4317
4318           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4319         }
4320
4321         /* player disappears */
4322         DrawLevelField(ExitX, ExitY);
4323       }
4324
4325       for (i = 0; i < MAX_PLAYERS; i++)
4326       {
4327         struct PlayerInfo *player = &stored_player[i];
4328
4329         if (player->present)
4330         {
4331           RemovePlayer(player);
4332
4333           /* player disappears */
4334           DrawLevelField(player->jx, player->jy);
4335         }
4336       }
4337     }
4338
4339     PlaySound(SND_GAME_WINNING);
4340   }
4341
4342   if (game_over_delay_1 > 0)
4343   {
4344     game_over_delay_1--;
4345
4346     return;
4347   }
4348
4349   if (time != time_final)
4350   {
4351     int time_to_go = ABS(time_final - time);
4352     int time_count_dir = (time < time_final ? +1 : -1);
4353     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4354
4355     time  += time_count_steps * time_count_dir;
4356     score += time_count_steps * level.score[SC_TIME_BONUS];
4357
4358     local_player->LevelSolved_CountingTime = time;
4359     local_player->LevelSolved_CountingScore = score;
4360
4361     game_panel_controls[GAME_PANEL_TIME].value = time;
4362     game_panel_controls[GAME_PANEL_SCORE].value = score;
4363
4364     DisplayGameControlValues();
4365
4366     if (time == time_final)
4367       StopSound(SND_GAME_LEVELTIME_BONUS);
4368     else if (setup.sound_loops)
4369       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4370     else
4371       PlaySound(SND_GAME_LEVELTIME_BONUS);
4372
4373     return;
4374   }
4375
4376   local_player->LevelSolved_PanelOff = TRUE;
4377
4378   if (game_over_delay_2 > 0)
4379   {
4380     game_over_delay_2--;
4381
4382     return;
4383   }
4384
4385   GameEnd();
4386 }
4387
4388 void GameEnd()
4389 {
4390   int hi_pos;
4391   boolean raise_level = FALSE;
4392
4393   local_player->LevelSolved_GameEnd = TRUE;
4394
4395   if (!global.use_envelope_request)
4396     CloseDoor(DOOR_CLOSE_1);
4397
4398   if (local_player->LevelSolved_SaveTape)
4399   {
4400     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4401   }
4402
4403   CloseDoor(DOOR_CLOSE_ALL);
4404
4405   if (level_editor_test_game)
4406   {
4407     game_status = GAME_MODE_MAIN;
4408
4409     DrawAndFadeInMainMenu(REDRAW_FIELD);
4410
4411     return;
4412   }
4413
4414   if (!local_player->LevelSolved_SaveScore)
4415   {
4416     FadeOut(REDRAW_FIELD);
4417
4418     game_status = GAME_MODE_MAIN;
4419
4420     DrawAndFadeInMainMenu(REDRAW_FIELD);
4421
4422     return;
4423   }
4424
4425   if (level_nr == leveldir_current->handicap_level)
4426   {
4427     leveldir_current->handicap_level++;
4428
4429     SaveLevelSetup_SeriesInfo();
4430   }
4431
4432   if (level_nr < leveldir_current->last_level)
4433     raise_level = TRUE;                 /* advance to next level */
4434
4435   if ((hi_pos = NewHiScore()) >= 0) 
4436   {
4437     game_status = GAME_MODE_SCORES;
4438
4439     /* needed if different viewport properties defined for scores */
4440     ChangeViewportPropertiesIfNeeded();
4441
4442     DrawHallOfFame(hi_pos);
4443
4444     if (raise_level)
4445     {
4446       level_nr++;
4447       TapeErase();
4448     }
4449   }
4450   else
4451   {
4452     FadeOut(REDRAW_FIELD);
4453
4454     game_status = GAME_MODE_MAIN;
4455
4456     if (raise_level)
4457     {
4458       level_nr++;
4459       TapeErase();
4460     }
4461
4462     DrawAndFadeInMainMenu(REDRAW_FIELD);
4463   }
4464 }
4465
4466 int NewHiScore()
4467 {
4468   int k, l;
4469   int position = -1;
4470
4471   LoadScore(level_nr);
4472
4473   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4474       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4475     return -1;
4476
4477   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4478   {
4479     if (local_player->score_final > highscore[k].Score)
4480     {
4481       /* player has made it to the hall of fame */
4482
4483       if (k < MAX_SCORE_ENTRIES - 1)
4484       {
4485         int m = MAX_SCORE_ENTRIES - 1;
4486
4487 #ifdef ONE_PER_NAME
4488         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4489           if (strEqual(setup.player_name, highscore[l].Name))
4490             m = l;
4491         if (m == k)     /* player's new highscore overwrites his old one */
4492           goto put_into_list;
4493 #endif
4494
4495         for (l = m; l > k; l--)
4496         {
4497           strcpy(highscore[l].Name, highscore[l - 1].Name);
4498           highscore[l].Score = highscore[l - 1].Score;
4499         }
4500       }
4501
4502 #ifdef ONE_PER_NAME
4503       put_into_list:
4504 #endif
4505       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4506       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4507       highscore[k].Score = local_player->score_final; 
4508       position = k;
4509       break;
4510     }
4511
4512 #ifdef ONE_PER_NAME
4513     else if (!strncmp(setup.player_name, highscore[k].Name,
4514                       MAX_PLAYER_NAME_LEN))
4515       break;    /* player already there with a higher score */
4516 #endif
4517
4518   }
4519
4520   if (position >= 0) 
4521     SaveScore(level_nr);
4522
4523   return position;
4524 }
4525
4526 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4527 {
4528   int element = Feld[x][y];
4529   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4530   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4531   int horiz_move = (dx != 0);
4532   int sign = (horiz_move ? dx : dy);
4533   int step = sign * element_info[element].move_stepsize;
4534
4535   /* special values for move stepsize for spring and things on conveyor belt */
4536   if (horiz_move)
4537   {
4538     if (CAN_FALL(element) &&
4539         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4540       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4541     else if (element == EL_SPRING)
4542       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4543   }
4544
4545   return step;
4546 }
4547
4548 inline static int getElementMoveStepsize(int x, int y)
4549 {
4550   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4551 }
4552
4553 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4554 {
4555   if (player->GfxAction != action || player->GfxDir != dir)
4556   {
4557     player->GfxAction = action;
4558     player->GfxDir = dir;
4559     player->Frame = 0;
4560     player->StepFrame = 0;
4561   }
4562 }
4563
4564 static void ResetGfxFrame(int x, int y, boolean redraw)
4565 {
4566   int element = Feld[x][y];
4567   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4568   int last_gfx_frame = GfxFrame[x][y];
4569
4570   if (graphic_info[graphic].anim_global_sync)
4571     GfxFrame[x][y] = FrameCounter;
4572   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4573     GfxFrame[x][y] = CustomValue[x][y];
4574   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4575     GfxFrame[x][y] = element_info[element].collect_score;
4576   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4577     GfxFrame[x][y] = ChangeDelay[x][y];
4578
4579   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4580     DrawLevelGraphicAnimation(x, y, graphic);
4581 }
4582
4583 static void ResetGfxAnimation(int x, int y)
4584 {
4585   GfxAction[x][y] = ACTION_DEFAULT;
4586   GfxDir[x][y] = MovDir[x][y];
4587   GfxFrame[x][y] = 0;
4588
4589   ResetGfxFrame(x, y, FALSE);
4590 }
4591
4592 static void ResetRandomAnimationValue(int x, int y)
4593 {
4594   GfxRandom[x][y] = INIT_GFX_RANDOM();
4595 }
4596
4597 void InitMovingField(int x, int y, int direction)
4598 {
4599   int element = Feld[x][y];
4600   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4601   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4602   int newx = x + dx;
4603   int newy = y + dy;
4604   boolean is_moving_before, is_moving_after;
4605
4606   /* check if element was/is moving or being moved before/after mode change */
4607   is_moving_before = (WasJustMoving[x][y] != 0);
4608   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4609
4610   /* reset animation only for moving elements which change direction of moving
4611      or which just started or stopped moving
4612      (else CEs with property "can move" / "not moving" are reset each frame) */
4613   if (is_moving_before != is_moving_after ||
4614       direction != MovDir[x][y])
4615     ResetGfxAnimation(x, y);
4616
4617   MovDir[x][y] = direction;
4618   GfxDir[x][y] = direction;
4619
4620   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4621                      direction == MV_DOWN && CAN_FALL(element) ?
4622                      ACTION_FALLING : ACTION_MOVING);
4623
4624   /* this is needed for CEs with property "can move" / "not moving" */
4625
4626   if (is_moving_after)
4627   {
4628     if (Feld[newx][newy] == EL_EMPTY)
4629       Feld[newx][newy] = EL_BLOCKED;
4630
4631     MovDir[newx][newy] = MovDir[x][y];
4632
4633     CustomValue[newx][newy] = CustomValue[x][y];
4634
4635     GfxFrame[newx][newy] = GfxFrame[x][y];
4636     GfxRandom[newx][newy] = GfxRandom[x][y];
4637     GfxAction[newx][newy] = GfxAction[x][y];
4638     GfxDir[newx][newy] = GfxDir[x][y];
4639   }
4640 }
4641
4642 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4643 {
4644   int direction = MovDir[x][y];
4645   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4646   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4647
4648   *goes_to_x = newx;
4649   *goes_to_y = newy;
4650 }
4651
4652 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4653 {
4654   int oldx = x, oldy = y;
4655   int direction = MovDir[x][y];
4656
4657   if (direction == MV_LEFT)
4658     oldx++;
4659   else if (direction == MV_RIGHT)
4660     oldx--;
4661   else if (direction == MV_UP)
4662     oldy++;
4663   else if (direction == MV_DOWN)
4664     oldy--;
4665
4666   *comes_from_x = oldx;
4667   *comes_from_y = oldy;
4668 }
4669
4670 int MovingOrBlocked2Element(int x, int y)
4671 {
4672   int element = Feld[x][y];
4673
4674   if (element == EL_BLOCKED)
4675   {
4676     int oldx, oldy;
4677
4678     Blocked2Moving(x, y, &oldx, &oldy);
4679     return Feld[oldx][oldy];
4680   }
4681   else
4682     return element;
4683 }
4684
4685 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4686 {
4687   /* like MovingOrBlocked2Element(), but if element is moving
4688      and (x,y) is the field the moving element is just leaving,
4689      return EL_BLOCKED instead of the element value */
4690   int element = Feld[x][y];
4691
4692   if (IS_MOVING(x, y))
4693   {
4694     if (element == EL_BLOCKED)
4695     {
4696       int oldx, oldy;
4697
4698       Blocked2Moving(x, y, &oldx, &oldy);
4699       return Feld[oldx][oldy];
4700     }
4701     else
4702       return EL_BLOCKED;
4703   }
4704   else
4705     return element;
4706 }
4707
4708 static void RemoveField(int x, int y)
4709 {
4710   Feld[x][y] = EL_EMPTY;
4711
4712   MovPos[x][y] = 0;
4713   MovDir[x][y] = 0;
4714   MovDelay[x][y] = 0;
4715
4716   CustomValue[x][y] = 0;
4717
4718   AmoebaNr[x][y] = 0;
4719   ChangeDelay[x][y] = 0;
4720   ChangePage[x][y] = -1;
4721   Pushed[x][y] = FALSE;
4722
4723   GfxElement[x][y] = EL_UNDEFINED;
4724   GfxAction[x][y] = ACTION_DEFAULT;
4725   GfxDir[x][y] = MV_NONE;
4726 }
4727
4728 void RemoveMovingField(int x, int y)
4729 {
4730   int oldx = x, oldy = y, newx = x, newy = y;
4731   int element = Feld[x][y];
4732   int next_element = EL_UNDEFINED;
4733
4734   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4735     return;
4736
4737   if (IS_MOVING(x, y))
4738   {
4739     Moving2Blocked(x, y, &newx, &newy);
4740
4741     if (Feld[newx][newy] != EL_BLOCKED)
4742     {
4743       /* element is moving, but target field is not free (blocked), but
4744          already occupied by something different (example: acid pool);
4745          in this case, only remove the moving field, but not the target */
4746
4747       RemoveField(oldx, oldy);
4748
4749       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4750
4751       TEST_DrawLevelField(oldx, oldy);
4752
4753       return;
4754     }
4755   }
4756   else if (element == EL_BLOCKED)
4757   {
4758     Blocked2Moving(x, y, &oldx, &oldy);
4759     if (!IS_MOVING(oldx, oldy))
4760       return;
4761   }
4762
4763   if (element == EL_BLOCKED &&
4764       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4765        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4766        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4767        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4768        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4769        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4770     next_element = get_next_element(Feld[oldx][oldy]);
4771
4772   RemoveField(oldx, oldy);
4773   RemoveField(newx, newy);
4774
4775   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4776
4777   if (next_element != EL_UNDEFINED)
4778     Feld[oldx][oldy] = next_element;
4779
4780   TEST_DrawLevelField(oldx, oldy);
4781   TEST_DrawLevelField(newx, newy);
4782 }
4783
4784 void DrawDynamite(int x, int y)
4785 {
4786   int sx = SCREENX(x), sy = SCREENY(y);
4787   int graphic = el2img(Feld[x][y]);
4788   int frame;
4789
4790   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4791     return;
4792
4793   if (IS_WALKABLE_INSIDE(Back[x][y]))
4794     return;
4795
4796   if (Back[x][y])
4797     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4798   else if (Store[x][y])
4799     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4800
4801   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4802
4803   if (Back[x][y] || Store[x][y])
4804     DrawGraphicThruMask(sx, sy, graphic, frame);
4805   else
4806     DrawGraphic(sx, sy, graphic, frame);
4807 }
4808
4809 void CheckDynamite(int x, int y)
4810 {
4811   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4812   {
4813     MovDelay[x][y]--;
4814
4815     if (MovDelay[x][y] != 0)
4816     {
4817       DrawDynamite(x, y);
4818       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4819
4820       return;
4821     }
4822   }
4823
4824   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4825
4826   Bang(x, y);
4827 }
4828
4829 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4830 {
4831   boolean num_checked_players = 0;
4832   int i;
4833
4834   for (i = 0; i < MAX_PLAYERS; i++)
4835   {
4836     if (stored_player[i].active)
4837     {
4838       int sx = stored_player[i].jx;
4839       int sy = stored_player[i].jy;
4840
4841       if (num_checked_players == 0)
4842       {
4843         *sx1 = *sx2 = sx;
4844         *sy1 = *sy2 = sy;
4845       }
4846       else
4847       {
4848         *sx1 = MIN(*sx1, sx);
4849         *sy1 = MIN(*sy1, sy);
4850         *sx2 = MAX(*sx2, sx);
4851         *sy2 = MAX(*sy2, sy);
4852       }
4853
4854       num_checked_players++;
4855     }
4856   }
4857 }
4858
4859 static boolean checkIfAllPlayersFitToScreen_RND()
4860 {
4861   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4862
4863   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4864
4865   return (sx2 - sx1 < SCR_FIELDX &&
4866           sy2 - sy1 < SCR_FIELDY);
4867 }
4868
4869 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4870 {
4871   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4872
4873   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4874
4875   *sx = (sx1 + sx2) / 2;
4876   *sy = (sy1 + sy2) / 2;
4877 }
4878
4879 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4880                         boolean center_screen, boolean quick_relocation)
4881 {
4882   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4883   boolean no_delay = (tape.warp_forward);
4884   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4885   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4886
4887   if (quick_relocation)
4888   {
4889     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4890     {
4891       if (!level.shifted_relocation || center_screen)
4892       {
4893         /* quick relocation (without scrolling), with centering of screen */
4894
4895         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4896                     x > SBX_Right + MIDPOSX ? SBX_Right :
4897                     x - MIDPOSX);
4898
4899         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4900                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4901                     y - MIDPOSY);
4902       }
4903       else
4904       {
4905         /* quick relocation (without scrolling), but do not center screen */
4906
4907         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4908                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4909                                old_x - MIDPOSX);
4910
4911         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4912                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4913                                old_y - MIDPOSY);
4914
4915         int offset_x = x + (scroll_x - center_scroll_x);
4916         int offset_y = y + (scroll_y - center_scroll_y);
4917
4918         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4919                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4920                     offset_x - MIDPOSX);
4921
4922         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4923                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4924                     offset_y - MIDPOSY);
4925       }
4926     }
4927     else
4928     {
4929       if (!level.shifted_relocation || center_screen)
4930       {
4931         /* quick relocation (without scrolling), with centering of screen */
4932
4933         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4934                     x > SBX_Right + MIDPOSX ? SBX_Right :
4935                     x - MIDPOSX);
4936
4937         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4938                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4939                     y - MIDPOSY);
4940       }
4941       else
4942       {
4943         /* quick relocation (without scrolling), but do not center screen */
4944
4945         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4946                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4947                                old_x - MIDPOSX);
4948
4949         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4950                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4951                                old_y - MIDPOSY);
4952
4953         int offset_x = x + (scroll_x - center_scroll_x);
4954         int offset_y = y + (scroll_y - center_scroll_y);
4955
4956         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4957                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4958                     offset_x - MIDPOSX);
4959
4960         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4961                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4962                     offset_y - MIDPOSY);
4963       }
4964     }
4965
4966     RedrawPlayfield(TRUE, 0,0,0,0);
4967   }
4968   else
4969   {
4970     int scroll_xx, scroll_yy;
4971
4972     if (!level.shifted_relocation || center_screen)
4973     {
4974       /* visible relocation (with scrolling), with centering of screen */
4975
4976       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4977                    x > SBX_Right + MIDPOSX ? SBX_Right :
4978                    x - MIDPOSX);
4979
4980       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4981                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4982                    y - MIDPOSY);
4983     }
4984     else
4985     {
4986       /* visible relocation (with scrolling), but do not center screen */
4987
4988       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4989                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4990                              old_x - MIDPOSX);
4991
4992       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4993                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4994                              old_y - MIDPOSY);
4995
4996       int offset_x = x + (scroll_x - center_scroll_x);
4997       int offset_y = y + (scroll_y - center_scroll_y);
4998
4999       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5000                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5001                    offset_x - MIDPOSX);
5002
5003       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5004                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5005                    offset_y - MIDPOSY);
5006     }
5007
5008
5009     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5010
5011     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5012     {
5013       int dx = 0, dy = 0;
5014       int fx = FX, fy = FY;
5015
5016       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5017       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5018
5019       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5020         break;
5021
5022       scroll_x -= dx;
5023       scroll_y -= dy;
5024
5025       fx += dx * TILEX / 2;
5026       fy += dy * TILEY / 2;
5027
5028       ScrollLevel(dx, dy);
5029       DrawAllPlayers();
5030
5031       /* scroll in two steps of half tile size to make things smoother */
5032       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5033       Delay(wait_delay_value);
5034
5035       /* scroll second step to align at full tile size */
5036       BackToFront();
5037       Delay(wait_delay_value);
5038     }
5039
5040     DrawAllPlayers();
5041     BackToFront();
5042     Delay(wait_delay_value);
5043   }
5044 }
5045
5046 void RelocatePlayer(int jx, int jy, int el_player_raw)
5047 {
5048   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5049   int player_nr = GET_PLAYER_NR(el_player);
5050   struct PlayerInfo *player = &stored_player[player_nr];
5051   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5052   boolean no_delay = (tape.warp_forward);
5053   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5054   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5055   int old_jx = player->jx;
5056   int old_jy = player->jy;
5057   int old_element = Feld[old_jx][old_jy];
5058   int element = Feld[jx][jy];
5059   boolean player_relocated = (old_jx != jx || old_jy != jy);
5060
5061   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5062   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5063   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5064   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5065   int leave_side_horiz = move_dir_horiz;
5066   int leave_side_vert  = move_dir_vert;
5067   int enter_side = enter_side_horiz | enter_side_vert;
5068   int leave_side = leave_side_horiz | leave_side_vert;
5069
5070   if (player->GameOver)         /* do not reanimate dead player */
5071     return;
5072
5073   if (!player_relocated)        /* no need to relocate the player */
5074     return;
5075
5076   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5077   {
5078     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5079     DrawLevelField(jx, jy);
5080   }
5081
5082   if (player->present)
5083   {
5084     while (player->MovPos)
5085     {
5086       ScrollPlayer(player, SCROLL_GO_ON);
5087       ScrollScreen(NULL, SCROLL_GO_ON);
5088
5089       AdvanceFrameAndPlayerCounters(player->index_nr);
5090
5091       DrawPlayer(player);
5092
5093       BackToFront();
5094       Delay(wait_delay_value);
5095     }
5096
5097     DrawPlayer(player);         /* needed here only to cleanup last field */
5098     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5099
5100     player->is_moving = FALSE;
5101   }
5102
5103   if (IS_CUSTOM_ELEMENT(old_element))
5104     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5105                                CE_LEFT_BY_PLAYER,
5106                                player->index_bit, leave_side);
5107
5108   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5109                                       CE_PLAYER_LEAVES_X,
5110                                       player->index_bit, leave_side);
5111
5112   Feld[jx][jy] = el_player;
5113   InitPlayerField(jx, jy, el_player, TRUE);
5114
5115   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5116      possible that the relocation target field did not contain a player element,
5117      but a walkable element, to which the new player was relocated -- in this
5118      case, restore that (already initialized!) element on the player field */
5119   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5120   {
5121     Feld[jx][jy] = element;     /* restore previously existing element */
5122   }
5123
5124   /* only visually relocate centered player */
5125   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5126                      FALSE, level.instant_relocation);
5127
5128   TestIfPlayerTouchesBadThing(jx, jy);
5129   TestIfPlayerTouchesCustomElement(jx, jy);
5130
5131   if (IS_CUSTOM_ELEMENT(element))
5132     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5133                                player->index_bit, enter_side);
5134
5135   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5136                                       player->index_bit, enter_side);
5137
5138   if (player->is_switching)
5139   {
5140     /* ensure that relocation while still switching an element does not cause
5141        a new element to be treated as also switched directly after relocation
5142        (this is important for teleporter switches that teleport the player to
5143        a place where another teleporter switch is in the same direction, which
5144        would then incorrectly be treated as immediately switched before the
5145        direction key that caused the switch was released) */
5146
5147     player->switch_x += jx - old_jx;
5148     player->switch_y += jy - old_jy;
5149   }
5150 }
5151
5152 void Explode(int ex, int ey, int phase, int mode)
5153 {
5154   int x, y;
5155   int last_phase;
5156   int border_element;
5157
5158   /* !!! eliminate this variable !!! */
5159   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5160
5161   if (game.explosions_delayed)
5162   {
5163     ExplodeField[ex][ey] = mode;
5164     return;
5165   }
5166
5167   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5168   {
5169     int center_element = Feld[ex][ey];
5170     int artwork_element, explosion_element;     /* set these values later */
5171
5172     /* remove things displayed in background while burning dynamite */
5173     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5174       Back[ex][ey] = 0;
5175
5176     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5177     {
5178       /* put moving element to center field (and let it explode there) */
5179       center_element = MovingOrBlocked2Element(ex, ey);
5180       RemoveMovingField(ex, ey);
5181       Feld[ex][ey] = center_element;
5182     }
5183
5184     /* now "center_element" is finally determined -- set related values now */
5185     artwork_element = center_element;           /* for custom player artwork */
5186     explosion_element = center_element;         /* for custom player artwork */
5187
5188     if (IS_PLAYER(ex, ey))
5189     {
5190       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5191
5192       artwork_element = stored_player[player_nr].artwork_element;
5193
5194       if (level.use_explosion_element[player_nr])
5195       {
5196         explosion_element = level.explosion_element[player_nr];
5197         artwork_element = explosion_element;
5198       }
5199     }
5200
5201     if (mode == EX_TYPE_NORMAL ||
5202         mode == EX_TYPE_CENTER ||
5203         mode == EX_TYPE_CROSS)
5204       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5205
5206     last_phase = element_info[explosion_element].explosion_delay + 1;
5207
5208     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5209     {
5210       int xx = x - ex + 1;
5211       int yy = y - ey + 1;
5212       int element;
5213
5214       if (!IN_LEV_FIELD(x, y) ||
5215           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5216           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5217         continue;
5218
5219       element = Feld[x][y];
5220
5221       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5222       {
5223         element = MovingOrBlocked2Element(x, y);
5224
5225         if (!IS_EXPLOSION_PROOF(element))
5226           RemoveMovingField(x, y);
5227       }
5228
5229       /* indestructible elements can only explode in center (but not flames) */
5230       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5231                                            mode == EX_TYPE_BORDER)) ||
5232           element == EL_FLAMES)
5233         continue;
5234
5235       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5236          behaviour, for example when touching a yamyam that explodes to rocks
5237          with active deadly shield, a rock is created under the player !!! */
5238       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5239 #if 0
5240       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5241           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5242            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5243 #else
5244       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5245 #endif
5246       {
5247         if (IS_ACTIVE_BOMB(element))
5248         {
5249           /* re-activate things under the bomb like gate or penguin */
5250           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5251           Back[x][y] = 0;
5252         }
5253
5254         continue;
5255       }
5256
5257       /* save walkable background elements while explosion on same tile */
5258       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5259           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5260         Back[x][y] = element;
5261
5262       /* ignite explodable elements reached by other explosion */
5263       if (element == EL_EXPLOSION)
5264         element = Store2[x][y];
5265
5266       if (AmoebaNr[x][y] &&
5267           (element == EL_AMOEBA_FULL ||
5268            element == EL_BD_AMOEBA ||
5269            element == EL_AMOEBA_GROWING))
5270       {
5271         AmoebaCnt[AmoebaNr[x][y]]--;
5272         AmoebaCnt2[AmoebaNr[x][y]]--;
5273       }
5274
5275       RemoveField(x, y);
5276
5277       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5278       {
5279         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5280
5281         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5282
5283         if (PLAYERINFO(ex, ey)->use_murphy)
5284           Store[x][y] = EL_EMPTY;
5285       }
5286
5287       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5288          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5289       else if (ELEM_IS_PLAYER(center_element))
5290         Store[x][y] = EL_EMPTY;
5291       else if (center_element == EL_YAMYAM)
5292         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5293       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5294         Store[x][y] = element_info[center_element].content.e[xx][yy];
5295 #if 1
5296       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5297          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5298          otherwise) -- FIX THIS !!! */
5299       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5300         Store[x][y] = element_info[element].content.e[1][1];
5301 #else
5302       else if (!CAN_EXPLODE(element))
5303         Store[x][y] = element_info[element].content.e[1][1];
5304 #endif
5305       else
5306         Store[x][y] = EL_EMPTY;
5307
5308       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5309           center_element == EL_AMOEBA_TO_DIAMOND)
5310         Store2[x][y] = element;
5311
5312       Feld[x][y] = EL_EXPLOSION;
5313       GfxElement[x][y] = artwork_element;
5314
5315       ExplodePhase[x][y] = 1;
5316       ExplodeDelay[x][y] = last_phase;
5317
5318       Stop[x][y] = TRUE;
5319     }
5320
5321     if (center_element == EL_YAMYAM)
5322       game.yamyam_content_nr =
5323         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5324
5325     return;
5326   }
5327
5328   if (Stop[ex][ey])
5329     return;
5330
5331   x = ex;
5332   y = ey;
5333
5334   if (phase == 1)
5335     GfxFrame[x][y] = 0;         /* restart explosion animation */
5336
5337   last_phase = ExplodeDelay[x][y];
5338
5339   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5340
5341   /* this can happen if the player leaves an explosion just in time */
5342   if (GfxElement[x][y] == EL_UNDEFINED)
5343     GfxElement[x][y] = EL_EMPTY;
5344
5345   border_element = Store2[x][y];
5346   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5347     border_element = StorePlayer[x][y];
5348
5349   if (phase == element_info[border_element].ignition_delay ||
5350       phase == last_phase)
5351   {
5352     boolean border_explosion = FALSE;
5353
5354     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5355         !PLAYER_EXPLOSION_PROTECTED(x, y))
5356     {
5357       KillPlayerUnlessExplosionProtected(x, y);
5358       border_explosion = TRUE;
5359     }
5360     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5361     {
5362       Feld[x][y] = Store2[x][y];
5363       Store2[x][y] = 0;
5364       Bang(x, y);
5365       border_explosion = TRUE;
5366     }
5367     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5368     {
5369       AmoebeUmwandeln(x, y);
5370       Store2[x][y] = 0;
5371       border_explosion = TRUE;
5372     }
5373
5374     /* if an element just explodes due to another explosion (chain-reaction),
5375        do not immediately end the new explosion when it was the last frame of
5376        the explosion (as it would be done in the following "if"-statement!) */
5377     if (border_explosion && phase == last_phase)
5378       return;
5379   }
5380
5381   if (phase == last_phase)
5382   {
5383     int element;
5384
5385     element = Feld[x][y] = Store[x][y];
5386     Store[x][y] = Store2[x][y] = 0;
5387     GfxElement[x][y] = EL_UNDEFINED;
5388
5389     /* player can escape from explosions and might therefore be still alive */
5390     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5391         element <= EL_PLAYER_IS_EXPLODING_4)
5392     {
5393       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5394       int explosion_element = EL_PLAYER_1 + player_nr;
5395       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5396       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5397
5398       if (level.use_explosion_element[player_nr])
5399         explosion_element = level.explosion_element[player_nr];
5400
5401       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5402                     element_info[explosion_element].content.e[xx][yy]);
5403     }
5404
5405     /* restore probably existing indestructible background element */
5406     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5407       element = Feld[x][y] = Back[x][y];
5408     Back[x][y] = 0;
5409
5410     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5411     GfxDir[x][y] = MV_NONE;
5412     ChangeDelay[x][y] = 0;
5413     ChangePage[x][y] = -1;
5414
5415     CustomValue[x][y] = 0;
5416
5417     InitField_WithBug2(x, y, FALSE);
5418
5419     TEST_DrawLevelField(x, y);
5420
5421     TestIfElementTouchesCustomElement(x, y);
5422
5423     if (GFX_CRUMBLED(element))
5424       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5425
5426     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5427       StorePlayer[x][y] = 0;
5428
5429     if (ELEM_IS_PLAYER(element))
5430       RelocatePlayer(x, y, element);
5431   }
5432   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5433   {
5434     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5435     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5436
5437     if (phase == delay)
5438       TEST_DrawLevelFieldCrumbled(x, y);
5439
5440     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5441     {
5442       DrawLevelElement(x, y, Back[x][y]);
5443       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5444     }
5445     else if (IS_WALKABLE_UNDER(Back[x][y]))
5446     {
5447       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5448       DrawLevelElementThruMask(x, y, Back[x][y]);
5449     }
5450     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5451       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5452   }
5453 }
5454
5455 void DynaExplode(int ex, int ey)
5456 {
5457   int i, j;
5458   int dynabomb_element = Feld[ex][ey];
5459   int dynabomb_size = 1;
5460   boolean dynabomb_xl = FALSE;
5461   struct PlayerInfo *player;
5462   static int xy[4][2] =
5463   {
5464     { 0, -1 },
5465     { -1, 0 },
5466     { +1, 0 },
5467     { 0, +1 }
5468   };
5469
5470   if (IS_ACTIVE_BOMB(dynabomb_element))
5471   {
5472     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5473     dynabomb_size = player->dynabomb_size;
5474     dynabomb_xl = player->dynabomb_xl;
5475     player->dynabombs_left++;
5476   }
5477
5478   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5479
5480   for (i = 0; i < NUM_DIRECTIONS; i++)
5481   {
5482     for (j = 1; j <= dynabomb_size; j++)
5483     {
5484       int x = ex + j * xy[i][0];
5485       int y = ey + j * xy[i][1];
5486       int element;
5487
5488       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5489         break;
5490
5491       element = Feld[x][y];
5492
5493       /* do not restart explosions of fields with active bombs */
5494       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5495         continue;
5496
5497       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5498
5499       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5500           !IS_DIGGABLE(element) && !dynabomb_xl)
5501         break;
5502     }
5503   }
5504 }
5505
5506 void Bang(int x, int y)
5507 {
5508   int element = MovingOrBlocked2Element(x, y);
5509   int explosion_type = EX_TYPE_NORMAL;
5510
5511   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5512   {
5513     struct PlayerInfo *player = PLAYERINFO(x, y);
5514
5515     element = Feld[x][y] = player->initial_element;
5516
5517     if (level.use_explosion_element[player->index_nr])
5518     {
5519       int explosion_element = level.explosion_element[player->index_nr];
5520
5521       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5522         explosion_type = EX_TYPE_CROSS;
5523       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5524         explosion_type = EX_TYPE_CENTER;
5525     }
5526   }
5527
5528   switch (element)
5529   {
5530     case EL_BUG:
5531     case EL_SPACESHIP:
5532     case EL_BD_BUTTERFLY:
5533     case EL_BD_FIREFLY:
5534     case EL_YAMYAM:
5535     case EL_DARK_YAMYAM:
5536     case EL_ROBOT:
5537     case EL_PACMAN:
5538     case EL_MOLE:
5539       RaiseScoreElement(element);
5540       break;
5541
5542     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5543     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5544     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5545     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5546     case EL_DYNABOMB_INCREASE_NUMBER:
5547     case EL_DYNABOMB_INCREASE_SIZE:
5548     case EL_DYNABOMB_INCREASE_POWER:
5549       explosion_type = EX_TYPE_DYNA;
5550       break;
5551
5552     case EL_DC_LANDMINE:
5553       explosion_type = EX_TYPE_CENTER;
5554       break;
5555
5556     case EL_PENGUIN:
5557     case EL_LAMP:
5558     case EL_LAMP_ACTIVE:
5559     case EL_AMOEBA_TO_DIAMOND:
5560       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5561         explosion_type = EX_TYPE_CENTER;
5562       break;
5563
5564     default:
5565       if (element_info[element].explosion_type == EXPLODES_CROSS)
5566         explosion_type = EX_TYPE_CROSS;
5567       else if (element_info[element].explosion_type == EXPLODES_1X1)
5568         explosion_type = EX_TYPE_CENTER;
5569       break;
5570   }
5571
5572   if (explosion_type == EX_TYPE_DYNA)
5573     DynaExplode(x, y);
5574   else
5575     Explode(x, y, EX_PHASE_START, explosion_type);
5576
5577   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5578 }
5579
5580 void SplashAcid(int x, int y)
5581 {
5582   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5583       (!IN_LEV_FIELD(x - 1, y - 2) ||
5584        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5585     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5586
5587   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5588       (!IN_LEV_FIELD(x + 1, y - 2) ||
5589        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5590     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5591
5592   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5593 }
5594
5595 static void InitBeltMovement()
5596 {
5597   static int belt_base_element[4] =
5598   {
5599     EL_CONVEYOR_BELT_1_LEFT,
5600     EL_CONVEYOR_BELT_2_LEFT,
5601     EL_CONVEYOR_BELT_3_LEFT,
5602     EL_CONVEYOR_BELT_4_LEFT
5603   };
5604   static int belt_base_active_element[4] =
5605   {
5606     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5607     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5608     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5609     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5610   };
5611
5612   int x, y, i, j;
5613
5614   /* set frame order for belt animation graphic according to belt direction */
5615   for (i = 0; i < NUM_BELTS; i++)
5616   {
5617     int belt_nr = i;
5618
5619     for (j = 0; j < NUM_BELT_PARTS; j++)
5620     {
5621       int element = belt_base_active_element[belt_nr] + j;
5622       int graphic_1 = el2img(element);
5623       int graphic_2 = el2panelimg(element);
5624
5625       if (game.belt_dir[i] == MV_LEFT)
5626       {
5627         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5628         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5629       }
5630       else
5631       {
5632         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5633         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5634       }
5635     }
5636   }
5637
5638   SCAN_PLAYFIELD(x, y)
5639   {
5640     int element = Feld[x][y];
5641
5642     for (i = 0; i < NUM_BELTS; i++)
5643     {
5644       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5645       {
5646         int e_belt_nr = getBeltNrFromBeltElement(element);
5647         int belt_nr = i;
5648
5649         if (e_belt_nr == belt_nr)
5650         {
5651           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5652
5653           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5654         }
5655       }
5656     }
5657   }
5658 }
5659
5660 static void ToggleBeltSwitch(int x, int y)
5661 {
5662   static int belt_base_element[4] =
5663   {
5664     EL_CONVEYOR_BELT_1_LEFT,
5665     EL_CONVEYOR_BELT_2_LEFT,
5666     EL_CONVEYOR_BELT_3_LEFT,
5667     EL_CONVEYOR_BELT_4_LEFT
5668   };
5669   static int belt_base_active_element[4] =
5670   {
5671     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5672     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5673     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5674     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5675   };
5676   static int belt_base_switch_element[4] =
5677   {
5678     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5679     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5680     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5681     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5682   };
5683   static int belt_move_dir[4] =
5684   {
5685     MV_LEFT,
5686     MV_NONE,
5687     MV_RIGHT,
5688     MV_NONE,
5689   };
5690
5691   int element = Feld[x][y];
5692   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5693   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5694   int belt_dir = belt_move_dir[belt_dir_nr];
5695   int xx, yy, i;
5696
5697   if (!IS_BELT_SWITCH(element))
5698     return;
5699
5700   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5701   game.belt_dir[belt_nr] = belt_dir;
5702
5703   if (belt_dir_nr == 3)
5704     belt_dir_nr = 1;
5705
5706   /* set frame order for belt animation graphic according to belt direction */
5707   for (i = 0; i < NUM_BELT_PARTS; i++)
5708   {
5709     int element = belt_base_active_element[belt_nr] + i;
5710     int graphic_1 = el2img(element);
5711     int graphic_2 = el2panelimg(element);
5712
5713     if (belt_dir == MV_LEFT)
5714     {
5715       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5716       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5717     }
5718     else
5719     {
5720       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5721       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5722     }
5723   }
5724
5725   SCAN_PLAYFIELD(xx, yy)
5726   {
5727     int element = Feld[xx][yy];
5728
5729     if (IS_BELT_SWITCH(element))
5730     {
5731       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5732
5733       if (e_belt_nr == belt_nr)
5734       {
5735         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5736         TEST_DrawLevelField(xx, yy);
5737       }
5738     }
5739     else if (IS_BELT(element) && belt_dir != MV_NONE)
5740     {
5741       int e_belt_nr = getBeltNrFromBeltElement(element);
5742
5743       if (e_belt_nr == belt_nr)
5744       {
5745         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5746
5747         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5748         TEST_DrawLevelField(xx, yy);
5749       }
5750     }
5751     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5752     {
5753       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5754
5755       if (e_belt_nr == belt_nr)
5756       {
5757         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5758
5759         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5760         TEST_DrawLevelField(xx, yy);
5761       }
5762     }
5763   }
5764 }
5765
5766 static void ToggleSwitchgateSwitch(int x, int y)
5767 {
5768   int xx, yy;
5769
5770   game.switchgate_pos = !game.switchgate_pos;
5771
5772   SCAN_PLAYFIELD(xx, yy)
5773   {
5774     int element = Feld[xx][yy];
5775
5776     if (element == EL_SWITCHGATE_SWITCH_UP)
5777     {
5778       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5779       TEST_DrawLevelField(xx, yy);
5780     }
5781     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5782     {
5783       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5784       TEST_DrawLevelField(xx, yy);
5785     }
5786     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5787     {
5788       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5789       TEST_DrawLevelField(xx, yy);
5790     }
5791     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5792     {
5793       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5794       TEST_DrawLevelField(xx, yy);
5795     }
5796     else if (element == EL_SWITCHGATE_OPEN ||
5797              element == EL_SWITCHGATE_OPENING)
5798     {
5799       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5800
5801       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5802     }
5803     else if (element == EL_SWITCHGATE_CLOSED ||
5804              element == EL_SWITCHGATE_CLOSING)
5805     {
5806       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5807
5808       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5809     }
5810   }
5811 }
5812
5813 static int getInvisibleActiveFromInvisibleElement(int element)
5814 {
5815   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5816           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5817           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5818           element);
5819 }
5820
5821 static int getInvisibleFromInvisibleActiveElement(int element)
5822 {
5823   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5824           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5825           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5826           element);
5827 }
5828
5829 static void RedrawAllLightSwitchesAndInvisibleElements()
5830 {
5831   int x, y;
5832
5833   SCAN_PLAYFIELD(x, y)
5834   {
5835     int element = Feld[x][y];
5836
5837     if (element == EL_LIGHT_SWITCH &&
5838         game.light_time_left > 0)
5839     {
5840       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5841       TEST_DrawLevelField(x, y);
5842     }
5843     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5844              game.light_time_left == 0)
5845     {
5846       Feld[x][y] = EL_LIGHT_SWITCH;
5847       TEST_DrawLevelField(x, y);
5848     }
5849     else if (element == EL_EMC_DRIPPER &&
5850              game.light_time_left > 0)
5851     {
5852       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5853       TEST_DrawLevelField(x, y);
5854     }
5855     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5856              game.light_time_left == 0)
5857     {
5858       Feld[x][y] = EL_EMC_DRIPPER;
5859       TEST_DrawLevelField(x, y);
5860     }
5861     else if (element == EL_INVISIBLE_STEELWALL ||
5862              element == EL_INVISIBLE_WALL ||
5863              element == EL_INVISIBLE_SAND)
5864     {
5865       if (game.light_time_left > 0)
5866         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5867
5868       TEST_DrawLevelField(x, y);
5869
5870       /* uncrumble neighbour fields, if needed */
5871       if (element == EL_INVISIBLE_SAND)
5872         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5873     }
5874     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5875              element == EL_INVISIBLE_WALL_ACTIVE ||
5876              element == EL_INVISIBLE_SAND_ACTIVE)
5877     {
5878       if (game.light_time_left == 0)
5879         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5880
5881       TEST_DrawLevelField(x, y);
5882
5883       /* re-crumble neighbour fields, if needed */
5884       if (element == EL_INVISIBLE_SAND)
5885         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5886     }
5887   }
5888 }
5889
5890 static void RedrawAllInvisibleElementsForLenses()
5891 {
5892   int x, y;
5893
5894   SCAN_PLAYFIELD(x, y)
5895   {
5896     int element = Feld[x][y];
5897
5898     if (element == EL_EMC_DRIPPER &&
5899         game.lenses_time_left > 0)
5900     {
5901       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5902       TEST_DrawLevelField(x, y);
5903     }
5904     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5905              game.lenses_time_left == 0)
5906     {
5907       Feld[x][y] = EL_EMC_DRIPPER;
5908       TEST_DrawLevelField(x, y);
5909     }
5910     else if (element == EL_INVISIBLE_STEELWALL ||
5911              element == EL_INVISIBLE_WALL ||
5912              element == EL_INVISIBLE_SAND)
5913     {
5914       if (game.lenses_time_left > 0)
5915         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5916
5917       TEST_DrawLevelField(x, y);
5918
5919       /* uncrumble neighbour fields, if needed */
5920       if (element == EL_INVISIBLE_SAND)
5921         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5922     }
5923     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5924              element == EL_INVISIBLE_WALL_ACTIVE ||
5925              element == EL_INVISIBLE_SAND_ACTIVE)
5926     {
5927       if (game.lenses_time_left == 0)
5928         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5929
5930       TEST_DrawLevelField(x, y);
5931
5932       /* re-crumble neighbour fields, if needed */
5933       if (element == EL_INVISIBLE_SAND)
5934         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5935     }
5936   }
5937 }
5938
5939 static void RedrawAllInvisibleElementsForMagnifier()
5940 {
5941   int x, y;
5942
5943   SCAN_PLAYFIELD(x, y)
5944   {
5945     int element = Feld[x][y];
5946
5947     if (element == EL_EMC_FAKE_GRASS &&
5948         game.magnify_time_left > 0)
5949     {
5950       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5951       TEST_DrawLevelField(x, y);
5952     }
5953     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5954              game.magnify_time_left == 0)
5955     {
5956       Feld[x][y] = EL_EMC_FAKE_GRASS;
5957       TEST_DrawLevelField(x, y);
5958     }
5959     else if (IS_GATE_GRAY(element) &&
5960              game.magnify_time_left > 0)
5961     {
5962       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5963                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5964                     IS_EM_GATE_GRAY(element) ?
5965                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5966                     IS_EMC_GATE_GRAY(element) ?
5967                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5968                     IS_DC_GATE_GRAY(element) ?
5969                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5970                     element);
5971       TEST_DrawLevelField(x, y);
5972     }
5973     else if (IS_GATE_GRAY_ACTIVE(element) &&
5974              game.magnify_time_left == 0)
5975     {
5976       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5977                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5978                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5979                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5980                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5981                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5982                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5983                     EL_DC_GATE_WHITE_GRAY :
5984                     element);
5985       TEST_DrawLevelField(x, y);
5986     }
5987   }
5988 }
5989
5990 static void ToggleLightSwitch(int x, int y)
5991 {
5992   int element = Feld[x][y];
5993
5994   game.light_time_left =
5995     (element == EL_LIGHT_SWITCH ?
5996      level.time_light * FRAMES_PER_SECOND : 0);
5997
5998   RedrawAllLightSwitchesAndInvisibleElements();
5999 }
6000
6001 static void ActivateTimegateSwitch(int x, int y)
6002 {
6003   int xx, yy;
6004
6005   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6006
6007   SCAN_PLAYFIELD(xx, yy)
6008   {
6009     int element = Feld[xx][yy];
6010
6011     if (element == EL_TIMEGATE_CLOSED ||
6012         element == EL_TIMEGATE_CLOSING)
6013     {
6014       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6015       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6016     }
6017
6018     /*
6019     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6020     {
6021       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6022       TEST_DrawLevelField(xx, yy);
6023     }
6024     */
6025
6026   }
6027
6028   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6029                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6030 }
6031
6032 void Impact(int x, int y)
6033 {
6034   boolean last_line = (y == lev_fieldy - 1);
6035   boolean object_hit = FALSE;
6036   boolean impact = (last_line || object_hit);
6037   int element = Feld[x][y];
6038   int smashed = EL_STEELWALL;
6039
6040   if (!last_line)       /* check if element below was hit */
6041   {
6042     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6043       return;
6044
6045     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6046                                          MovDir[x][y + 1] != MV_DOWN ||
6047                                          MovPos[x][y + 1] <= TILEY / 2));
6048
6049     /* do not smash moving elements that left the smashed field in time */
6050     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6051         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6052       object_hit = FALSE;
6053
6054 #if USE_QUICKSAND_IMPACT_BUGFIX
6055     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6056     {
6057       RemoveMovingField(x, y + 1);
6058       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6059       Feld[x][y + 2] = EL_ROCK;
6060       TEST_DrawLevelField(x, y + 2);
6061
6062       object_hit = TRUE;
6063     }
6064
6065     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6066     {
6067       RemoveMovingField(x, y + 1);
6068       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6069       Feld[x][y + 2] = EL_ROCK;
6070       TEST_DrawLevelField(x, y + 2);
6071
6072       object_hit = TRUE;
6073     }
6074 #endif
6075
6076     if (object_hit)
6077       smashed = MovingOrBlocked2Element(x, y + 1);
6078
6079     impact = (last_line || object_hit);
6080   }
6081
6082   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6083   {
6084     SplashAcid(x, y + 1);
6085     return;
6086   }
6087
6088   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6089   /* only reset graphic animation if graphic really changes after impact */
6090   if (impact &&
6091       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6092   {
6093     ResetGfxAnimation(x, y);
6094     TEST_DrawLevelField(x, y);
6095   }
6096
6097   if (impact && CAN_EXPLODE_IMPACT(element))
6098   {
6099     Bang(x, y);
6100     return;
6101   }
6102   else if (impact && element == EL_PEARL &&
6103            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6104   {
6105     ResetGfxAnimation(x, y);
6106
6107     Feld[x][y] = EL_PEARL_BREAKING;
6108     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6109     return;
6110   }
6111   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6112   {
6113     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6114
6115     return;
6116   }
6117
6118   if (impact && element == EL_AMOEBA_DROP)
6119   {
6120     if (object_hit && IS_PLAYER(x, y + 1))
6121       KillPlayerUnlessEnemyProtected(x, y + 1);
6122     else if (object_hit && smashed == EL_PENGUIN)
6123       Bang(x, y + 1);
6124     else
6125     {
6126       Feld[x][y] = EL_AMOEBA_GROWING;
6127       Store[x][y] = EL_AMOEBA_WET;
6128
6129       ResetRandomAnimationValue(x, y);
6130     }
6131     return;
6132   }
6133
6134   if (object_hit)               /* check which object was hit */
6135   {
6136     if ((CAN_PASS_MAGIC_WALL(element) && 
6137          (smashed == EL_MAGIC_WALL ||
6138           smashed == EL_BD_MAGIC_WALL)) ||
6139         (CAN_PASS_DC_MAGIC_WALL(element) &&
6140          smashed == EL_DC_MAGIC_WALL))
6141     {
6142       int xx, yy;
6143       int activated_magic_wall =
6144         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6145          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6146          EL_DC_MAGIC_WALL_ACTIVE);
6147
6148       /* activate magic wall / mill */
6149       SCAN_PLAYFIELD(xx, yy)
6150       {
6151         if (Feld[xx][yy] == smashed)
6152           Feld[xx][yy] = activated_magic_wall;
6153       }
6154
6155       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6156       game.magic_wall_active = TRUE;
6157
6158       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6159                             SND_MAGIC_WALL_ACTIVATING :
6160                             smashed == EL_BD_MAGIC_WALL ?
6161                             SND_BD_MAGIC_WALL_ACTIVATING :
6162                             SND_DC_MAGIC_WALL_ACTIVATING));
6163     }
6164
6165     if (IS_PLAYER(x, y + 1))
6166     {
6167       if (CAN_SMASH_PLAYER(element))
6168       {
6169         KillPlayerUnlessEnemyProtected(x, y + 1);
6170         return;
6171       }
6172     }
6173     else if (smashed == EL_PENGUIN)
6174     {
6175       if (CAN_SMASH_PLAYER(element))
6176       {
6177         Bang(x, y + 1);
6178         return;
6179       }
6180     }
6181     else if (element == EL_BD_DIAMOND)
6182     {
6183       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6184       {
6185         Bang(x, y + 1);
6186         return;
6187       }
6188     }
6189     else if (((element == EL_SP_INFOTRON ||
6190                element == EL_SP_ZONK) &&
6191               (smashed == EL_SP_SNIKSNAK ||
6192                smashed == EL_SP_ELECTRON ||
6193                smashed == EL_SP_DISK_ORANGE)) ||
6194              (element == EL_SP_INFOTRON &&
6195               smashed == EL_SP_DISK_YELLOW))
6196     {
6197       Bang(x, y + 1);
6198       return;
6199     }
6200     else if (CAN_SMASH_EVERYTHING(element))
6201     {
6202       if (IS_CLASSIC_ENEMY(smashed) ||
6203           CAN_EXPLODE_SMASHED(smashed))
6204       {
6205         Bang(x, y + 1);
6206         return;
6207       }
6208       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6209       {
6210         if (smashed == EL_LAMP ||
6211             smashed == EL_LAMP_ACTIVE)
6212         {
6213           Bang(x, y + 1);
6214           return;
6215         }
6216         else if (smashed == EL_NUT)
6217         {
6218           Feld[x][y + 1] = EL_NUT_BREAKING;
6219           PlayLevelSound(x, y, SND_NUT_BREAKING);
6220           RaiseScoreElement(EL_NUT);
6221           return;
6222         }
6223         else if (smashed == EL_PEARL)
6224         {
6225           ResetGfxAnimation(x, y);
6226
6227           Feld[x][y + 1] = EL_PEARL_BREAKING;
6228           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6229           return;
6230         }
6231         else if (smashed == EL_DIAMOND)
6232         {
6233           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6234           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6235           return;
6236         }
6237         else if (IS_BELT_SWITCH(smashed))
6238         {
6239           ToggleBeltSwitch(x, y + 1);
6240         }
6241         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6242                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6243                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6244                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6245         {
6246           ToggleSwitchgateSwitch(x, y + 1);
6247         }
6248         else if (smashed == EL_LIGHT_SWITCH ||
6249                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6250         {
6251           ToggleLightSwitch(x, y + 1);
6252         }
6253         else
6254         {
6255           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6256
6257           CheckElementChangeBySide(x, y + 1, smashed, element,
6258                                    CE_SWITCHED, CH_SIDE_TOP);
6259           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6260                                             CH_SIDE_TOP);
6261         }
6262       }
6263       else
6264       {
6265         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6266       }
6267     }
6268   }
6269
6270   /* play sound of magic wall / mill */
6271   if (!last_line &&
6272       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6273        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6274        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6275   {
6276     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6277       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6278     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6279       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6280     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6281       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6282
6283     return;
6284   }
6285
6286   /* play sound of object that hits the ground */
6287   if (last_line || object_hit)
6288     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6289 }
6290
6291 inline static void TurnRoundExt(int x, int y)
6292 {
6293   static struct
6294   {
6295     int dx, dy;
6296   } move_xy[] =
6297   {
6298     {  0,  0 },
6299     { -1,  0 },
6300     { +1,  0 },
6301     {  0,  0 },
6302     {  0, -1 },
6303     {  0,  0 }, { 0, 0 }, { 0, 0 },
6304     {  0, +1 }
6305   };
6306   static struct
6307   {
6308     int left, right, back;
6309   } turn[] =
6310   {
6311     { 0,        0,              0        },
6312     { MV_DOWN,  MV_UP,          MV_RIGHT },
6313     { MV_UP,    MV_DOWN,        MV_LEFT  },
6314     { 0,        0,              0        },
6315     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6316     { 0,        0,              0        },
6317     { 0,        0,              0        },
6318     { 0,        0,              0        },
6319     { MV_RIGHT, MV_LEFT,        MV_UP    }
6320   };
6321
6322   int element = Feld[x][y];
6323   int move_pattern = element_info[element].move_pattern;
6324
6325   int old_move_dir = MovDir[x][y];
6326   int left_dir  = turn[old_move_dir].left;
6327   int right_dir = turn[old_move_dir].right;
6328   int back_dir  = turn[old_move_dir].back;
6329
6330   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6331   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6332   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6333   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6334
6335   int left_x  = x + left_dx,  left_y  = y + left_dy;
6336   int right_x = x + right_dx, right_y = y + right_dy;
6337   int move_x  = x + move_dx,  move_y  = y + move_dy;
6338
6339   int xx, yy;
6340
6341   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6342   {
6343     TestIfBadThingTouchesOtherBadThing(x, y);
6344
6345     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6346       MovDir[x][y] = right_dir;
6347     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6348       MovDir[x][y] = left_dir;
6349
6350     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6351       MovDelay[x][y] = 9;
6352     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6353       MovDelay[x][y] = 1;
6354   }
6355   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6356   {
6357     TestIfBadThingTouchesOtherBadThing(x, y);
6358
6359     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6360       MovDir[x][y] = left_dir;
6361     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6362       MovDir[x][y] = right_dir;
6363
6364     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6365       MovDelay[x][y] = 9;
6366     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6367       MovDelay[x][y] = 1;
6368   }
6369   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6370   {
6371     TestIfBadThingTouchesOtherBadThing(x, y);
6372
6373     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6374       MovDir[x][y] = left_dir;
6375     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6376       MovDir[x][y] = right_dir;
6377
6378     if (MovDir[x][y] != old_move_dir)
6379       MovDelay[x][y] = 9;
6380   }
6381   else if (element == EL_YAMYAM)
6382   {
6383     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6384     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6385
6386     if (can_turn_left && can_turn_right)
6387       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6388     else if (can_turn_left)
6389       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6390     else if (can_turn_right)
6391       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6392     else
6393       MovDir[x][y] = back_dir;
6394
6395     MovDelay[x][y] = 16 + 16 * RND(3);
6396   }
6397   else if (element == EL_DARK_YAMYAM)
6398   {
6399     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6400                                                          left_x, left_y);
6401     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6402                                                          right_x, right_y);
6403
6404     if (can_turn_left && can_turn_right)
6405       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6406     else if (can_turn_left)
6407       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6408     else if (can_turn_right)
6409       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6410     else
6411       MovDir[x][y] = back_dir;
6412
6413     MovDelay[x][y] = 16 + 16 * RND(3);
6414   }
6415   else if (element == EL_PACMAN)
6416   {
6417     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6418     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6419
6420     if (can_turn_left && can_turn_right)
6421       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6422     else if (can_turn_left)
6423       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6424     else if (can_turn_right)
6425       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6426     else
6427       MovDir[x][y] = back_dir;
6428
6429     MovDelay[x][y] = 6 + RND(40);
6430   }
6431   else if (element == EL_PIG)
6432   {
6433     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6434     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6435     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6436     boolean should_turn_left, should_turn_right, should_move_on;
6437     int rnd_value = 24;
6438     int rnd = RND(rnd_value);
6439
6440     should_turn_left = (can_turn_left &&
6441                         (!can_move_on ||
6442                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6443                                                    y + back_dy + left_dy)));
6444     should_turn_right = (can_turn_right &&
6445                          (!can_move_on ||
6446                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6447                                                     y + back_dy + right_dy)));
6448     should_move_on = (can_move_on &&
6449                       (!can_turn_left ||
6450                        !can_turn_right ||
6451                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6452                                                  y + move_dy + left_dy) ||
6453                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6454                                                  y + move_dy + right_dy)));
6455
6456     if (should_turn_left || should_turn_right || should_move_on)
6457     {
6458       if (should_turn_left && should_turn_right && should_move_on)
6459         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6460                         rnd < 2 * rnd_value / 3 ? right_dir :
6461                         old_move_dir);
6462       else if (should_turn_left && should_turn_right)
6463         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6464       else if (should_turn_left && should_move_on)
6465         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6466       else if (should_turn_right && should_move_on)
6467         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6468       else if (should_turn_left)
6469         MovDir[x][y] = left_dir;
6470       else if (should_turn_right)
6471         MovDir[x][y] = right_dir;
6472       else if (should_move_on)
6473         MovDir[x][y] = old_move_dir;
6474     }
6475     else if (can_move_on && rnd > rnd_value / 8)
6476       MovDir[x][y] = old_move_dir;
6477     else if (can_turn_left && can_turn_right)
6478       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6479     else if (can_turn_left && rnd > rnd_value / 8)
6480       MovDir[x][y] = left_dir;
6481     else if (can_turn_right && rnd > rnd_value/8)
6482       MovDir[x][y] = right_dir;
6483     else
6484       MovDir[x][y] = back_dir;
6485
6486     xx = x + move_xy[MovDir[x][y]].dx;
6487     yy = y + move_xy[MovDir[x][y]].dy;
6488
6489     if (!IN_LEV_FIELD(xx, yy) ||
6490         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6491       MovDir[x][y] = old_move_dir;
6492
6493     MovDelay[x][y] = 0;
6494   }
6495   else if (element == EL_DRAGON)
6496   {
6497     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6498     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6499     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6500     int rnd_value = 24;
6501     int rnd = RND(rnd_value);
6502
6503     if (can_move_on && rnd > rnd_value / 8)
6504       MovDir[x][y] = old_move_dir;
6505     else if (can_turn_left && can_turn_right)
6506       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6507     else if (can_turn_left && rnd > rnd_value / 8)
6508       MovDir[x][y] = left_dir;
6509     else if (can_turn_right && rnd > rnd_value / 8)
6510       MovDir[x][y] = right_dir;
6511     else
6512       MovDir[x][y] = back_dir;
6513
6514     xx = x + move_xy[MovDir[x][y]].dx;
6515     yy = y + move_xy[MovDir[x][y]].dy;
6516
6517     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6518       MovDir[x][y] = old_move_dir;
6519
6520     MovDelay[x][y] = 0;
6521   }
6522   else if (element == EL_MOLE)
6523   {
6524     boolean can_move_on =
6525       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6526                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6527                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6528     if (!can_move_on)
6529     {
6530       boolean can_turn_left =
6531         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6532                               IS_AMOEBOID(Feld[left_x][left_y])));
6533
6534       boolean can_turn_right =
6535         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6536                               IS_AMOEBOID(Feld[right_x][right_y])));
6537
6538       if (can_turn_left && can_turn_right)
6539         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6540       else if (can_turn_left)
6541         MovDir[x][y] = left_dir;
6542       else
6543         MovDir[x][y] = right_dir;
6544     }
6545
6546     if (MovDir[x][y] != old_move_dir)
6547       MovDelay[x][y] = 9;
6548   }
6549   else if (element == EL_BALLOON)
6550   {
6551     MovDir[x][y] = game.wind_direction;
6552     MovDelay[x][y] = 0;
6553   }
6554   else if (element == EL_SPRING)
6555   {
6556     if (MovDir[x][y] & MV_HORIZONTAL)
6557     {
6558       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6559           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6560       {
6561         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6562         ResetGfxAnimation(move_x, move_y);
6563         TEST_DrawLevelField(move_x, move_y);
6564
6565         MovDir[x][y] = back_dir;
6566       }
6567       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6568                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6569         MovDir[x][y] = MV_NONE;
6570     }
6571
6572     MovDelay[x][y] = 0;
6573   }
6574   else if (element == EL_ROBOT ||
6575            element == EL_SATELLITE ||
6576            element == EL_PENGUIN ||
6577            element == EL_EMC_ANDROID)
6578   {
6579     int attr_x = -1, attr_y = -1;
6580
6581     if (AllPlayersGone)
6582     {
6583       attr_x = ExitX;
6584       attr_y = ExitY;
6585     }
6586     else
6587     {
6588       int i;
6589
6590       for (i = 0; i < MAX_PLAYERS; i++)
6591       {
6592         struct PlayerInfo *player = &stored_player[i];
6593         int jx = player->jx, jy = player->jy;
6594
6595         if (!player->active)
6596           continue;
6597
6598         if (attr_x == -1 ||
6599             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6600         {
6601           attr_x = jx;
6602           attr_y = jy;
6603         }
6604       }
6605     }
6606
6607     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6608         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6609          game.engine_version < VERSION_IDENT(3,1,0,0)))
6610     {
6611       attr_x = ZX;
6612       attr_y = ZY;
6613     }
6614
6615     if (element == EL_PENGUIN)
6616     {
6617       int i;
6618       static int xy[4][2] =
6619       {
6620         { 0, -1 },
6621         { -1, 0 },
6622         { +1, 0 },
6623         { 0, +1 }
6624       };
6625
6626       for (i = 0; i < NUM_DIRECTIONS; i++)
6627       {
6628         int ex = x + xy[i][0];
6629         int ey = y + xy[i][1];
6630
6631         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6632                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6633                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6634                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6635         {
6636           attr_x = ex;
6637           attr_y = ey;
6638           break;
6639         }
6640       }
6641     }
6642
6643     MovDir[x][y] = MV_NONE;
6644     if (attr_x < x)
6645       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6646     else if (attr_x > x)
6647       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6648     if (attr_y < y)
6649       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6650     else if (attr_y > y)
6651       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6652
6653     if (element == EL_ROBOT)
6654     {
6655       int newx, newy;
6656
6657       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6658         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6659       Moving2Blocked(x, y, &newx, &newy);
6660
6661       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6662         MovDelay[x][y] = 8 + 8 * !RND(3);
6663       else
6664         MovDelay[x][y] = 16;
6665     }
6666     else if (element == EL_PENGUIN)
6667     {
6668       int newx, newy;
6669
6670       MovDelay[x][y] = 1;
6671
6672       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6673       {
6674         boolean first_horiz = RND(2);
6675         int new_move_dir = MovDir[x][y];
6676
6677         MovDir[x][y] =
6678           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6679         Moving2Blocked(x, y, &newx, &newy);
6680
6681         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6682           return;
6683
6684         MovDir[x][y] =
6685           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6686         Moving2Blocked(x, y, &newx, &newy);
6687
6688         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6689           return;
6690
6691         MovDir[x][y] = old_move_dir;
6692         return;
6693       }
6694     }
6695     else if (element == EL_SATELLITE)
6696     {
6697       int newx, newy;
6698
6699       MovDelay[x][y] = 1;
6700
6701       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6702       {
6703         boolean first_horiz = RND(2);
6704         int new_move_dir = MovDir[x][y];
6705
6706         MovDir[x][y] =
6707           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6708         Moving2Blocked(x, y, &newx, &newy);
6709
6710         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6711           return;
6712
6713         MovDir[x][y] =
6714           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6715         Moving2Blocked(x, y, &newx, &newy);
6716
6717         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6718           return;
6719
6720         MovDir[x][y] = old_move_dir;
6721         return;
6722       }
6723     }
6724     else if (element == EL_EMC_ANDROID)
6725     {
6726       static int check_pos[16] =
6727       {
6728         -1,             /*  0 => (invalid)          */
6729         7,              /*  1 => MV_LEFT            */
6730         3,              /*  2 => MV_RIGHT           */
6731         -1,             /*  3 => (invalid)          */
6732         1,              /*  4 =>            MV_UP   */
6733         0,              /*  5 => MV_LEFT  | MV_UP   */
6734         2,              /*  6 => MV_RIGHT | MV_UP   */
6735         -1,             /*  7 => (invalid)          */
6736         5,              /*  8 =>            MV_DOWN */
6737         6,              /*  9 => MV_LEFT  | MV_DOWN */
6738         4,              /* 10 => MV_RIGHT | MV_DOWN */
6739         -1,             /* 11 => (invalid)          */
6740         -1,             /* 12 => (invalid)          */
6741         -1,             /* 13 => (invalid)          */
6742         -1,             /* 14 => (invalid)          */
6743         -1,             /* 15 => (invalid)          */
6744       };
6745       static struct
6746       {
6747         int dx, dy;
6748         int dir;
6749       } check_xy[8] =
6750       {
6751         { -1, -1,       MV_LEFT  | MV_UP   },
6752         {  0, -1,                  MV_UP   },
6753         { +1, -1,       MV_RIGHT | MV_UP   },
6754         { +1,  0,       MV_RIGHT           },
6755         { +1, +1,       MV_RIGHT | MV_DOWN },
6756         {  0, +1,                  MV_DOWN },
6757         { -1, +1,       MV_LEFT  | MV_DOWN },
6758         { -1,  0,       MV_LEFT            },
6759       };
6760       int start_pos, check_order;
6761       boolean can_clone = FALSE;
6762       int i;
6763
6764       /* check if there is any free field around current position */
6765       for (i = 0; i < 8; i++)
6766       {
6767         int newx = x + check_xy[i].dx;
6768         int newy = y + check_xy[i].dy;
6769
6770         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6771         {
6772           can_clone = TRUE;
6773
6774           break;
6775         }
6776       }
6777
6778       if (can_clone)            /* randomly find an element to clone */
6779       {
6780         can_clone = FALSE;
6781
6782         start_pos = check_pos[RND(8)];
6783         check_order = (RND(2) ? -1 : +1);
6784
6785         for (i = 0; i < 8; i++)
6786         {
6787           int pos_raw = start_pos + i * check_order;
6788           int pos = (pos_raw + 8) % 8;
6789           int newx = x + check_xy[pos].dx;
6790           int newy = y + check_xy[pos].dy;
6791
6792           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6793           {
6794             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6795             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6796
6797             Store[x][y] = Feld[newx][newy];
6798
6799             can_clone = TRUE;
6800
6801             break;
6802           }
6803         }
6804       }
6805
6806       if (can_clone)            /* randomly find a direction to move */
6807       {
6808         can_clone = FALSE;
6809
6810         start_pos = check_pos[RND(8)];
6811         check_order = (RND(2) ? -1 : +1);
6812
6813         for (i = 0; i < 8; i++)
6814         {
6815           int pos_raw = start_pos + i * check_order;
6816           int pos = (pos_raw + 8) % 8;
6817           int newx = x + check_xy[pos].dx;
6818           int newy = y + check_xy[pos].dy;
6819           int new_move_dir = check_xy[pos].dir;
6820
6821           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6822           {
6823             MovDir[x][y] = new_move_dir;
6824             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6825
6826             can_clone = TRUE;
6827
6828             break;
6829           }
6830         }
6831       }
6832
6833       if (can_clone)            /* cloning and moving successful */
6834         return;
6835
6836       /* cannot clone -- try to move towards player */
6837
6838       start_pos = check_pos[MovDir[x][y] & 0x0f];
6839       check_order = (RND(2) ? -1 : +1);
6840
6841       for (i = 0; i < 3; i++)
6842       {
6843         /* first check start_pos, then previous/next or (next/previous) pos */
6844         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6845         int pos = (pos_raw + 8) % 8;
6846         int newx = x + check_xy[pos].dx;
6847         int newy = y + check_xy[pos].dy;
6848         int new_move_dir = check_xy[pos].dir;
6849
6850         if (IS_PLAYER(newx, newy))
6851           break;
6852
6853         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6854         {
6855           MovDir[x][y] = new_move_dir;
6856           MovDelay[x][y] = level.android_move_time * 8 + 1;
6857
6858           break;
6859         }
6860       }
6861     }
6862   }
6863   else if (move_pattern == MV_TURNING_LEFT ||
6864            move_pattern == MV_TURNING_RIGHT ||
6865            move_pattern == MV_TURNING_LEFT_RIGHT ||
6866            move_pattern == MV_TURNING_RIGHT_LEFT ||
6867            move_pattern == MV_TURNING_RANDOM ||
6868            move_pattern == MV_ALL_DIRECTIONS)
6869   {
6870     boolean can_turn_left =
6871       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6872     boolean can_turn_right =
6873       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6874
6875     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6876       return;
6877
6878     if (move_pattern == MV_TURNING_LEFT)
6879       MovDir[x][y] = left_dir;
6880     else if (move_pattern == MV_TURNING_RIGHT)
6881       MovDir[x][y] = right_dir;
6882     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6883       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6884     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6885       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6886     else if (move_pattern == MV_TURNING_RANDOM)
6887       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6888                       can_turn_right && !can_turn_left ? right_dir :
6889                       RND(2) ? left_dir : right_dir);
6890     else if (can_turn_left && can_turn_right)
6891       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6892     else if (can_turn_left)
6893       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6894     else if (can_turn_right)
6895       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6896     else
6897       MovDir[x][y] = back_dir;
6898
6899     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6900   }
6901   else if (move_pattern == MV_HORIZONTAL ||
6902            move_pattern == MV_VERTICAL)
6903   {
6904     if (move_pattern & old_move_dir)
6905       MovDir[x][y] = back_dir;
6906     else if (move_pattern == MV_HORIZONTAL)
6907       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6908     else if (move_pattern == MV_VERTICAL)
6909       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6910
6911     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6912   }
6913   else if (move_pattern & MV_ANY_DIRECTION)
6914   {
6915     MovDir[x][y] = move_pattern;
6916     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6917   }
6918   else if (move_pattern & MV_WIND_DIRECTION)
6919   {
6920     MovDir[x][y] = game.wind_direction;
6921     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6922   }
6923   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6924   {
6925     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6926       MovDir[x][y] = left_dir;
6927     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6928       MovDir[x][y] = right_dir;
6929
6930     if (MovDir[x][y] != old_move_dir)
6931       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6932   }
6933   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6934   {
6935     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6936       MovDir[x][y] = right_dir;
6937     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6938       MovDir[x][y] = left_dir;
6939
6940     if (MovDir[x][y] != old_move_dir)
6941       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6942   }
6943   else if (move_pattern == MV_TOWARDS_PLAYER ||
6944            move_pattern == MV_AWAY_FROM_PLAYER)
6945   {
6946     int attr_x = -1, attr_y = -1;
6947     int newx, newy;
6948     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6949
6950     if (AllPlayersGone)
6951     {
6952       attr_x = ExitX;
6953       attr_y = ExitY;
6954     }
6955     else
6956     {
6957       int i;
6958
6959       for (i = 0; i < MAX_PLAYERS; i++)
6960       {
6961         struct PlayerInfo *player = &stored_player[i];
6962         int jx = player->jx, jy = player->jy;
6963
6964         if (!player->active)
6965           continue;
6966
6967         if (attr_x == -1 ||
6968             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6969         {
6970           attr_x = jx;
6971           attr_y = jy;
6972         }
6973       }
6974     }
6975
6976     MovDir[x][y] = MV_NONE;
6977     if (attr_x < x)
6978       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6979     else if (attr_x > x)
6980       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6981     if (attr_y < y)
6982       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6983     else if (attr_y > y)
6984       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6985
6986     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6987
6988     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6989     {
6990       boolean first_horiz = RND(2);
6991       int new_move_dir = MovDir[x][y];
6992
6993       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6994       {
6995         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6996         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6997
6998         return;
6999       }
7000
7001       MovDir[x][y] =
7002         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7003       Moving2Blocked(x, y, &newx, &newy);
7004
7005       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7006         return;
7007
7008       MovDir[x][y] =
7009         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7010       Moving2Blocked(x, y, &newx, &newy);
7011
7012       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7013         return;
7014
7015       MovDir[x][y] = old_move_dir;
7016     }
7017   }
7018   else if (move_pattern == MV_WHEN_PUSHED ||
7019            move_pattern == MV_WHEN_DROPPED)
7020   {
7021     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7022       MovDir[x][y] = MV_NONE;
7023
7024     MovDelay[x][y] = 0;
7025   }
7026   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7027   {
7028     static int test_xy[7][2] =
7029     {
7030       { 0, -1 },
7031       { -1, 0 },
7032       { +1, 0 },
7033       { 0, +1 },
7034       { 0, -1 },
7035       { -1, 0 },
7036       { +1, 0 },
7037     };
7038     static int test_dir[7] =
7039     {
7040       MV_UP,
7041       MV_LEFT,
7042       MV_RIGHT,
7043       MV_DOWN,
7044       MV_UP,
7045       MV_LEFT,
7046       MV_RIGHT,
7047     };
7048     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7049     int move_preference = -1000000;     /* start with very low preference */
7050     int new_move_dir = MV_NONE;
7051     int start_test = RND(4);
7052     int i;
7053
7054     for (i = 0; i < NUM_DIRECTIONS; i++)
7055     {
7056       int move_dir = test_dir[start_test + i];
7057       int move_dir_preference;
7058
7059       xx = x + test_xy[start_test + i][0];
7060       yy = y + test_xy[start_test + i][1];
7061
7062       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7063           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7064       {
7065         new_move_dir = move_dir;
7066
7067         break;
7068       }
7069
7070       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7071         continue;
7072
7073       move_dir_preference = -1 * RunnerVisit[xx][yy];
7074       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7075         move_dir_preference = PlayerVisit[xx][yy];
7076
7077       if (move_dir_preference > move_preference)
7078       {
7079         /* prefer field that has not been visited for the longest time */
7080         move_preference = move_dir_preference;
7081         new_move_dir = move_dir;
7082       }
7083       else if (move_dir_preference == move_preference &&
7084                move_dir == old_move_dir)
7085       {
7086         /* prefer last direction when all directions are preferred equally */
7087         move_preference = move_dir_preference;
7088         new_move_dir = move_dir;
7089       }
7090     }
7091
7092     MovDir[x][y] = new_move_dir;
7093     if (old_move_dir != new_move_dir)
7094       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7095   }
7096 }
7097
7098 static void TurnRound(int x, int y)
7099 {
7100   int direction = MovDir[x][y];
7101
7102   TurnRoundExt(x, y);
7103
7104   GfxDir[x][y] = MovDir[x][y];
7105
7106   if (direction != MovDir[x][y])
7107     GfxFrame[x][y] = 0;
7108
7109   if (MovDelay[x][y])
7110     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7111
7112   ResetGfxFrame(x, y, FALSE);
7113 }
7114
7115 static boolean JustBeingPushed(int x, int y)
7116 {
7117   int i;
7118
7119   for (i = 0; i < MAX_PLAYERS; i++)
7120   {
7121     struct PlayerInfo *player = &stored_player[i];
7122
7123     if (player->active && player->is_pushing && player->MovPos)
7124     {
7125       int next_jx = player->jx + (player->jx - player->last_jx);
7126       int next_jy = player->jy + (player->jy - player->last_jy);
7127
7128       if (x == next_jx && y == next_jy)
7129         return TRUE;
7130     }
7131   }
7132
7133   return FALSE;
7134 }
7135
7136 void StartMoving(int x, int y)
7137 {
7138   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7139   int element = Feld[x][y];
7140
7141   if (Stop[x][y])
7142     return;
7143
7144   if (MovDelay[x][y] == 0)
7145     GfxAction[x][y] = ACTION_DEFAULT;
7146
7147   if (CAN_FALL(element) && y < lev_fieldy - 1)
7148   {
7149     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7150         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7151       if (JustBeingPushed(x, y))
7152         return;
7153
7154     if (element == EL_QUICKSAND_FULL)
7155     {
7156       if (IS_FREE(x, y + 1))
7157       {
7158         InitMovingField(x, y, MV_DOWN);
7159         started_moving = TRUE;
7160
7161         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7162 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7163         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7164           Store[x][y] = EL_ROCK;
7165 #else
7166         Store[x][y] = EL_ROCK;
7167 #endif
7168
7169         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7170       }
7171       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7172       {
7173         if (!MovDelay[x][y])
7174         {
7175           MovDelay[x][y] = TILEY + 1;
7176
7177           ResetGfxAnimation(x, y);
7178           ResetGfxAnimation(x, y + 1);
7179         }
7180
7181         if (MovDelay[x][y])
7182         {
7183           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7184           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7185
7186           MovDelay[x][y]--;
7187           if (MovDelay[x][y])
7188             return;
7189         }
7190
7191         Feld[x][y] = EL_QUICKSAND_EMPTY;
7192         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7193         Store[x][y + 1] = Store[x][y];
7194         Store[x][y] = 0;
7195
7196         PlayLevelSoundAction(x, y, ACTION_FILLING);
7197       }
7198       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7199       {
7200         if (!MovDelay[x][y])
7201         {
7202           MovDelay[x][y] = TILEY + 1;
7203
7204           ResetGfxAnimation(x, y);
7205           ResetGfxAnimation(x, y + 1);
7206         }
7207
7208         if (MovDelay[x][y])
7209         {
7210           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7211           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7212
7213           MovDelay[x][y]--;
7214           if (MovDelay[x][y])
7215             return;
7216         }
7217
7218         Feld[x][y] = EL_QUICKSAND_EMPTY;
7219         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7220         Store[x][y + 1] = Store[x][y];
7221         Store[x][y] = 0;
7222
7223         PlayLevelSoundAction(x, y, ACTION_FILLING);
7224       }
7225     }
7226     else if (element == EL_QUICKSAND_FAST_FULL)
7227     {
7228       if (IS_FREE(x, y + 1))
7229       {
7230         InitMovingField(x, y, MV_DOWN);
7231         started_moving = TRUE;
7232
7233         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7234 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7235         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7236           Store[x][y] = EL_ROCK;
7237 #else
7238         Store[x][y] = EL_ROCK;
7239 #endif
7240
7241         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7242       }
7243       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7244       {
7245         if (!MovDelay[x][y])
7246         {
7247           MovDelay[x][y] = TILEY + 1;
7248
7249           ResetGfxAnimation(x, y);
7250           ResetGfxAnimation(x, y + 1);
7251         }
7252
7253         if (MovDelay[x][y])
7254         {
7255           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7256           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7257
7258           MovDelay[x][y]--;
7259           if (MovDelay[x][y])
7260             return;
7261         }
7262
7263         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7264         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7265         Store[x][y + 1] = Store[x][y];
7266         Store[x][y] = 0;
7267
7268         PlayLevelSoundAction(x, y, ACTION_FILLING);
7269       }
7270       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7271       {
7272         if (!MovDelay[x][y])
7273         {
7274           MovDelay[x][y] = TILEY + 1;
7275
7276           ResetGfxAnimation(x, y);
7277           ResetGfxAnimation(x, y + 1);
7278         }
7279
7280         if (MovDelay[x][y])
7281         {
7282           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7283           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7284
7285           MovDelay[x][y]--;
7286           if (MovDelay[x][y])
7287             return;
7288         }
7289
7290         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7291         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7292         Store[x][y + 1] = Store[x][y];
7293         Store[x][y] = 0;
7294
7295         PlayLevelSoundAction(x, y, ACTION_FILLING);
7296       }
7297     }
7298     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7299              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7300     {
7301       InitMovingField(x, y, MV_DOWN);
7302       started_moving = TRUE;
7303
7304       Feld[x][y] = EL_QUICKSAND_FILLING;
7305       Store[x][y] = element;
7306
7307       PlayLevelSoundAction(x, y, ACTION_FILLING);
7308     }
7309     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7310              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7311     {
7312       InitMovingField(x, y, MV_DOWN);
7313       started_moving = TRUE;
7314
7315       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7316       Store[x][y] = element;
7317
7318       PlayLevelSoundAction(x, y, ACTION_FILLING);
7319     }
7320     else if (element == EL_MAGIC_WALL_FULL)
7321     {
7322       if (IS_FREE(x, y + 1))
7323       {
7324         InitMovingField(x, y, MV_DOWN);
7325         started_moving = TRUE;
7326
7327         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7328         Store[x][y] = EL_CHANGED(Store[x][y]);
7329       }
7330       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7331       {
7332         if (!MovDelay[x][y])
7333           MovDelay[x][y] = TILEY / 4 + 1;
7334
7335         if (MovDelay[x][y])
7336         {
7337           MovDelay[x][y]--;
7338           if (MovDelay[x][y])
7339             return;
7340         }
7341
7342         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7343         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7344         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7345         Store[x][y] = 0;
7346       }
7347     }
7348     else if (element == EL_BD_MAGIC_WALL_FULL)
7349     {
7350       if (IS_FREE(x, y + 1))
7351       {
7352         InitMovingField(x, y, MV_DOWN);
7353         started_moving = TRUE;
7354
7355         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7356         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7357       }
7358       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7359       {
7360         if (!MovDelay[x][y])
7361           MovDelay[x][y] = TILEY / 4 + 1;
7362
7363         if (MovDelay[x][y])
7364         {
7365           MovDelay[x][y]--;
7366           if (MovDelay[x][y])
7367             return;
7368         }
7369
7370         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7371         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7372         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7373         Store[x][y] = 0;
7374       }
7375     }
7376     else if (element == EL_DC_MAGIC_WALL_FULL)
7377     {
7378       if (IS_FREE(x, y + 1))
7379       {
7380         InitMovingField(x, y, MV_DOWN);
7381         started_moving = TRUE;
7382
7383         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7384         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7385       }
7386       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7387       {
7388         if (!MovDelay[x][y])
7389           MovDelay[x][y] = TILEY / 4 + 1;
7390
7391         if (MovDelay[x][y])
7392         {
7393           MovDelay[x][y]--;
7394           if (MovDelay[x][y])
7395             return;
7396         }
7397
7398         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7399         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7400         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7401         Store[x][y] = 0;
7402       }
7403     }
7404     else if ((CAN_PASS_MAGIC_WALL(element) &&
7405               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7406                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7407              (CAN_PASS_DC_MAGIC_WALL(element) &&
7408               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7409
7410     {
7411       InitMovingField(x, y, MV_DOWN);
7412       started_moving = TRUE;
7413
7414       Feld[x][y] =
7415         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7416          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7417          EL_DC_MAGIC_WALL_FILLING);
7418       Store[x][y] = element;
7419     }
7420     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7421     {
7422       SplashAcid(x, y + 1);
7423
7424       InitMovingField(x, y, MV_DOWN);
7425       started_moving = TRUE;
7426
7427       Store[x][y] = EL_ACID;
7428     }
7429     else if (
7430              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7431               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7432              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7433               CAN_FALL(element) && WasJustFalling[x][y] &&
7434               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7435
7436              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7437               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7438               (Feld[x][y + 1] == EL_BLOCKED)))
7439     {
7440       /* this is needed for a special case not covered by calling "Impact()"
7441          from "ContinueMoving()": if an element moves to a tile directly below
7442          another element which was just falling on that tile (which was empty
7443          in the previous frame), the falling element above would just stop
7444          instead of smashing the element below (in previous version, the above
7445          element was just checked for "moving" instead of "falling", resulting
7446          in incorrect smashes caused by horizontal movement of the above
7447          element; also, the case of the player being the element to smash was
7448          simply not covered here... :-/ ) */
7449
7450       CheckCollision[x][y] = 0;
7451       CheckImpact[x][y] = 0;
7452
7453       Impact(x, y);
7454     }
7455     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7456     {
7457       if (MovDir[x][y] == MV_NONE)
7458       {
7459         InitMovingField(x, y, MV_DOWN);
7460         started_moving = TRUE;
7461       }
7462     }
7463     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7464     {
7465       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7466         MovDir[x][y] = MV_DOWN;
7467
7468       InitMovingField(x, y, MV_DOWN);
7469       started_moving = TRUE;
7470     }
7471     else if (element == EL_AMOEBA_DROP)
7472     {
7473       Feld[x][y] = EL_AMOEBA_GROWING;
7474       Store[x][y] = EL_AMOEBA_WET;
7475     }
7476     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7477               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7478              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7479              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7480     {
7481       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7482                                 (IS_FREE(x - 1, y + 1) ||
7483                                  Feld[x - 1][y + 1] == EL_ACID));
7484       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7485                                 (IS_FREE(x + 1, y + 1) ||
7486                                  Feld[x + 1][y + 1] == EL_ACID));
7487       boolean can_fall_any  = (can_fall_left || can_fall_right);
7488       boolean can_fall_both = (can_fall_left && can_fall_right);
7489       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7490
7491       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7492       {
7493         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7494           can_fall_right = FALSE;
7495         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7496           can_fall_left = FALSE;
7497         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7498           can_fall_right = FALSE;
7499         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7500           can_fall_left = FALSE;
7501
7502         can_fall_any  = (can_fall_left || can_fall_right);
7503         can_fall_both = FALSE;
7504       }
7505
7506       if (can_fall_both)
7507       {
7508         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7509           can_fall_right = FALSE;       /* slip down on left side */
7510         else
7511           can_fall_left = !(can_fall_right = RND(2));
7512
7513         can_fall_both = FALSE;
7514       }
7515
7516       if (can_fall_any)
7517       {
7518         /* if not determined otherwise, prefer left side for slipping down */
7519         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7520         started_moving = TRUE;
7521       }
7522     }
7523     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7524     {
7525       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7526       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7527       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7528       int belt_dir = game.belt_dir[belt_nr];
7529
7530       if ((belt_dir == MV_LEFT  && left_is_free) ||
7531           (belt_dir == MV_RIGHT && right_is_free))
7532       {
7533         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7534
7535         InitMovingField(x, y, belt_dir);
7536         started_moving = TRUE;
7537
7538         Pushed[x][y] = TRUE;
7539         Pushed[nextx][y] = TRUE;
7540
7541         GfxAction[x][y] = ACTION_DEFAULT;
7542       }
7543       else
7544       {
7545         MovDir[x][y] = 0;       /* if element was moving, stop it */
7546       }
7547     }
7548   }
7549
7550   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7551   if (CAN_MOVE(element) && !started_moving)
7552   {
7553     int move_pattern = element_info[element].move_pattern;
7554     int newx, newy;
7555
7556     Moving2Blocked(x, y, &newx, &newy);
7557
7558     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7559       return;
7560
7561     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7562         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7563     {
7564       WasJustMoving[x][y] = 0;
7565       CheckCollision[x][y] = 0;
7566
7567       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7568
7569       if (Feld[x][y] != element)        /* element has changed */
7570         return;
7571     }
7572
7573     if (!MovDelay[x][y])        /* start new movement phase */
7574     {
7575       /* all objects that can change their move direction after each step
7576          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7577
7578       if (element != EL_YAMYAM &&
7579           element != EL_DARK_YAMYAM &&
7580           element != EL_PACMAN &&
7581           !(move_pattern & MV_ANY_DIRECTION) &&
7582           move_pattern != MV_TURNING_LEFT &&
7583           move_pattern != MV_TURNING_RIGHT &&
7584           move_pattern != MV_TURNING_LEFT_RIGHT &&
7585           move_pattern != MV_TURNING_RIGHT_LEFT &&
7586           move_pattern != MV_TURNING_RANDOM)
7587       {
7588         TurnRound(x, y);
7589
7590         if (MovDelay[x][y] && (element == EL_BUG ||
7591                                element == EL_SPACESHIP ||
7592                                element == EL_SP_SNIKSNAK ||
7593                                element == EL_SP_ELECTRON ||
7594                                element == EL_MOLE))
7595           TEST_DrawLevelField(x, y);
7596       }
7597     }
7598
7599     if (MovDelay[x][y])         /* wait some time before next movement */
7600     {
7601       MovDelay[x][y]--;
7602
7603       if (element == EL_ROBOT ||
7604           element == EL_YAMYAM ||
7605           element == EL_DARK_YAMYAM)
7606       {
7607         DrawLevelElementAnimationIfNeeded(x, y, element);
7608         PlayLevelSoundAction(x, y, ACTION_WAITING);
7609       }
7610       else if (element == EL_SP_ELECTRON)
7611         DrawLevelElementAnimationIfNeeded(x, y, element);
7612       else if (element == EL_DRAGON)
7613       {
7614         int i;
7615         int dir = MovDir[x][y];
7616         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7617         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7618         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7619                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7620                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7621                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7622         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7623
7624         GfxAction[x][y] = ACTION_ATTACKING;
7625
7626         if (IS_PLAYER(x, y))
7627           DrawPlayerField(x, y);
7628         else
7629           TEST_DrawLevelField(x, y);
7630
7631         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7632
7633         for (i = 1; i <= 3; i++)
7634         {
7635           int xx = x + i * dx;
7636           int yy = y + i * dy;
7637           int sx = SCREENX(xx);
7638           int sy = SCREENY(yy);
7639           int flame_graphic = graphic + (i - 1);
7640
7641           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7642             break;
7643
7644           if (MovDelay[x][y])
7645           {
7646             int flamed = MovingOrBlocked2Element(xx, yy);
7647
7648             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7649               Bang(xx, yy);
7650             else
7651               RemoveMovingField(xx, yy);
7652
7653             ChangeDelay[xx][yy] = 0;
7654
7655             Feld[xx][yy] = EL_FLAMES;
7656
7657             if (IN_SCR_FIELD(sx, sy))
7658             {
7659               TEST_DrawLevelFieldCrumbled(xx, yy);
7660               DrawGraphic(sx, sy, flame_graphic, frame);
7661             }
7662           }
7663           else
7664           {
7665             if (Feld[xx][yy] == EL_FLAMES)
7666               Feld[xx][yy] = EL_EMPTY;
7667             TEST_DrawLevelField(xx, yy);
7668           }
7669         }
7670       }
7671
7672       if (MovDelay[x][y])       /* element still has to wait some time */
7673       {
7674         PlayLevelSoundAction(x, y, ACTION_WAITING);
7675
7676         return;
7677       }
7678     }
7679
7680     /* now make next step */
7681
7682     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7683
7684     if (DONT_COLLIDE_WITH(element) &&
7685         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7686         !PLAYER_ENEMY_PROTECTED(newx, newy))
7687     {
7688       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7689
7690       return;
7691     }
7692
7693     else if (CAN_MOVE_INTO_ACID(element) &&
7694              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7695              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7696              (MovDir[x][y] == MV_DOWN ||
7697               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7698     {
7699       SplashAcid(newx, newy);
7700       Store[x][y] = EL_ACID;
7701     }
7702     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7703     {
7704       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7705           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7706           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7707           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7708       {
7709         RemoveField(x, y);
7710         TEST_DrawLevelField(x, y);
7711
7712         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7713         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7714           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7715
7716         local_player->friends_still_needed--;
7717         if (!local_player->friends_still_needed &&
7718             !local_player->GameOver && AllPlayersGone)
7719           PlayerWins(local_player);
7720
7721         return;
7722       }
7723       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7724       {
7725         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7726           TEST_DrawLevelField(newx, newy);
7727         else
7728           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7729       }
7730       else if (!IS_FREE(newx, newy))
7731       {
7732         GfxAction[x][y] = ACTION_WAITING;
7733
7734         if (IS_PLAYER(x, y))
7735           DrawPlayerField(x, y);
7736         else
7737           TEST_DrawLevelField(x, y);
7738
7739         return;
7740       }
7741     }
7742     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7743     {
7744       if (IS_FOOD_PIG(Feld[newx][newy]))
7745       {
7746         if (IS_MOVING(newx, newy))
7747           RemoveMovingField(newx, newy);
7748         else
7749         {
7750           Feld[newx][newy] = EL_EMPTY;
7751           TEST_DrawLevelField(newx, newy);
7752         }
7753
7754         PlayLevelSound(x, y, SND_PIG_DIGGING);
7755       }
7756       else if (!IS_FREE(newx, newy))
7757       {
7758         if (IS_PLAYER(x, y))
7759           DrawPlayerField(x, y);
7760         else
7761           TEST_DrawLevelField(x, y);
7762
7763         return;
7764       }
7765     }
7766     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7767     {
7768       if (Store[x][y] != EL_EMPTY)
7769       {
7770         boolean can_clone = FALSE;
7771         int xx, yy;
7772
7773         /* check if element to clone is still there */
7774         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7775         {
7776           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7777           {
7778             can_clone = TRUE;
7779
7780             break;
7781           }
7782         }
7783
7784         /* cannot clone or target field not free anymore -- do not clone */
7785         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7786           Store[x][y] = EL_EMPTY;
7787       }
7788
7789       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7790       {
7791         if (IS_MV_DIAGONAL(MovDir[x][y]))
7792         {
7793           int diagonal_move_dir = MovDir[x][y];
7794           int stored = Store[x][y];
7795           int change_delay = 8;
7796           int graphic;
7797
7798           /* android is moving diagonally */
7799
7800           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7801
7802           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7803           GfxElement[x][y] = EL_EMC_ANDROID;
7804           GfxAction[x][y] = ACTION_SHRINKING;
7805           GfxDir[x][y] = diagonal_move_dir;
7806           ChangeDelay[x][y] = change_delay;
7807
7808           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7809                                    GfxDir[x][y]);
7810
7811           DrawLevelGraphicAnimation(x, y, graphic);
7812           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7813
7814           if (Feld[newx][newy] == EL_ACID)
7815           {
7816             SplashAcid(newx, newy);
7817
7818             return;
7819           }
7820
7821           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7822
7823           Store[newx][newy] = EL_EMC_ANDROID;
7824           GfxElement[newx][newy] = EL_EMC_ANDROID;
7825           GfxAction[newx][newy] = ACTION_GROWING;
7826           GfxDir[newx][newy] = diagonal_move_dir;
7827           ChangeDelay[newx][newy] = change_delay;
7828
7829           graphic = el_act_dir2img(GfxElement[newx][newy],
7830                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7831
7832           DrawLevelGraphicAnimation(newx, newy, graphic);
7833           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7834
7835           return;
7836         }
7837         else
7838         {
7839           Feld[newx][newy] = EL_EMPTY;
7840           TEST_DrawLevelField(newx, newy);
7841
7842           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7843         }
7844       }
7845       else if (!IS_FREE(newx, newy))
7846       {
7847         return;
7848       }
7849     }
7850     else if (IS_CUSTOM_ELEMENT(element) &&
7851              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7852     {
7853       if (!DigFieldByCE(newx, newy, element))
7854         return;
7855
7856       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7857       {
7858         RunnerVisit[x][y] = FrameCounter;
7859         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7860       }
7861     }
7862     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7863     {
7864       if (!IS_FREE(newx, newy))
7865       {
7866         if (IS_PLAYER(x, y))
7867           DrawPlayerField(x, y);
7868         else
7869           TEST_DrawLevelField(x, y);
7870
7871         return;
7872       }
7873       else
7874       {
7875         boolean wanna_flame = !RND(10);
7876         int dx = newx - x, dy = newy - y;
7877         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7878         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7879         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7880                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7881         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7882                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7883
7884         if ((wanna_flame ||
7885              IS_CLASSIC_ENEMY(element1) ||
7886              IS_CLASSIC_ENEMY(element2)) &&
7887             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7888             element1 != EL_FLAMES && element2 != EL_FLAMES)
7889         {
7890           ResetGfxAnimation(x, y);
7891           GfxAction[x][y] = ACTION_ATTACKING;
7892
7893           if (IS_PLAYER(x, y))
7894             DrawPlayerField(x, y);
7895           else
7896             TEST_DrawLevelField(x, y);
7897
7898           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7899
7900           MovDelay[x][y] = 50;
7901
7902           Feld[newx][newy] = EL_FLAMES;
7903           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7904             Feld[newx1][newy1] = EL_FLAMES;
7905           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7906             Feld[newx2][newy2] = EL_FLAMES;
7907
7908           return;
7909         }
7910       }
7911     }
7912     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7913              Feld[newx][newy] == EL_DIAMOND)
7914     {
7915       if (IS_MOVING(newx, newy))
7916         RemoveMovingField(newx, newy);
7917       else
7918       {
7919         Feld[newx][newy] = EL_EMPTY;
7920         TEST_DrawLevelField(newx, newy);
7921       }
7922
7923       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7924     }
7925     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7926              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7927     {
7928       if (AmoebaNr[newx][newy])
7929       {
7930         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7931         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7932             Feld[newx][newy] == EL_BD_AMOEBA)
7933           AmoebaCnt[AmoebaNr[newx][newy]]--;
7934       }
7935
7936       if (IS_MOVING(newx, newy))
7937       {
7938         RemoveMovingField(newx, newy);
7939       }
7940       else
7941       {
7942         Feld[newx][newy] = EL_EMPTY;
7943         TEST_DrawLevelField(newx, newy);
7944       }
7945
7946       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7947     }
7948     else if ((element == EL_PACMAN || element == EL_MOLE)
7949              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7950     {
7951       if (AmoebaNr[newx][newy])
7952       {
7953         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7954         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7955             Feld[newx][newy] == EL_BD_AMOEBA)
7956           AmoebaCnt[AmoebaNr[newx][newy]]--;
7957       }
7958
7959       if (element == EL_MOLE)
7960       {
7961         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7962         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7963
7964         ResetGfxAnimation(x, y);
7965         GfxAction[x][y] = ACTION_DIGGING;
7966         TEST_DrawLevelField(x, y);
7967
7968         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7969
7970         return;                         /* wait for shrinking amoeba */
7971       }
7972       else      /* element == EL_PACMAN */
7973       {
7974         Feld[newx][newy] = EL_EMPTY;
7975         TEST_DrawLevelField(newx, newy);
7976         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7977       }
7978     }
7979     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7980              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7981               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7982     {
7983       /* wait for shrinking amoeba to completely disappear */
7984       return;
7985     }
7986     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7987     {
7988       /* object was running against a wall */
7989
7990       TurnRound(x, y);
7991
7992       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7993         DrawLevelElementAnimation(x, y, element);
7994
7995       if (DONT_TOUCH(element))
7996         TestIfBadThingTouchesPlayer(x, y);
7997
7998       return;
7999     }
8000
8001     InitMovingField(x, y, MovDir[x][y]);
8002
8003     PlayLevelSoundAction(x, y, ACTION_MOVING);
8004   }
8005
8006   if (MovDir[x][y])
8007     ContinueMoving(x, y);
8008 }
8009
8010 void ContinueMoving(int x, int y)
8011 {
8012   int element = Feld[x][y];
8013   struct ElementInfo *ei = &element_info[element];
8014   int direction = MovDir[x][y];
8015   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8016   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8017   int newx = x + dx, newy = y + dy;
8018   int stored = Store[x][y];
8019   int stored_new = Store[newx][newy];
8020   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8021   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8022   boolean last_line = (newy == lev_fieldy - 1);
8023
8024   MovPos[x][y] += getElementMoveStepsize(x, y);
8025
8026   if (pushed_by_player) /* special case: moving object pushed by player */
8027     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8028
8029   if (ABS(MovPos[x][y]) < TILEX)
8030   {
8031     TEST_DrawLevelField(x, y);
8032
8033     return;     /* element is still moving */
8034   }
8035
8036   /* element reached destination field */
8037
8038   Feld[x][y] = EL_EMPTY;
8039   Feld[newx][newy] = element;
8040   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8041
8042   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8043   {
8044     element = Feld[newx][newy] = EL_ACID;
8045   }
8046   else if (element == EL_MOLE)
8047   {
8048     Feld[x][y] = EL_SAND;
8049
8050     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8051   }
8052   else if (element == EL_QUICKSAND_FILLING)
8053   {
8054     element = Feld[newx][newy] = get_next_element(element);
8055     Store[newx][newy] = Store[x][y];
8056   }
8057   else if (element == EL_QUICKSAND_EMPTYING)
8058   {
8059     Feld[x][y] = get_next_element(element);
8060     element = Feld[newx][newy] = Store[x][y];
8061   }
8062   else if (element == EL_QUICKSAND_FAST_FILLING)
8063   {
8064     element = Feld[newx][newy] = get_next_element(element);
8065     Store[newx][newy] = Store[x][y];
8066   }
8067   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8068   {
8069     Feld[x][y] = get_next_element(element);
8070     element = Feld[newx][newy] = Store[x][y];
8071   }
8072   else if (element == EL_MAGIC_WALL_FILLING)
8073   {
8074     element = Feld[newx][newy] = get_next_element(element);
8075     if (!game.magic_wall_active)
8076       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8077     Store[newx][newy] = Store[x][y];
8078   }
8079   else if (element == EL_MAGIC_WALL_EMPTYING)
8080   {
8081     Feld[x][y] = get_next_element(element);
8082     if (!game.magic_wall_active)
8083       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8084     element = Feld[newx][newy] = Store[x][y];
8085
8086     InitField(newx, newy, FALSE);
8087   }
8088   else if (element == EL_BD_MAGIC_WALL_FILLING)
8089   {
8090     element = Feld[newx][newy] = get_next_element(element);
8091     if (!game.magic_wall_active)
8092       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8093     Store[newx][newy] = Store[x][y];
8094   }
8095   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8096   {
8097     Feld[x][y] = get_next_element(element);
8098     if (!game.magic_wall_active)
8099       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8100     element = Feld[newx][newy] = Store[x][y];
8101
8102     InitField(newx, newy, FALSE);
8103   }
8104   else if (element == EL_DC_MAGIC_WALL_FILLING)
8105   {
8106     element = Feld[newx][newy] = get_next_element(element);
8107     if (!game.magic_wall_active)
8108       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8109     Store[newx][newy] = Store[x][y];
8110   }
8111   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8112   {
8113     Feld[x][y] = get_next_element(element);
8114     if (!game.magic_wall_active)
8115       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8116     element = Feld[newx][newy] = Store[x][y];
8117
8118     InitField(newx, newy, FALSE);
8119   }
8120   else if (element == EL_AMOEBA_DROPPING)
8121   {
8122     Feld[x][y] = get_next_element(element);
8123     element = Feld[newx][newy] = Store[x][y];
8124   }
8125   else if (element == EL_SOKOBAN_OBJECT)
8126   {
8127     if (Back[x][y])
8128       Feld[x][y] = Back[x][y];
8129
8130     if (Back[newx][newy])
8131       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8132
8133     Back[x][y] = Back[newx][newy] = 0;
8134   }
8135
8136   Store[x][y] = EL_EMPTY;
8137   MovPos[x][y] = 0;
8138   MovDir[x][y] = 0;
8139   MovDelay[x][y] = 0;
8140
8141   MovDelay[newx][newy] = 0;
8142
8143   if (CAN_CHANGE_OR_HAS_ACTION(element))
8144   {
8145     /* copy element change control values to new field */
8146     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8147     ChangePage[newx][newy]  = ChangePage[x][y];
8148     ChangeCount[newx][newy] = ChangeCount[x][y];
8149     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8150   }
8151
8152   CustomValue[newx][newy] = CustomValue[x][y];
8153
8154   ChangeDelay[x][y] = 0;
8155   ChangePage[x][y] = -1;
8156   ChangeCount[x][y] = 0;
8157   ChangeEvent[x][y] = -1;
8158
8159   CustomValue[x][y] = 0;
8160
8161   /* copy animation control values to new field */
8162   GfxFrame[newx][newy]  = GfxFrame[x][y];
8163   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8164   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8165   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8166
8167   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8168
8169   /* some elements can leave other elements behind after moving */
8170   if (ei->move_leave_element != EL_EMPTY &&
8171       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8172       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8173   {
8174     int move_leave_element = ei->move_leave_element;
8175
8176     /* this makes it possible to leave the removed element again */
8177     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8178       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8179
8180     Feld[x][y] = move_leave_element;
8181
8182     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8183       MovDir[x][y] = direction;
8184
8185     InitField(x, y, FALSE);
8186
8187     if (GFX_CRUMBLED(Feld[x][y]))
8188       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8189
8190     if (ELEM_IS_PLAYER(move_leave_element))
8191       RelocatePlayer(x, y, move_leave_element);
8192   }
8193
8194   /* do this after checking for left-behind element */
8195   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8196
8197   if (!CAN_MOVE(element) ||
8198       (CAN_FALL(element) && direction == MV_DOWN &&
8199        (element == EL_SPRING ||
8200         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8201         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8202     GfxDir[x][y] = MovDir[newx][newy] = 0;
8203
8204   TEST_DrawLevelField(x, y);
8205   TEST_DrawLevelField(newx, newy);
8206
8207   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8208
8209   /* prevent pushed element from moving on in pushed direction */
8210   if (pushed_by_player && CAN_MOVE(element) &&
8211       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8212       !(element_info[element].move_pattern & direction))
8213     TurnRound(newx, newy);
8214
8215   /* prevent elements on conveyor belt from moving on in last direction */
8216   if (pushed_by_conveyor && CAN_FALL(element) &&
8217       direction & MV_HORIZONTAL)
8218     MovDir[newx][newy] = 0;
8219
8220   if (!pushed_by_player)
8221   {
8222     int nextx = newx + dx, nexty = newy + dy;
8223     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8224
8225     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8226
8227     if (CAN_FALL(element) && direction == MV_DOWN)
8228       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8229
8230     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8231       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8232
8233     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8234       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8235   }
8236
8237   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8238   {
8239     TestIfBadThingTouchesPlayer(newx, newy);
8240     TestIfBadThingTouchesFriend(newx, newy);
8241
8242     if (!IS_CUSTOM_ELEMENT(element))
8243       TestIfBadThingTouchesOtherBadThing(newx, newy);
8244   }
8245   else if (element == EL_PENGUIN)
8246     TestIfFriendTouchesBadThing(newx, newy);
8247
8248   if (DONT_GET_HIT_BY(element))
8249   {
8250     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8251   }
8252
8253   /* give the player one last chance (one more frame) to move away */
8254   if (CAN_FALL(element) && direction == MV_DOWN &&
8255       (last_line || (!IS_FREE(x, newy + 1) &&
8256                      (!IS_PLAYER(x, newy + 1) ||
8257                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8258     Impact(x, newy);
8259
8260   if (pushed_by_player && !game.use_change_when_pushing_bug)
8261   {
8262     int push_side = MV_DIR_OPPOSITE(direction);
8263     struct PlayerInfo *player = PLAYERINFO(x, y);
8264
8265     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8266                                player->index_bit, push_side);
8267     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8268                                         player->index_bit, push_side);
8269   }
8270
8271   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8272     MovDelay[newx][newy] = 1;
8273
8274   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8275
8276   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8277   TestIfElementHitsCustomElement(newx, newy, direction);
8278   TestIfPlayerTouchesCustomElement(newx, newy);
8279   TestIfElementTouchesCustomElement(newx, newy);
8280
8281   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8282       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8283     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8284                              MV_DIR_OPPOSITE(direction));
8285 }
8286
8287 int AmoebeNachbarNr(int ax, int ay)
8288 {
8289   int i;
8290   int element = Feld[ax][ay];
8291   int group_nr = 0;
8292   static int xy[4][2] =
8293   {
8294     { 0, -1 },
8295     { -1, 0 },
8296     { +1, 0 },
8297     { 0, +1 }
8298   };
8299
8300   for (i = 0; i < NUM_DIRECTIONS; i++)
8301   {
8302     int x = ax + xy[i][0];
8303     int y = ay + xy[i][1];
8304
8305     if (!IN_LEV_FIELD(x, y))
8306       continue;
8307
8308     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8309       group_nr = AmoebaNr[x][y];
8310   }
8311
8312   return group_nr;
8313 }
8314
8315 void AmoebenVereinigen(int ax, int ay)
8316 {
8317   int i, x, y, xx, yy;
8318   int new_group_nr = AmoebaNr[ax][ay];
8319   static int xy[4][2] =
8320   {
8321     { 0, -1 },
8322     { -1, 0 },
8323     { +1, 0 },
8324     { 0, +1 }
8325   };
8326
8327   if (new_group_nr == 0)
8328     return;
8329
8330   for (i = 0; i < NUM_DIRECTIONS; i++)
8331   {
8332     x = ax + xy[i][0];
8333     y = ay + xy[i][1];
8334
8335     if (!IN_LEV_FIELD(x, y))
8336       continue;
8337
8338     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8339          Feld[x][y] == EL_BD_AMOEBA ||
8340          Feld[x][y] == EL_AMOEBA_DEAD) &&
8341         AmoebaNr[x][y] != new_group_nr)
8342     {
8343       int old_group_nr = AmoebaNr[x][y];
8344
8345       if (old_group_nr == 0)
8346         return;
8347
8348       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8349       AmoebaCnt[old_group_nr] = 0;
8350       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8351       AmoebaCnt2[old_group_nr] = 0;
8352
8353       SCAN_PLAYFIELD(xx, yy)
8354       {
8355         if (AmoebaNr[xx][yy] == old_group_nr)
8356           AmoebaNr[xx][yy] = new_group_nr;
8357       }
8358     }
8359   }
8360 }
8361
8362 void AmoebeUmwandeln(int ax, int ay)
8363 {
8364   int i, x, y;
8365
8366   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8367   {
8368     int group_nr = AmoebaNr[ax][ay];
8369
8370 #ifdef DEBUG
8371     if (group_nr == 0)
8372     {
8373       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8374       printf("AmoebeUmwandeln(): This should never happen!\n");
8375       return;
8376     }
8377 #endif
8378
8379     SCAN_PLAYFIELD(x, y)
8380     {
8381       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8382       {
8383         AmoebaNr[x][y] = 0;
8384         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8385       }
8386     }
8387
8388     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8389                             SND_AMOEBA_TURNING_TO_GEM :
8390                             SND_AMOEBA_TURNING_TO_ROCK));
8391     Bang(ax, ay);
8392   }
8393   else
8394   {
8395     static int xy[4][2] =
8396     {
8397       { 0, -1 },
8398       { -1, 0 },
8399       { +1, 0 },
8400       { 0, +1 }
8401     };
8402
8403     for (i = 0; i < NUM_DIRECTIONS; i++)
8404     {
8405       x = ax + xy[i][0];
8406       y = ay + xy[i][1];
8407
8408       if (!IN_LEV_FIELD(x, y))
8409         continue;
8410
8411       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8412       {
8413         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8414                               SND_AMOEBA_TURNING_TO_GEM :
8415                               SND_AMOEBA_TURNING_TO_ROCK));
8416         Bang(x, y);
8417       }
8418     }
8419   }
8420 }
8421
8422 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8423 {
8424   int x, y;
8425   int group_nr = AmoebaNr[ax][ay];
8426   boolean done = FALSE;
8427
8428 #ifdef DEBUG
8429   if (group_nr == 0)
8430   {
8431     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8432     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8433     return;
8434   }
8435 #endif
8436
8437   SCAN_PLAYFIELD(x, y)
8438   {
8439     if (AmoebaNr[x][y] == group_nr &&
8440         (Feld[x][y] == EL_AMOEBA_DEAD ||
8441          Feld[x][y] == EL_BD_AMOEBA ||
8442          Feld[x][y] == EL_AMOEBA_GROWING))
8443     {
8444       AmoebaNr[x][y] = 0;
8445       Feld[x][y] = new_element;
8446       InitField(x, y, FALSE);
8447       TEST_DrawLevelField(x, y);
8448       done = TRUE;
8449     }
8450   }
8451
8452   if (done)
8453     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8454                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8455                             SND_BD_AMOEBA_TURNING_TO_GEM));
8456 }
8457
8458 void AmoebeWaechst(int x, int y)
8459 {
8460   static unsigned int sound_delay = 0;
8461   static unsigned int sound_delay_value = 0;
8462
8463   if (!MovDelay[x][y])          /* start new growing cycle */
8464   {
8465     MovDelay[x][y] = 7;
8466
8467     if (DelayReached(&sound_delay, sound_delay_value))
8468     {
8469       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8470       sound_delay_value = 30;
8471     }
8472   }
8473
8474   if (MovDelay[x][y])           /* wait some time before growing bigger */
8475   {
8476     MovDelay[x][y]--;
8477     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8478     {
8479       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8480                                            6 - MovDelay[x][y]);
8481
8482       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8483     }
8484
8485     if (!MovDelay[x][y])
8486     {
8487       Feld[x][y] = Store[x][y];
8488       Store[x][y] = 0;
8489       TEST_DrawLevelField(x, y);
8490     }
8491   }
8492 }
8493
8494 void AmoebaDisappearing(int x, int y)
8495 {
8496   static unsigned int sound_delay = 0;
8497   static unsigned int sound_delay_value = 0;
8498
8499   if (!MovDelay[x][y])          /* start new shrinking cycle */
8500   {
8501     MovDelay[x][y] = 7;
8502
8503     if (DelayReached(&sound_delay, sound_delay_value))
8504       sound_delay_value = 30;
8505   }
8506
8507   if (MovDelay[x][y])           /* wait some time before shrinking */
8508   {
8509     MovDelay[x][y]--;
8510     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8511     {
8512       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8513                                            6 - MovDelay[x][y]);
8514
8515       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8516     }
8517
8518     if (!MovDelay[x][y])
8519     {
8520       Feld[x][y] = EL_EMPTY;
8521       TEST_DrawLevelField(x, y);
8522
8523       /* don't let mole enter this field in this cycle;
8524          (give priority to objects falling to this field from above) */
8525       Stop[x][y] = TRUE;
8526     }
8527   }
8528 }
8529
8530 void AmoebeAbleger(int ax, int ay)
8531 {
8532   int i;
8533   int element = Feld[ax][ay];
8534   int graphic = el2img(element);
8535   int newax = ax, neway = ay;
8536   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8537   static int xy[4][2] =
8538   {
8539     { 0, -1 },
8540     { -1, 0 },
8541     { +1, 0 },
8542     { 0, +1 }
8543   };
8544
8545   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8546   {
8547     Feld[ax][ay] = EL_AMOEBA_DEAD;
8548     TEST_DrawLevelField(ax, ay);
8549     return;
8550   }
8551
8552   if (IS_ANIMATED(graphic))
8553     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8554
8555   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8556     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8557
8558   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8559   {
8560     MovDelay[ax][ay]--;
8561     if (MovDelay[ax][ay])
8562       return;
8563   }
8564
8565   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8566   {
8567     int start = RND(4);
8568     int x = ax + xy[start][0];
8569     int y = ay + xy[start][1];
8570
8571     if (!IN_LEV_FIELD(x, y))
8572       return;
8573
8574     if (IS_FREE(x, y) ||
8575         CAN_GROW_INTO(Feld[x][y]) ||
8576         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8577         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8578     {
8579       newax = x;
8580       neway = y;
8581     }
8582
8583     if (newax == ax && neway == ay)
8584       return;
8585   }
8586   else                          /* normal or "filled" (BD style) amoeba */
8587   {
8588     int start = RND(4);
8589     boolean waiting_for_player = FALSE;
8590
8591     for (i = 0; i < NUM_DIRECTIONS; i++)
8592     {
8593       int j = (start + i) % 4;
8594       int x = ax + xy[j][0];
8595       int y = ay + xy[j][1];
8596
8597       if (!IN_LEV_FIELD(x, y))
8598         continue;
8599
8600       if (IS_FREE(x, y) ||
8601           CAN_GROW_INTO(Feld[x][y]) ||
8602           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8603           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8604       {
8605         newax = x;
8606         neway = y;
8607         break;
8608       }
8609       else if (IS_PLAYER(x, y))
8610         waiting_for_player = TRUE;
8611     }
8612
8613     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8614     {
8615       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8616       {
8617         Feld[ax][ay] = EL_AMOEBA_DEAD;
8618         TEST_DrawLevelField(ax, ay);
8619         AmoebaCnt[AmoebaNr[ax][ay]]--;
8620
8621         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8622         {
8623           if (element == EL_AMOEBA_FULL)
8624             AmoebeUmwandeln(ax, ay);
8625           else if (element == EL_BD_AMOEBA)
8626             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8627         }
8628       }
8629       return;
8630     }
8631     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8632     {
8633       /* amoeba gets larger by growing in some direction */
8634
8635       int new_group_nr = AmoebaNr[ax][ay];
8636
8637 #ifdef DEBUG
8638   if (new_group_nr == 0)
8639   {
8640     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8641     printf("AmoebeAbleger(): This should never happen!\n");
8642     return;
8643   }
8644 #endif
8645
8646       AmoebaNr[newax][neway] = new_group_nr;
8647       AmoebaCnt[new_group_nr]++;
8648       AmoebaCnt2[new_group_nr]++;
8649
8650       /* if amoeba touches other amoeba(s) after growing, unify them */
8651       AmoebenVereinigen(newax, neway);
8652
8653       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8654       {
8655         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8656         return;
8657       }
8658     }
8659   }
8660
8661   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8662       (neway == lev_fieldy - 1 && newax != ax))
8663   {
8664     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8665     Store[newax][neway] = element;
8666   }
8667   else if (neway == ay || element == EL_EMC_DRIPPER)
8668   {
8669     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8670
8671     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8672   }
8673   else
8674   {
8675     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8676     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8677     Store[ax][ay] = EL_AMOEBA_DROP;
8678     ContinueMoving(ax, ay);
8679     return;
8680   }
8681
8682   TEST_DrawLevelField(newax, neway);
8683 }
8684
8685 void Life(int ax, int ay)
8686 {
8687   int x1, y1, x2, y2;
8688   int life_time = 40;
8689   int element = Feld[ax][ay];
8690   int graphic = el2img(element);
8691   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8692                          level.biomaze);
8693   boolean changed = FALSE;
8694
8695   if (IS_ANIMATED(graphic))
8696     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8697
8698   if (Stop[ax][ay])
8699     return;
8700
8701   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8702     MovDelay[ax][ay] = life_time;
8703
8704   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8705   {
8706     MovDelay[ax][ay]--;
8707     if (MovDelay[ax][ay])
8708       return;
8709   }
8710
8711   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8712   {
8713     int xx = ax+x1, yy = ay+y1;
8714     int nachbarn = 0;
8715
8716     if (!IN_LEV_FIELD(xx, yy))
8717       continue;
8718
8719     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8720     {
8721       int x = xx+x2, y = yy+y2;
8722
8723       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8724         continue;
8725
8726       if (((Feld[x][y] == element ||
8727             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8728            !Stop[x][y]) ||
8729           (IS_FREE(x, y) && Stop[x][y]))
8730         nachbarn++;
8731     }
8732
8733     if (xx == ax && yy == ay)           /* field in the middle */
8734     {
8735       if (nachbarn < life_parameter[0] ||
8736           nachbarn > life_parameter[1])
8737       {
8738         Feld[xx][yy] = EL_EMPTY;
8739         if (!Stop[xx][yy])
8740           TEST_DrawLevelField(xx, yy);
8741         Stop[xx][yy] = TRUE;
8742         changed = TRUE;
8743       }
8744     }
8745     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8746     {                                   /* free border field */
8747       if (nachbarn >= life_parameter[2] &&
8748           nachbarn <= life_parameter[3])
8749       {
8750         Feld[xx][yy] = element;
8751         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8752         if (!Stop[xx][yy])
8753           TEST_DrawLevelField(xx, yy);
8754         Stop[xx][yy] = TRUE;
8755         changed = TRUE;
8756       }
8757     }
8758   }
8759
8760   if (changed)
8761     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8762                    SND_GAME_OF_LIFE_GROWING);
8763 }
8764
8765 static void InitRobotWheel(int x, int y)
8766 {
8767   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8768 }
8769
8770 static void RunRobotWheel(int x, int y)
8771 {
8772   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8773 }
8774
8775 static void StopRobotWheel(int x, int y)
8776 {
8777   if (ZX == x && ZY == y)
8778   {
8779     ZX = ZY = -1;
8780
8781     game.robot_wheel_active = FALSE;
8782   }
8783 }
8784
8785 static void InitTimegateWheel(int x, int y)
8786 {
8787   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8788 }
8789
8790 static void RunTimegateWheel(int x, int y)
8791 {
8792   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8793 }
8794
8795 static void InitMagicBallDelay(int x, int y)
8796 {
8797   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8798 }
8799
8800 static void ActivateMagicBall(int bx, int by)
8801 {
8802   int x, y;
8803
8804   if (level.ball_random)
8805   {
8806     int pos_border = RND(8);    /* select one of the eight border elements */
8807     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8808     int xx = pos_content % 3;
8809     int yy = pos_content / 3;
8810
8811     x = bx - 1 + xx;
8812     y = by - 1 + yy;
8813
8814     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8815       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8816   }
8817   else
8818   {
8819     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8820     {
8821       int xx = x - bx + 1;
8822       int yy = y - by + 1;
8823
8824       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8825         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8826     }
8827   }
8828
8829   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8830 }
8831
8832 void CheckExit(int x, int y)
8833 {
8834   if (local_player->gems_still_needed > 0 ||
8835       local_player->sokobanfields_still_needed > 0 ||
8836       local_player->lights_still_needed > 0)
8837   {
8838     int element = Feld[x][y];
8839     int graphic = el2img(element);
8840
8841     if (IS_ANIMATED(graphic))
8842       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8843
8844     return;
8845   }
8846
8847   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8848     return;
8849
8850   Feld[x][y] = EL_EXIT_OPENING;
8851
8852   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8853 }
8854
8855 void CheckExitEM(int x, int y)
8856 {
8857   if (local_player->gems_still_needed > 0 ||
8858       local_player->sokobanfields_still_needed > 0 ||
8859       local_player->lights_still_needed > 0)
8860   {
8861     int element = Feld[x][y];
8862     int graphic = el2img(element);
8863
8864     if (IS_ANIMATED(graphic))
8865       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8866
8867     return;
8868   }
8869
8870   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8871     return;
8872
8873   Feld[x][y] = EL_EM_EXIT_OPENING;
8874
8875   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8876 }
8877
8878 void CheckExitSteel(int x, int y)
8879 {
8880   if (local_player->gems_still_needed > 0 ||
8881       local_player->sokobanfields_still_needed > 0 ||
8882       local_player->lights_still_needed > 0)
8883   {
8884     int element = Feld[x][y];
8885     int graphic = el2img(element);
8886
8887     if (IS_ANIMATED(graphic))
8888       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8889
8890     return;
8891   }
8892
8893   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8894     return;
8895
8896   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8897
8898   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8899 }
8900
8901 void CheckExitSteelEM(int x, int y)
8902 {
8903   if (local_player->gems_still_needed > 0 ||
8904       local_player->sokobanfields_still_needed > 0 ||
8905       local_player->lights_still_needed > 0)
8906   {
8907     int element = Feld[x][y];
8908     int graphic = el2img(element);
8909
8910     if (IS_ANIMATED(graphic))
8911       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8912
8913     return;
8914   }
8915
8916   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8917     return;
8918
8919   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8920
8921   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8922 }
8923
8924 void CheckExitSP(int x, int y)
8925 {
8926   if (local_player->gems_still_needed > 0)
8927   {
8928     int element = Feld[x][y];
8929     int graphic = el2img(element);
8930
8931     if (IS_ANIMATED(graphic))
8932       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8933
8934     return;
8935   }
8936
8937   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8938     return;
8939
8940   Feld[x][y] = EL_SP_EXIT_OPENING;
8941
8942   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8943 }
8944
8945 static void CloseAllOpenTimegates()
8946 {
8947   int x, y;
8948
8949   SCAN_PLAYFIELD(x, y)
8950   {
8951     int element = Feld[x][y];
8952
8953     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8954     {
8955       Feld[x][y] = EL_TIMEGATE_CLOSING;
8956
8957       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8958     }
8959   }
8960 }
8961
8962 void DrawTwinkleOnField(int x, int y)
8963 {
8964   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8965     return;
8966
8967   if (Feld[x][y] == EL_BD_DIAMOND)
8968     return;
8969
8970   if (MovDelay[x][y] == 0)      /* next animation frame */
8971     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8972
8973   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8974   {
8975     MovDelay[x][y]--;
8976
8977     DrawLevelElementAnimation(x, y, Feld[x][y]);
8978
8979     if (MovDelay[x][y] != 0)
8980     {
8981       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8982                                            10 - MovDelay[x][y]);
8983
8984       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8985     }
8986   }
8987 }
8988
8989 void MauerWaechst(int x, int y)
8990 {
8991   int delay = 6;
8992
8993   if (!MovDelay[x][y])          /* next animation frame */
8994     MovDelay[x][y] = 3 * delay;
8995
8996   if (MovDelay[x][y])           /* wait some time before next frame */
8997   {
8998     MovDelay[x][y]--;
8999
9000     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9001     {
9002       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9003       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9004
9005       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9006     }
9007
9008     if (!MovDelay[x][y])
9009     {
9010       if (MovDir[x][y] == MV_LEFT)
9011       {
9012         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9013           TEST_DrawLevelField(x - 1, y);
9014       }
9015       else if (MovDir[x][y] == MV_RIGHT)
9016       {
9017         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9018           TEST_DrawLevelField(x + 1, y);
9019       }
9020       else if (MovDir[x][y] == MV_UP)
9021       {
9022         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9023           TEST_DrawLevelField(x, y - 1);
9024       }
9025       else
9026       {
9027         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9028           TEST_DrawLevelField(x, y + 1);
9029       }
9030
9031       Feld[x][y] = Store[x][y];
9032       Store[x][y] = 0;
9033       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9034       TEST_DrawLevelField(x, y);
9035     }
9036   }
9037 }
9038
9039 void MauerAbleger(int ax, int ay)
9040 {
9041   int element = Feld[ax][ay];
9042   int graphic = el2img(element);
9043   boolean oben_frei = FALSE, unten_frei = FALSE;
9044   boolean links_frei = FALSE, rechts_frei = FALSE;
9045   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9046   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9047   boolean new_wall = FALSE;
9048
9049   if (IS_ANIMATED(graphic))
9050     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9051
9052   if (!MovDelay[ax][ay])        /* start building new wall */
9053     MovDelay[ax][ay] = 6;
9054
9055   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9056   {
9057     MovDelay[ax][ay]--;
9058     if (MovDelay[ax][ay])
9059       return;
9060   }
9061
9062   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9063     oben_frei = TRUE;
9064   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9065     unten_frei = TRUE;
9066   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9067     links_frei = TRUE;
9068   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9069     rechts_frei = TRUE;
9070
9071   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9072       element == EL_EXPANDABLE_WALL_ANY)
9073   {
9074     if (oben_frei)
9075     {
9076       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9077       Store[ax][ay-1] = element;
9078       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9079       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9080         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9081                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9082       new_wall = TRUE;
9083     }
9084     if (unten_frei)
9085     {
9086       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9087       Store[ax][ay+1] = element;
9088       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9089       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9090         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9091                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9092       new_wall = TRUE;
9093     }
9094   }
9095
9096   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9097       element == EL_EXPANDABLE_WALL_ANY ||
9098       element == EL_EXPANDABLE_WALL ||
9099       element == EL_BD_EXPANDABLE_WALL)
9100   {
9101     if (links_frei)
9102     {
9103       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9104       Store[ax-1][ay] = element;
9105       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9106       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9107         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9108                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9109       new_wall = TRUE;
9110     }
9111
9112     if (rechts_frei)
9113     {
9114       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9115       Store[ax+1][ay] = element;
9116       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9117       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9118         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9119                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9120       new_wall = TRUE;
9121     }
9122   }
9123
9124   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9125     TEST_DrawLevelField(ax, ay);
9126
9127   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9128     oben_massiv = TRUE;
9129   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9130     unten_massiv = TRUE;
9131   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9132     links_massiv = TRUE;
9133   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9134     rechts_massiv = TRUE;
9135
9136   if (((oben_massiv && unten_massiv) ||
9137        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9138        element == EL_EXPANDABLE_WALL) &&
9139       ((links_massiv && rechts_massiv) ||
9140        element == EL_EXPANDABLE_WALL_VERTICAL))
9141     Feld[ax][ay] = EL_WALL;
9142
9143   if (new_wall)
9144     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9145 }
9146
9147 void MauerAblegerStahl(int ax, int ay)
9148 {
9149   int element = Feld[ax][ay];
9150   int graphic = el2img(element);
9151   boolean oben_frei = FALSE, unten_frei = FALSE;
9152   boolean links_frei = FALSE, rechts_frei = FALSE;
9153   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9154   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9155   boolean new_wall = FALSE;
9156
9157   if (IS_ANIMATED(graphic))
9158     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9159
9160   if (!MovDelay[ax][ay])        /* start building new wall */
9161     MovDelay[ax][ay] = 6;
9162
9163   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9164   {
9165     MovDelay[ax][ay]--;
9166     if (MovDelay[ax][ay])
9167       return;
9168   }
9169
9170   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9171     oben_frei = TRUE;
9172   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9173     unten_frei = TRUE;
9174   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9175     links_frei = TRUE;
9176   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9177     rechts_frei = TRUE;
9178
9179   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9180       element == EL_EXPANDABLE_STEELWALL_ANY)
9181   {
9182     if (oben_frei)
9183     {
9184       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9185       Store[ax][ay-1] = element;
9186       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9187       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9188         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9189                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9190       new_wall = TRUE;
9191     }
9192     if (unten_frei)
9193     {
9194       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9195       Store[ax][ay+1] = element;
9196       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9197       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9198         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9199                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9200       new_wall = TRUE;
9201     }
9202   }
9203
9204   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9205       element == EL_EXPANDABLE_STEELWALL_ANY)
9206   {
9207     if (links_frei)
9208     {
9209       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9210       Store[ax-1][ay] = element;
9211       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9212       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9213         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9214                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9215       new_wall = TRUE;
9216     }
9217
9218     if (rechts_frei)
9219     {
9220       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9221       Store[ax+1][ay] = element;
9222       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9223       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9224         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9225                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9226       new_wall = TRUE;
9227     }
9228   }
9229
9230   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9231     oben_massiv = TRUE;
9232   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9233     unten_massiv = TRUE;
9234   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9235     links_massiv = TRUE;
9236   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9237     rechts_massiv = TRUE;
9238
9239   if (((oben_massiv && unten_massiv) ||
9240        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9241       ((links_massiv && rechts_massiv) ||
9242        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9243     Feld[ax][ay] = EL_STEELWALL;
9244
9245   if (new_wall)
9246     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9247 }
9248
9249 void CheckForDragon(int x, int y)
9250 {
9251   int i, j;
9252   boolean dragon_found = FALSE;
9253   static int xy[4][2] =
9254   {
9255     { 0, -1 },
9256     { -1, 0 },
9257     { +1, 0 },
9258     { 0, +1 }
9259   };
9260
9261   for (i = 0; i < NUM_DIRECTIONS; i++)
9262   {
9263     for (j = 0; j < 4; j++)
9264     {
9265       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9266
9267       if (IN_LEV_FIELD(xx, yy) &&
9268           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9269       {
9270         if (Feld[xx][yy] == EL_DRAGON)
9271           dragon_found = TRUE;
9272       }
9273       else
9274         break;
9275     }
9276   }
9277
9278   if (!dragon_found)
9279   {
9280     for (i = 0; i < NUM_DIRECTIONS; i++)
9281     {
9282       for (j = 0; j < 3; j++)
9283       {
9284         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9285   
9286         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9287         {
9288           Feld[xx][yy] = EL_EMPTY;
9289           TEST_DrawLevelField(xx, yy);
9290         }
9291         else
9292           break;
9293       }
9294     }
9295   }
9296 }
9297
9298 static void InitBuggyBase(int x, int y)
9299 {
9300   int element = Feld[x][y];
9301   int activating_delay = FRAMES_PER_SECOND / 4;
9302
9303   ChangeDelay[x][y] =
9304     (element == EL_SP_BUGGY_BASE ?
9305      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9306      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9307      activating_delay :
9308      element == EL_SP_BUGGY_BASE_ACTIVE ?
9309      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9310 }
9311
9312 static void WarnBuggyBase(int x, int y)
9313 {
9314   int i;
9315   static int xy[4][2] =
9316   {
9317     { 0, -1 },
9318     { -1, 0 },
9319     { +1, 0 },
9320     { 0, +1 }
9321   };
9322
9323   for (i = 0; i < NUM_DIRECTIONS; i++)
9324   {
9325     int xx = x + xy[i][0];
9326     int yy = y + xy[i][1];
9327
9328     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9329     {
9330       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9331
9332       break;
9333     }
9334   }
9335 }
9336
9337 static void InitTrap(int x, int y)
9338 {
9339   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9340 }
9341
9342 static void ActivateTrap(int x, int y)
9343 {
9344   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9345 }
9346
9347 static void ChangeActiveTrap(int x, int y)
9348 {
9349   int graphic = IMG_TRAP_ACTIVE;
9350
9351   /* if new animation frame was drawn, correct crumbled sand border */
9352   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9353     TEST_DrawLevelFieldCrumbled(x, y);
9354 }
9355
9356 static int getSpecialActionElement(int element, int number, int base_element)
9357 {
9358   return (element != EL_EMPTY ? element :
9359           number != -1 ? base_element + number - 1 :
9360           EL_EMPTY);
9361 }
9362
9363 static int getModifiedActionNumber(int value_old, int operator, int operand,
9364                                    int value_min, int value_max)
9365 {
9366   int value_new = (operator == CA_MODE_SET      ? operand :
9367                    operator == CA_MODE_ADD      ? value_old + operand :
9368                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9369                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9370                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9371                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9372                    value_old);
9373
9374   return (value_new < value_min ? value_min :
9375           value_new > value_max ? value_max :
9376           value_new);
9377 }
9378
9379 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9380 {
9381   struct ElementInfo *ei = &element_info[element];
9382   struct ElementChangeInfo *change = &ei->change_page[page];
9383   int target_element = change->target_element;
9384   int action_type = change->action_type;
9385   int action_mode = change->action_mode;
9386   int action_arg = change->action_arg;
9387   int action_element = change->action_element;
9388   int i;
9389
9390   if (!change->has_action)
9391     return;
9392
9393   /* ---------- determine action paramater values -------------------------- */
9394
9395   int level_time_value =
9396     (level.time > 0 ? TimeLeft :
9397      TimePlayed);
9398
9399   int action_arg_element_raw =
9400     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9401      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9402      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9403      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9404      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9405      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9406      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9407      EL_EMPTY);
9408   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9409
9410   int action_arg_direction =
9411     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9412      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9413      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9414      change->actual_trigger_side :
9415      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9416      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9417      MV_NONE);
9418
9419   int action_arg_number_min =
9420     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9421      CA_ARG_MIN);
9422
9423   int action_arg_number_max =
9424     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9425      action_type == CA_SET_LEVEL_GEMS ? 999 :
9426      action_type == CA_SET_LEVEL_TIME ? 9999 :
9427      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9428      action_type == CA_SET_CE_VALUE ? 9999 :
9429      action_type == CA_SET_CE_SCORE ? 9999 :
9430      CA_ARG_MAX);
9431
9432   int action_arg_number_reset =
9433     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9434      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9435      action_type == CA_SET_LEVEL_TIME ? level.time :
9436      action_type == CA_SET_LEVEL_SCORE ? 0 :
9437      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9438      action_type == CA_SET_CE_SCORE ? 0 :
9439      0);
9440
9441   int action_arg_number =
9442     (action_arg <= CA_ARG_MAX ? action_arg :
9443      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9444      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9445      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9446      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9447      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9448      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9449      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9450      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9451      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9452      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9453      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9454      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9455      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9456      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9457      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9458      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9459      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9460      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9461      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9462      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9463      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9464      -1);
9465
9466   int action_arg_number_old =
9467     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9468      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9469      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9470      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9471      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9472      0);
9473
9474   int action_arg_number_new =
9475     getModifiedActionNumber(action_arg_number_old,
9476                             action_mode, action_arg_number,
9477                             action_arg_number_min, action_arg_number_max);
9478
9479   int trigger_player_bits =
9480     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9481      change->actual_trigger_player_bits : change->trigger_player);
9482
9483   int action_arg_player_bits =
9484     (action_arg >= CA_ARG_PLAYER_1 &&
9485      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9486      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9487      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9488      PLAYER_BITS_ANY);
9489
9490   /* ---------- execute action  -------------------------------------------- */
9491
9492   switch (action_type)
9493   {
9494     case CA_NO_ACTION:
9495     {
9496       return;
9497     }
9498
9499     /* ---------- level actions  ------------------------------------------- */
9500
9501     case CA_RESTART_LEVEL:
9502     {
9503       game.restart_level = TRUE;
9504
9505       break;
9506     }
9507
9508     case CA_SHOW_ENVELOPE:
9509     {
9510       int element = getSpecialActionElement(action_arg_element,
9511                                             action_arg_number, EL_ENVELOPE_1);
9512
9513       if (IS_ENVELOPE(element))
9514         local_player->show_envelope = element;
9515
9516       break;
9517     }
9518
9519     case CA_SET_LEVEL_TIME:
9520     {
9521       if (level.time > 0)       /* only modify limited time value */
9522       {
9523         TimeLeft = action_arg_number_new;
9524
9525         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9526
9527         DisplayGameControlValues();
9528
9529         if (!TimeLeft && setup.time_limit)
9530           for (i = 0; i < MAX_PLAYERS; i++)
9531             KillPlayer(&stored_player[i]);
9532       }
9533
9534       break;
9535     }
9536
9537     case CA_SET_LEVEL_SCORE:
9538     {
9539       local_player->score = action_arg_number_new;
9540
9541       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9542
9543       DisplayGameControlValues();
9544
9545       break;
9546     }
9547
9548     case CA_SET_LEVEL_GEMS:
9549     {
9550       local_player->gems_still_needed = action_arg_number_new;
9551
9552       game_panel_controls[GAME_PANEL_GEMS].value =
9553         local_player->gems_still_needed;
9554
9555       DisplayGameControlValues();
9556
9557       break;
9558     }
9559
9560     case CA_SET_LEVEL_WIND:
9561     {
9562       game.wind_direction = action_arg_direction;
9563
9564       break;
9565     }
9566
9567     case CA_SET_LEVEL_RANDOM_SEED:
9568     {
9569       /* ensure that setting a new random seed while playing is predictable */
9570       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9571
9572       break;
9573     }
9574
9575     /* ---------- player actions  ------------------------------------------ */
9576
9577     case CA_MOVE_PLAYER:
9578     {
9579       /* automatically move to the next field in specified direction */
9580       for (i = 0; i < MAX_PLAYERS; i++)
9581         if (trigger_player_bits & (1 << i))
9582           stored_player[i].programmed_action = action_arg_direction;
9583
9584       break;
9585     }
9586
9587     case CA_EXIT_PLAYER:
9588     {
9589       for (i = 0; i < MAX_PLAYERS; i++)
9590         if (action_arg_player_bits & (1 << i))
9591           PlayerWins(&stored_player[i]);
9592
9593       break;
9594     }
9595
9596     case CA_KILL_PLAYER:
9597     {
9598       for (i = 0; i < MAX_PLAYERS; i++)
9599         if (action_arg_player_bits & (1 << i))
9600           KillPlayer(&stored_player[i]);
9601
9602       break;
9603     }
9604
9605     case CA_SET_PLAYER_KEYS:
9606     {
9607       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9608       int element = getSpecialActionElement(action_arg_element,
9609                                             action_arg_number, EL_KEY_1);
9610
9611       if (IS_KEY(element))
9612       {
9613         for (i = 0; i < MAX_PLAYERS; i++)
9614         {
9615           if (trigger_player_bits & (1 << i))
9616           {
9617             stored_player[i].key[KEY_NR(element)] = key_state;
9618
9619             DrawGameDoorValues();
9620           }
9621         }
9622       }
9623
9624       break;
9625     }
9626
9627     case CA_SET_PLAYER_SPEED:
9628     {
9629       for (i = 0; i < MAX_PLAYERS; i++)
9630       {
9631         if (trigger_player_bits & (1 << i))
9632         {
9633           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9634
9635           if (action_arg == CA_ARG_SPEED_FASTER &&
9636               stored_player[i].cannot_move)
9637           {
9638             action_arg_number = STEPSIZE_VERY_SLOW;
9639           }
9640           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9641                    action_arg == CA_ARG_SPEED_FASTER)
9642           {
9643             action_arg_number = 2;
9644             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9645                            CA_MODE_MULTIPLY);
9646           }
9647           else if (action_arg == CA_ARG_NUMBER_RESET)
9648           {
9649             action_arg_number = level.initial_player_stepsize[i];
9650           }
9651
9652           move_stepsize =
9653             getModifiedActionNumber(move_stepsize,
9654                                     action_mode,
9655                                     action_arg_number,
9656                                     action_arg_number_min,
9657                                     action_arg_number_max);
9658
9659           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9660         }
9661       }
9662
9663       break;
9664     }
9665
9666     case CA_SET_PLAYER_SHIELD:
9667     {
9668       for (i = 0; i < MAX_PLAYERS; i++)
9669       {
9670         if (trigger_player_bits & (1 << i))
9671         {
9672           if (action_arg == CA_ARG_SHIELD_OFF)
9673           {
9674             stored_player[i].shield_normal_time_left = 0;
9675             stored_player[i].shield_deadly_time_left = 0;
9676           }
9677           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9678           {
9679             stored_player[i].shield_normal_time_left = 999999;
9680           }
9681           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9682           {
9683             stored_player[i].shield_normal_time_left = 999999;
9684             stored_player[i].shield_deadly_time_left = 999999;
9685           }
9686         }
9687       }
9688
9689       break;
9690     }
9691
9692     case CA_SET_PLAYER_GRAVITY:
9693     {
9694       for (i = 0; i < MAX_PLAYERS; i++)
9695       {
9696         if (trigger_player_bits & (1 << i))
9697         {
9698           stored_player[i].gravity =
9699             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9700              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9701              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9702              stored_player[i].gravity);
9703         }
9704       }
9705
9706       break;
9707     }
9708
9709     case CA_SET_PLAYER_ARTWORK:
9710     {
9711       for (i = 0; i < MAX_PLAYERS; i++)
9712       {
9713         if (trigger_player_bits & (1 << i))
9714         {
9715           int artwork_element = action_arg_element;
9716
9717           if (action_arg == CA_ARG_ELEMENT_RESET)
9718             artwork_element =
9719               (level.use_artwork_element[i] ? level.artwork_element[i] :
9720                stored_player[i].element_nr);
9721
9722           if (stored_player[i].artwork_element != artwork_element)
9723             stored_player[i].Frame = 0;
9724
9725           stored_player[i].artwork_element = artwork_element;
9726
9727           SetPlayerWaiting(&stored_player[i], FALSE);
9728
9729           /* set number of special actions for bored and sleeping animation */
9730           stored_player[i].num_special_action_bored =
9731             get_num_special_action(artwork_element,
9732                                    ACTION_BORING_1, ACTION_BORING_LAST);
9733           stored_player[i].num_special_action_sleeping =
9734             get_num_special_action(artwork_element,
9735                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9736         }
9737       }
9738
9739       break;
9740     }
9741
9742     case CA_SET_PLAYER_INVENTORY:
9743     {
9744       for (i = 0; i < MAX_PLAYERS; i++)
9745       {
9746         struct PlayerInfo *player = &stored_player[i];
9747         int j, k;
9748
9749         if (trigger_player_bits & (1 << i))
9750         {
9751           int inventory_element = action_arg_element;
9752
9753           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9754               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9755               action_arg == CA_ARG_ELEMENT_ACTION)
9756           {
9757             int element = inventory_element;
9758             int collect_count = element_info[element].collect_count_initial;
9759
9760             if (!IS_CUSTOM_ELEMENT(element))
9761               collect_count = 1;
9762
9763             if (collect_count == 0)
9764               player->inventory_infinite_element = element;
9765             else
9766               for (k = 0; k < collect_count; k++)
9767                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9768                   player->inventory_element[player->inventory_size++] =
9769                     element;
9770           }
9771           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9772                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9773                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9774           {
9775             if (player->inventory_infinite_element != EL_UNDEFINED &&
9776                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9777                                      action_arg_element_raw))
9778               player->inventory_infinite_element = EL_UNDEFINED;
9779
9780             for (k = 0, j = 0; j < player->inventory_size; j++)
9781             {
9782               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9783                                         action_arg_element_raw))
9784                 player->inventory_element[k++] = player->inventory_element[j];
9785             }
9786
9787             player->inventory_size = k;
9788           }
9789           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9790           {
9791             if (player->inventory_size > 0)
9792             {
9793               for (j = 0; j < player->inventory_size - 1; j++)
9794                 player->inventory_element[j] = player->inventory_element[j + 1];
9795
9796               player->inventory_size--;
9797             }
9798           }
9799           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9800           {
9801             if (player->inventory_size > 0)
9802               player->inventory_size--;
9803           }
9804           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9805           {
9806             player->inventory_infinite_element = EL_UNDEFINED;
9807             player->inventory_size = 0;
9808           }
9809           else if (action_arg == CA_ARG_INVENTORY_RESET)
9810           {
9811             player->inventory_infinite_element = EL_UNDEFINED;
9812             player->inventory_size = 0;
9813
9814             if (level.use_initial_inventory[i])
9815             {
9816               for (j = 0; j < level.initial_inventory_size[i]; j++)
9817               {
9818                 int element = level.initial_inventory_content[i][j];
9819                 int collect_count = element_info[element].collect_count_initial;
9820
9821                 if (!IS_CUSTOM_ELEMENT(element))
9822                   collect_count = 1;
9823
9824                 if (collect_count == 0)
9825                   player->inventory_infinite_element = element;
9826                 else
9827                   for (k = 0; k < collect_count; k++)
9828                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9829                       player->inventory_element[player->inventory_size++] =
9830                         element;
9831               }
9832             }
9833           }
9834         }
9835       }
9836
9837       break;
9838     }
9839
9840     /* ---------- CE actions  ---------------------------------------------- */
9841
9842     case CA_SET_CE_VALUE:
9843     {
9844       int last_ce_value = CustomValue[x][y];
9845
9846       CustomValue[x][y] = action_arg_number_new;
9847
9848       if (CustomValue[x][y] != last_ce_value)
9849       {
9850         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9851         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9852
9853         if (CustomValue[x][y] == 0)
9854         {
9855           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9856           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9857         }
9858       }
9859
9860       break;
9861     }
9862
9863     case CA_SET_CE_SCORE:
9864     {
9865       int last_ce_score = ei->collect_score;
9866
9867       ei->collect_score = action_arg_number_new;
9868
9869       if (ei->collect_score != last_ce_score)
9870       {
9871         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9872         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9873
9874         if (ei->collect_score == 0)
9875         {
9876           int xx, yy;
9877
9878           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9879           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9880
9881           /*
9882             This is a very special case that seems to be a mixture between
9883             CheckElementChange() and CheckTriggeredElementChange(): while
9884             the first one only affects single elements that are triggered
9885             directly, the second one affects multiple elements in the playfield
9886             that are triggered indirectly by another element. This is a third
9887             case: Changing the CE score always affects multiple identical CEs,
9888             so every affected CE must be checked, not only the single CE for
9889             which the CE score was changed in the first place (as every instance
9890             of that CE shares the same CE score, and therefore also can change)!
9891           */
9892           SCAN_PLAYFIELD(xx, yy)
9893           {
9894             if (Feld[xx][yy] == element)
9895               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9896                                  CE_SCORE_GETS_ZERO);
9897           }
9898         }
9899       }
9900
9901       break;
9902     }
9903
9904     case CA_SET_CE_ARTWORK:
9905     {
9906       int artwork_element = action_arg_element;
9907       boolean reset_frame = FALSE;
9908       int xx, yy;
9909
9910       if (action_arg == CA_ARG_ELEMENT_RESET)
9911         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9912                            element);
9913
9914       if (ei->gfx_element != artwork_element)
9915         reset_frame = TRUE;
9916
9917       ei->gfx_element = artwork_element;
9918
9919       SCAN_PLAYFIELD(xx, yy)
9920       {
9921         if (Feld[xx][yy] == element)
9922         {
9923           if (reset_frame)
9924           {
9925             ResetGfxAnimation(xx, yy);
9926             ResetRandomAnimationValue(xx, yy);
9927           }
9928
9929           TEST_DrawLevelField(xx, yy);
9930         }
9931       }
9932
9933       break;
9934     }
9935
9936     /* ---------- engine actions  ------------------------------------------ */
9937
9938     case CA_SET_ENGINE_SCAN_MODE:
9939     {
9940       InitPlayfieldScanMode(action_arg);
9941
9942       break;
9943     }
9944
9945     default:
9946       break;
9947   }
9948 }
9949
9950 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9951 {
9952   int old_element = Feld[x][y];
9953   int new_element = GetElementFromGroupElement(element);
9954   int previous_move_direction = MovDir[x][y];
9955   int last_ce_value = CustomValue[x][y];
9956   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9957   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9958   boolean add_player_onto_element = (new_element_is_player &&
9959                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9960                                      IS_WALKABLE(old_element));
9961
9962   if (!add_player_onto_element)
9963   {
9964     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9965       RemoveMovingField(x, y);
9966     else
9967       RemoveField(x, y);
9968
9969     Feld[x][y] = new_element;
9970
9971     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9972       MovDir[x][y] = previous_move_direction;
9973
9974     if (element_info[new_element].use_last_ce_value)
9975       CustomValue[x][y] = last_ce_value;
9976
9977     InitField_WithBug1(x, y, FALSE);
9978
9979     new_element = Feld[x][y];   /* element may have changed */
9980
9981     ResetGfxAnimation(x, y);
9982     ResetRandomAnimationValue(x, y);
9983
9984     TEST_DrawLevelField(x, y);
9985
9986     if (GFX_CRUMBLED(new_element))
9987       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9988   }
9989
9990   /* check if element under the player changes from accessible to unaccessible
9991      (needed for special case of dropping element which then changes) */
9992   /* (must be checked after creating new element for walkable group elements) */
9993   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9994       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9995   {
9996     Bang(x, y);
9997
9998     return;
9999   }
10000
10001   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10002   if (new_element_is_player)
10003     RelocatePlayer(x, y, new_element);
10004
10005   if (is_change)
10006     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10007
10008   TestIfBadThingTouchesPlayer(x, y);
10009   TestIfPlayerTouchesCustomElement(x, y);
10010   TestIfElementTouchesCustomElement(x, y);
10011 }
10012
10013 static void CreateField(int x, int y, int element)
10014 {
10015   CreateFieldExt(x, y, element, FALSE);
10016 }
10017
10018 static void CreateElementFromChange(int x, int y, int element)
10019 {
10020   element = GET_VALID_RUNTIME_ELEMENT(element);
10021
10022   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10023   {
10024     int old_element = Feld[x][y];
10025
10026     /* prevent changed element from moving in same engine frame
10027        unless both old and new element can either fall or move */
10028     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10029         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10030       Stop[x][y] = TRUE;
10031   }
10032
10033   CreateFieldExt(x, y, element, TRUE);
10034 }
10035
10036 static boolean ChangeElement(int x, int y, int element, int page)
10037 {
10038   struct ElementInfo *ei = &element_info[element];
10039   struct ElementChangeInfo *change = &ei->change_page[page];
10040   int ce_value = CustomValue[x][y];
10041   int ce_score = ei->collect_score;
10042   int target_element;
10043   int old_element = Feld[x][y];
10044
10045   /* always use default change event to prevent running into a loop */
10046   if (ChangeEvent[x][y] == -1)
10047     ChangeEvent[x][y] = CE_DELAY;
10048
10049   if (ChangeEvent[x][y] == CE_DELAY)
10050   {
10051     /* reset actual trigger element, trigger player and action element */
10052     change->actual_trigger_element = EL_EMPTY;
10053     change->actual_trigger_player = EL_EMPTY;
10054     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10055     change->actual_trigger_side = CH_SIDE_NONE;
10056     change->actual_trigger_ce_value = 0;
10057     change->actual_trigger_ce_score = 0;
10058   }
10059
10060   /* do not change elements more than a specified maximum number of changes */
10061   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10062     return FALSE;
10063
10064   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10065
10066   if (change->explode)
10067   {
10068     Bang(x, y);
10069
10070     return TRUE;
10071   }
10072
10073   if (change->use_target_content)
10074   {
10075     boolean complete_replace = TRUE;
10076     boolean can_replace[3][3];
10077     int xx, yy;
10078
10079     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10080     {
10081       boolean is_empty;
10082       boolean is_walkable;
10083       boolean is_diggable;
10084       boolean is_collectible;
10085       boolean is_removable;
10086       boolean is_destructible;
10087       int ex = x + xx - 1;
10088       int ey = y + yy - 1;
10089       int content_element = change->target_content.e[xx][yy];
10090       int e;
10091
10092       can_replace[xx][yy] = TRUE;
10093
10094       if (ex == x && ey == y)   /* do not check changing element itself */
10095         continue;
10096
10097       if (content_element == EL_EMPTY_SPACE)
10098       {
10099         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10100
10101         continue;
10102       }
10103
10104       if (!IN_LEV_FIELD(ex, ey))
10105       {
10106         can_replace[xx][yy] = FALSE;
10107         complete_replace = FALSE;
10108
10109         continue;
10110       }
10111
10112       e = Feld[ex][ey];
10113
10114       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10115         e = MovingOrBlocked2Element(ex, ey);
10116
10117       is_empty = (IS_FREE(ex, ey) ||
10118                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10119
10120       is_walkable     = (is_empty || IS_WALKABLE(e));
10121       is_diggable     = (is_empty || IS_DIGGABLE(e));
10122       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10123       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10124       is_removable    = (is_diggable || is_collectible);
10125
10126       can_replace[xx][yy] =
10127         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10128           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10129           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10130           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10131           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10132           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10133          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10134
10135       if (!can_replace[xx][yy])
10136         complete_replace = FALSE;
10137     }
10138
10139     if (!change->only_if_complete || complete_replace)
10140     {
10141       boolean something_has_changed = FALSE;
10142
10143       if (change->only_if_complete && change->use_random_replace &&
10144           RND(100) < change->random_percentage)
10145         return FALSE;
10146
10147       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10148       {
10149         int ex = x + xx - 1;
10150         int ey = y + yy - 1;
10151         int content_element;
10152
10153         if (can_replace[xx][yy] && (!change->use_random_replace ||
10154                                     RND(100) < change->random_percentage))
10155         {
10156           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10157             RemoveMovingField(ex, ey);
10158
10159           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10160
10161           content_element = change->target_content.e[xx][yy];
10162           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10163                                               ce_value, ce_score);
10164
10165           CreateElementFromChange(ex, ey, target_element);
10166
10167           something_has_changed = TRUE;
10168
10169           /* for symmetry reasons, freeze newly created border elements */
10170           if (ex != x || ey != y)
10171             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10172         }
10173       }
10174
10175       if (something_has_changed)
10176       {
10177         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10178         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10179       }
10180     }
10181   }
10182   else
10183   {
10184     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10185                                         ce_value, ce_score);
10186
10187     if (element == EL_DIAGONAL_GROWING ||
10188         element == EL_DIAGONAL_SHRINKING)
10189     {
10190       target_element = Store[x][y];
10191
10192       Store[x][y] = EL_EMPTY;
10193     }
10194
10195     CreateElementFromChange(x, y, target_element);
10196
10197     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10198     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10199   }
10200
10201   /* this uses direct change before indirect change */
10202   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10203
10204   return TRUE;
10205 }
10206
10207 static void HandleElementChange(int x, int y, int page)
10208 {
10209   int element = MovingOrBlocked2Element(x, y);
10210   struct ElementInfo *ei = &element_info[element];
10211   struct ElementChangeInfo *change = &ei->change_page[page];
10212   boolean handle_action_before_change = FALSE;
10213
10214 #ifdef DEBUG
10215   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10216       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10217   {
10218     printf("\n\n");
10219     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10220            x, y, element, element_info[element].token_name);
10221     printf("HandleElementChange(): This should never happen!\n");
10222     printf("\n\n");
10223   }
10224 #endif
10225
10226   /* this can happen with classic bombs on walkable, changing elements */
10227   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10228   {
10229     return;
10230   }
10231
10232   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10233   {
10234     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10235
10236     if (change->can_change)
10237     {
10238       /* !!! not clear why graphic animation should be reset at all here !!! */
10239       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10240       /* when a custom element is about to change (for example by change delay),
10241          do not reset graphic animation when the custom element is moving */
10242       if (!IS_MOVING(x, y))
10243       {
10244         ResetGfxAnimation(x, y);
10245         ResetRandomAnimationValue(x, y);
10246       }
10247
10248       if (change->pre_change_function)
10249         change->pre_change_function(x, y);
10250     }
10251   }
10252
10253   ChangeDelay[x][y]--;
10254
10255   if (ChangeDelay[x][y] != 0)           /* continue element change */
10256   {
10257     if (change->can_change)
10258     {
10259       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10260
10261       if (IS_ANIMATED(graphic))
10262         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10263
10264       if (change->change_function)
10265         change->change_function(x, y);
10266     }
10267   }
10268   else                                  /* finish element change */
10269   {
10270     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10271     {
10272       page = ChangePage[x][y];
10273       ChangePage[x][y] = -1;
10274
10275       change = &ei->change_page[page];
10276     }
10277
10278     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10279     {
10280       ChangeDelay[x][y] = 1;            /* try change after next move step */
10281       ChangePage[x][y] = page;          /* remember page to use for change */
10282
10283       return;
10284     }
10285
10286     /* special case: set new level random seed before changing element */
10287     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10288       handle_action_before_change = TRUE;
10289
10290     if (change->has_action && handle_action_before_change)
10291       ExecuteCustomElementAction(x, y, element, page);
10292
10293     if (change->can_change)
10294     {
10295       if (ChangeElement(x, y, element, page))
10296       {
10297         if (change->post_change_function)
10298           change->post_change_function(x, y);
10299       }
10300     }
10301
10302     if (change->has_action && !handle_action_before_change)
10303       ExecuteCustomElementAction(x, y, element, page);
10304   }
10305 }
10306
10307 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10308                                               int trigger_element,
10309                                               int trigger_event,
10310                                               int trigger_player,
10311                                               int trigger_side,
10312                                               int trigger_page)
10313 {
10314   boolean change_done_any = FALSE;
10315   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10316   int i;
10317
10318   if (!(trigger_events[trigger_element][trigger_event]))
10319     return FALSE;
10320
10321   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10322
10323   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10324   {
10325     int element = EL_CUSTOM_START + i;
10326     boolean change_done = FALSE;
10327     int p;
10328
10329     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10330         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10331       continue;
10332
10333     for (p = 0; p < element_info[element].num_change_pages; p++)
10334     {
10335       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10336
10337       if (change->can_change_or_has_action &&
10338           change->has_event[trigger_event] &&
10339           change->trigger_side & trigger_side &&
10340           change->trigger_player & trigger_player &&
10341           change->trigger_page & trigger_page_bits &&
10342           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10343       {
10344         change->actual_trigger_element = trigger_element;
10345         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10346         change->actual_trigger_player_bits = trigger_player;
10347         change->actual_trigger_side = trigger_side;
10348         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10349         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10350
10351         if ((change->can_change && !change_done) || change->has_action)
10352         {
10353           int x, y;
10354
10355           SCAN_PLAYFIELD(x, y)
10356           {
10357             if (Feld[x][y] == element)
10358             {
10359               if (change->can_change && !change_done)
10360               {
10361                 /* if element already changed in this frame, not only prevent
10362                    another element change (checked in ChangeElement()), but
10363                    also prevent additional element actions for this element */
10364
10365                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10366                     !level.use_action_after_change_bug)
10367                   continue;
10368
10369                 ChangeDelay[x][y] = 1;
10370                 ChangeEvent[x][y] = trigger_event;
10371
10372                 HandleElementChange(x, y, p);
10373               }
10374               else if (change->has_action)
10375               {
10376                 /* if element already changed in this frame, not only prevent
10377                    another element change (checked in ChangeElement()), but
10378                    also prevent additional element actions for this element */
10379
10380                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10381                     !level.use_action_after_change_bug)
10382                   continue;
10383
10384                 ExecuteCustomElementAction(x, y, element, p);
10385                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10386               }
10387             }
10388           }
10389
10390           if (change->can_change)
10391           {
10392             change_done = TRUE;
10393             change_done_any = TRUE;
10394           }
10395         }
10396       }
10397     }
10398   }
10399
10400   RECURSION_LOOP_DETECTION_END();
10401
10402   return change_done_any;
10403 }
10404
10405 static boolean CheckElementChangeExt(int x, int y,
10406                                      int element,
10407                                      int trigger_element,
10408                                      int trigger_event,
10409                                      int trigger_player,
10410                                      int trigger_side)
10411 {
10412   boolean change_done = FALSE;
10413   int p;
10414
10415   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10416       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10417     return FALSE;
10418
10419   if (Feld[x][y] == EL_BLOCKED)
10420   {
10421     Blocked2Moving(x, y, &x, &y);
10422     element = Feld[x][y];
10423   }
10424
10425   /* check if element has already changed or is about to change after moving */
10426   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10427        Feld[x][y] != element) ||
10428
10429       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10430        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10431         ChangePage[x][y] != -1)))
10432     return FALSE;
10433
10434   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10435
10436   for (p = 0; p < element_info[element].num_change_pages; p++)
10437   {
10438     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10439
10440     /* check trigger element for all events where the element that is checked
10441        for changing interacts with a directly adjacent element -- this is
10442        different to element changes that affect other elements to change on the
10443        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10444     boolean check_trigger_element =
10445       (trigger_event == CE_TOUCHING_X ||
10446        trigger_event == CE_HITTING_X ||
10447        trigger_event == CE_HIT_BY_X ||
10448        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10449
10450     if (change->can_change_or_has_action &&
10451         change->has_event[trigger_event] &&
10452         change->trigger_side & trigger_side &&
10453         change->trigger_player & trigger_player &&
10454         (!check_trigger_element ||
10455          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10456     {
10457       change->actual_trigger_element = trigger_element;
10458       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10459       change->actual_trigger_player_bits = trigger_player;
10460       change->actual_trigger_side = trigger_side;
10461       change->actual_trigger_ce_value = CustomValue[x][y];
10462       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10463
10464       /* special case: trigger element not at (x,y) position for some events */
10465       if (check_trigger_element)
10466       {
10467         static struct
10468         {
10469           int dx, dy;
10470         } move_xy[] =
10471           {
10472             {  0,  0 },
10473             { -1,  0 },
10474             { +1,  0 },
10475             {  0,  0 },
10476             {  0, -1 },
10477             {  0,  0 }, { 0, 0 }, { 0, 0 },
10478             {  0, +1 }
10479           };
10480
10481         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10482         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10483
10484         change->actual_trigger_ce_value = CustomValue[xx][yy];
10485         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10486       }
10487
10488       if (change->can_change && !change_done)
10489       {
10490         ChangeDelay[x][y] = 1;
10491         ChangeEvent[x][y] = trigger_event;
10492
10493         HandleElementChange(x, y, p);
10494
10495         change_done = TRUE;
10496       }
10497       else if (change->has_action)
10498       {
10499         ExecuteCustomElementAction(x, y, element, p);
10500         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10501       }
10502     }
10503   }
10504
10505   RECURSION_LOOP_DETECTION_END();
10506
10507   return change_done;
10508 }
10509
10510 static void PlayPlayerSound(struct PlayerInfo *player)
10511 {
10512   int jx = player->jx, jy = player->jy;
10513   int sound_element = player->artwork_element;
10514   int last_action = player->last_action_waiting;
10515   int action = player->action_waiting;
10516
10517   if (player->is_waiting)
10518   {
10519     if (action != last_action)
10520       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10521     else
10522       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10523   }
10524   else
10525   {
10526     if (action != last_action)
10527       StopSound(element_info[sound_element].sound[last_action]);
10528
10529     if (last_action == ACTION_SLEEPING)
10530       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10531   }
10532 }
10533
10534 static void PlayAllPlayersSound()
10535 {
10536   int i;
10537
10538   for (i = 0; i < MAX_PLAYERS; i++)
10539     if (stored_player[i].active)
10540       PlayPlayerSound(&stored_player[i]);
10541 }
10542
10543 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10544 {
10545   boolean last_waiting = player->is_waiting;
10546   int move_dir = player->MovDir;
10547
10548   player->dir_waiting = move_dir;
10549   player->last_action_waiting = player->action_waiting;
10550
10551   if (is_waiting)
10552   {
10553     if (!last_waiting)          /* not waiting -> waiting */
10554     {
10555       player->is_waiting = TRUE;
10556
10557       player->frame_counter_bored =
10558         FrameCounter +
10559         game.player_boring_delay_fixed +
10560         GetSimpleRandom(game.player_boring_delay_random);
10561       player->frame_counter_sleeping =
10562         FrameCounter +
10563         game.player_sleeping_delay_fixed +
10564         GetSimpleRandom(game.player_sleeping_delay_random);
10565
10566       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10567     }
10568
10569     if (game.player_sleeping_delay_fixed +
10570         game.player_sleeping_delay_random > 0 &&
10571         player->anim_delay_counter == 0 &&
10572         player->post_delay_counter == 0 &&
10573         FrameCounter >= player->frame_counter_sleeping)
10574       player->is_sleeping = TRUE;
10575     else if (game.player_boring_delay_fixed +
10576              game.player_boring_delay_random > 0 &&
10577              FrameCounter >= player->frame_counter_bored)
10578       player->is_bored = TRUE;
10579
10580     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10581                               player->is_bored ? ACTION_BORING :
10582                               ACTION_WAITING);
10583
10584     if (player->is_sleeping && player->use_murphy)
10585     {
10586       /* special case for sleeping Murphy when leaning against non-free tile */
10587
10588       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10589           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10590            !IS_MOVING(player->jx - 1, player->jy)))
10591         move_dir = MV_LEFT;
10592       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10593                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10594                 !IS_MOVING(player->jx + 1, player->jy)))
10595         move_dir = MV_RIGHT;
10596       else
10597         player->is_sleeping = FALSE;
10598
10599       player->dir_waiting = move_dir;
10600     }
10601
10602     if (player->is_sleeping)
10603     {
10604       if (player->num_special_action_sleeping > 0)
10605       {
10606         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10607         {
10608           int last_special_action = player->special_action_sleeping;
10609           int num_special_action = player->num_special_action_sleeping;
10610           int special_action =
10611             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10612              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10613              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10614              last_special_action + 1 : ACTION_SLEEPING);
10615           int special_graphic =
10616             el_act_dir2img(player->artwork_element, special_action, move_dir);
10617
10618           player->anim_delay_counter =
10619             graphic_info[special_graphic].anim_delay_fixed +
10620             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10621           player->post_delay_counter =
10622             graphic_info[special_graphic].post_delay_fixed +
10623             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10624
10625           player->special_action_sleeping = special_action;
10626         }
10627
10628         if (player->anim_delay_counter > 0)
10629         {
10630           player->action_waiting = player->special_action_sleeping;
10631           player->anim_delay_counter--;
10632         }
10633         else if (player->post_delay_counter > 0)
10634         {
10635           player->post_delay_counter--;
10636         }
10637       }
10638     }
10639     else if (player->is_bored)
10640     {
10641       if (player->num_special_action_bored > 0)
10642       {
10643         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10644         {
10645           int special_action =
10646             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10647           int special_graphic =
10648             el_act_dir2img(player->artwork_element, special_action, move_dir);
10649
10650           player->anim_delay_counter =
10651             graphic_info[special_graphic].anim_delay_fixed +
10652             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10653           player->post_delay_counter =
10654             graphic_info[special_graphic].post_delay_fixed +
10655             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10656
10657           player->special_action_bored = special_action;
10658         }
10659
10660         if (player->anim_delay_counter > 0)
10661         {
10662           player->action_waiting = player->special_action_bored;
10663           player->anim_delay_counter--;
10664         }
10665         else if (player->post_delay_counter > 0)
10666         {
10667           player->post_delay_counter--;
10668         }
10669       }
10670     }
10671   }
10672   else if (last_waiting)        /* waiting -> not waiting */
10673   {
10674     player->is_waiting = FALSE;
10675     player->is_bored = FALSE;
10676     player->is_sleeping = FALSE;
10677
10678     player->frame_counter_bored = -1;
10679     player->frame_counter_sleeping = -1;
10680
10681     player->anim_delay_counter = 0;
10682     player->post_delay_counter = 0;
10683
10684     player->dir_waiting = player->MovDir;
10685     player->action_waiting = ACTION_DEFAULT;
10686
10687     player->special_action_bored = ACTION_DEFAULT;
10688     player->special_action_sleeping = ACTION_DEFAULT;
10689   }
10690 }
10691
10692 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10693 {
10694   static boolean player_was_moving = FALSE;
10695   static boolean player_was_snapping = FALSE;
10696   static boolean player_was_dropping = FALSE;
10697
10698   if ((!player->is_moving  && player_was_moving) ||
10699       (player->MovPos == 0 && player_was_moving) ||
10700       (player->is_snapping && !player_was_snapping) ||
10701       (player->is_dropping && !player_was_dropping))
10702   {
10703     if (!SaveEngineSnapshotToList())
10704       return;
10705
10706     player_was_moving = FALSE;
10707     player_was_snapping = TRUE;
10708     player_was_dropping = TRUE;
10709   }
10710   else
10711   {
10712     if (player->is_moving)
10713       player_was_moving = TRUE;
10714
10715     if (!player->is_snapping)
10716       player_was_snapping = FALSE;
10717
10718     if (!player->is_dropping)
10719       player_was_dropping = FALSE;
10720   }
10721 }
10722
10723 static void CheckSingleStepMode(struct PlayerInfo *player)
10724 {
10725   if (tape.single_step && tape.recording && !tape.pausing)
10726   {
10727     /* as it is called "single step mode", just return to pause mode when the
10728        player stopped moving after one tile (or never starts moving at all) */
10729     if (!player->is_moving && !player->is_pushing)
10730     {
10731       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10732       SnapField(player, 0, 0);                  /* stop snapping */
10733     }
10734   }
10735
10736   CheckSaveEngineSnapshot(player);
10737 }
10738
10739 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10740 {
10741   int left      = player_action & JOY_LEFT;
10742   int right     = player_action & JOY_RIGHT;
10743   int up        = player_action & JOY_UP;
10744   int down      = player_action & JOY_DOWN;
10745   int button1   = player_action & JOY_BUTTON_1;
10746   int button2   = player_action & JOY_BUTTON_2;
10747   int dx        = (left ? -1 : right ? 1 : 0);
10748   int dy        = (up   ? -1 : down  ? 1 : 0);
10749
10750   if (!player->active || tape.pausing)
10751     return 0;
10752
10753   if (player_action)
10754   {
10755     if (button1)
10756       SnapField(player, dx, dy);
10757     else
10758     {
10759       if (button2)
10760         DropElement(player);
10761
10762       MovePlayer(player, dx, dy);
10763     }
10764
10765     CheckSingleStepMode(player);
10766
10767     SetPlayerWaiting(player, FALSE);
10768
10769     return player_action;
10770   }
10771   else
10772   {
10773     /* no actions for this player (no input at player's configured device) */
10774
10775     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10776     SnapField(player, 0, 0);
10777     CheckGravityMovementWhenNotMoving(player);
10778
10779     if (player->MovPos == 0)
10780       SetPlayerWaiting(player, TRUE);
10781
10782     if (player->MovPos == 0)    /* needed for tape.playing */
10783       player->is_moving = FALSE;
10784
10785     player->is_dropping = FALSE;
10786     player->is_dropping_pressed = FALSE;
10787     player->drop_pressed_delay = 0;
10788
10789     CheckSingleStepMode(player);
10790
10791     return 0;
10792   }
10793 }
10794
10795 static void CheckLevelTime()
10796 {
10797   int i;
10798
10799   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10800   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10801   {
10802     if (level.native_em_level->lev->home == 0)  /* all players at home */
10803     {
10804       PlayerWins(local_player);
10805
10806       AllPlayersGone = TRUE;
10807
10808       level.native_em_level->lev->home = -1;
10809     }
10810
10811     if (level.native_em_level->ply[0]->alive == 0 &&
10812         level.native_em_level->ply[1]->alive == 0 &&
10813         level.native_em_level->ply[2]->alive == 0 &&
10814         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10815       AllPlayersGone = TRUE;
10816   }
10817   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10818   {
10819     if (game_sp.LevelSolved &&
10820         !game_sp.GameOver)                              /* game won */
10821     {
10822       PlayerWins(local_player);
10823
10824       game_sp.GameOver = TRUE;
10825
10826       AllPlayersGone = TRUE;
10827     }
10828
10829     if (game_sp.GameOver)                               /* game lost */
10830       AllPlayersGone = TRUE;
10831   }
10832
10833   if (TimeFrames >= FRAMES_PER_SECOND)
10834   {
10835     TimeFrames = 0;
10836     TapeTime++;
10837
10838     for (i = 0; i < MAX_PLAYERS; i++)
10839     {
10840       struct PlayerInfo *player = &stored_player[i];
10841
10842       if (SHIELD_ON(player))
10843       {
10844         player->shield_normal_time_left--;
10845
10846         if (player->shield_deadly_time_left > 0)
10847           player->shield_deadly_time_left--;
10848       }
10849     }
10850
10851     if (!local_player->LevelSolved && !level.use_step_counter)
10852     {
10853       TimePlayed++;
10854
10855       if (TimeLeft > 0)
10856       {
10857         TimeLeft--;
10858
10859         if (TimeLeft <= 10 && setup.time_limit)
10860           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10861
10862         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10863            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10864
10865         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10866
10867         if (!TimeLeft && setup.time_limit)
10868         {
10869           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10870             level.native_em_level->lev->killed_out_of_time = TRUE;
10871           else
10872             for (i = 0; i < MAX_PLAYERS; i++)
10873               KillPlayer(&stored_player[i]);
10874         }
10875       }
10876       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10877       {
10878         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10879       }
10880
10881       level.native_em_level->lev->time =
10882         (game.no_time_limit ? TimePlayed : TimeLeft);
10883     }
10884
10885     if (tape.recording || tape.playing)
10886       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10887   }
10888
10889   if (tape.recording || tape.playing)
10890     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10891
10892   UpdateAndDisplayGameControlValues();
10893 }
10894
10895 void AdvanceFrameAndPlayerCounters(int player_nr)
10896 {
10897   int i;
10898
10899   /* advance frame counters (global frame counter and time frame counter) */
10900   FrameCounter++;
10901   TimeFrames++;
10902
10903   /* advance player counters (counters for move delay, move animation etc.) */
10904   for (i = 0; i < MAX_PLAYERS; i++)
10905   {
10906     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10907     int move_delay_value = stored_player[i].move_delay_value;
10908     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10909
10910     if (!advance_player_counters)       /* not all players may be affected */
10911       continue;
10912
10913     if (move_frames == 0)       /* less than one move per game frame */
10914     {
10915       int stepsize = TILEX / move_delay_value;
10916       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10917       int count = (stored_player[i].is_moving ?
10918                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10919
10920       if (count % delay == 0)
10921         move_frames = 1;
10922     }
10923
10924     stored_player[i].Frame += move_frames;
10925
10926     if (stored_player[i].MovPos != 0)
10927       stored_player[i].StepFrame += move_frames;
10928
10929     if (stored_player[i].move_delay > 0)
10930       stored_player[i].move_delay--;
10931
10932     /* due to bugs in previous versions, counter must count up, not down */
10933     if (stored_player[i].push_delay != -1)
10934       stored_player[i].push_delay++;
10935
10936     if (stored_player[i].drop_delay > 0)
10937       stored_player[i].drop_delay--;
10938
10939     if (stored_player[i].is_dropping_pressed)
10940       stored_player[i].drop_pressed_delay++;
10941   }
10942 }
10943
10944 void StartGameActions(boolean init_network_game, boolean record_tape,
10945                       int random_seed)
10946 {
10947   unsigned int new_random_seed = InitRND(random_seed);
10948
10949   if (record_tape)
10950     TapeStartRecording(new_random_seed);
10951
10952 #if defined(NETWORK_AVALIABLE)
10953   if (init_network_game)
10954   {
10955     SendToServer_StartPlaying();
10956
10957     return;
10958   }
10959 #endif
10960
10961   InitGame();
10962 }
10963
10964 void GameActions()
10965 {
10966   static unsigned int game_frame_delay = 0;
10967   unsigned int game_frame_delay_value;
10968   byte *recorded_player_action;
10969   byte summarized_player_action = 0;
10970   byte tape_action[MAX_PLAYERS];
10971   int i;
10972
10973   for (i = 0; i < MAX_PLAYERS; i++)
10974   {
10975     struct PlayerInfo *player = &stored_player[i];
10976
10977     // allow engine snapshot if movement attempt was stopped
10978     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10979         (player->action & KEY_MOTION) == 0)
10980       game.snapshot.changed_action = TRUE;
10981
10982     // allow engine snapshot in case of snapping/dropping attempt
10983     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10984         (player->action & KEY_BUTTON) != 0)
10985       game.snapshot.changed_action = TRUE;
10986
10987     game.snapshot.last_action[i] = player->action;
10988   }
10989
10990   /* detect endless loops, caused by custom element programming */
10991   if (recursion_loop_detected && recursion_loop_depth == 0)
10992   {
10993     char *message = getStringCat3("Internal Error! Element ",
10994                                   EL_NAME(recursion_loop_element),
10995                                   " caused endless loop! Quit the game?");
10996
10997     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10998           EL_NAME(recursion_loop_element));
10999
11000     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11001
11002     recursion_loop_detected = FALSE;    /* if game should be continued */
11003
11004     free(message);
11005
11006     return;
11007   }
11008
11009   if (game.restart_level)
11010     StartGameActions(options.network, setup.autorecord, level.random_seed);
11011
11012   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11013   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11014   {
11015     if (level.native_em_level->lev->home == 0)  /* all players at home */
11016     {
11017       PlayerWins(local_player);
11018
11019       AllPlayersGone = TRUE;
11020
11021       level.native_em_level->lev->home = -1;
11022     }
11023
11024     if (level.native_em_level->ply[0]->alive == 0 &&
11025         level.native_em_level->ply[1]->alive == 0 &&
11026         level.native_em_level->ply[2]->alive == 0 &&
11027         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11028       AllPlayersGone = TRUE;
11029   }
11030   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11031   {
11032     if (game_sp.LevelSolved &&
11033         !game_sp.GameOver)                              /* game won */
11034     {
11035       PlayerWins(local_player);
11036
11037       game_sp.GameOver = TRUE;
11038
11039       AllPlayersGone = TRUE;
11040     }
11041
11042     if (game_sp.GameOver)                               /* game lost */
11043       AllPlayersGone = TRUE;
11044   }
11045
11046   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11047     GameWon();
11048
11049   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11050     TapeStop();
11051
11052   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11053     return;
11054
11055   game_frame_delay_value =
11056     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11057
11058   if (tape.playing && tape.warp_forward && !tape.pausing)
11059     game_frame_delay_value = 0;
11060
11061   /* ---------- main game synchronization point ---------- */
11062
11063   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11064
11065   if (network_playing && !network_player_action_received)
11066   {
11067     /* try to get network player actions in time */
11068
11069 #if defined(NETWORK_AVALIABLE)
11070     /* last chance to get network player actions without main loop delay */
11071     HandleNetworking();
11072 #endif
11073
11074     /* game was quit by network peer */
11075     if (game_status != GAME_MODE_PLAYING)
11076       return;
11077
11078     if (!network_player_action_received)
11079       return;           /* failed to get network player actions in time */
11080
11081     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11082   }
11083
11084   if (tape.pausing)
11085     return;
11086
11087   /* at this point we know that we really continue executing the game */
11088
11089   network_player_action_received = FALSE;
11090
11091   /* when playing tape, read previously recorded player input from tape data */
11092   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11093
11094   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11095   if (tape.pausing)
11096     return;
11097
11098   if (tape.set_centered_player)
11099   {
11100     game.centered_player_nr_next = tape.centered_player_nr_next;
11101     game.set_centered_player = TRUE;
11102   }
11103
11104   for (i = 0; i < MAX_PLAYERS; i++)
11105   {
11106     summarized_player_action |= stored_player[i].action;
11107
11108     if (!network_playing && (game.team_mode || tape.playing))
11109       stored_player[i].effective_action = stored_player[i].action;
11110   }
11111
11112 #if defined(NETWORK_AVALIABLE)
11113   if (network_playing)
11114     SendToServer_MovePlayer(summarized_player_action);
11115 #endif
11116
11117   if (!options.network && !game.team_mode)
11118     local_player->effective_action = summarized_player_action;
11119
11120   if (tape.recording &&
11121       setup.team_mode &&
11122       setup.input_on_focus &&
11123       game.centered_player_nr != -1)
11124   {
11125     for (i = 0; i < MAX_PLAYERS; i++)
11126       stored_player[i].effective_action =
11127         (i == game.centered_player_nr ? summarized_player_action : 0);
11128   }
11129
11130   if (recorded_player_action != NULL)
11131     for (i = 0; i < MAX_PLAYERS; i++)
11132       stored_player[i].effective_action = recorded_player_action[i];
11133
11134   for (i = 0; i < MAX_PLAYERS; i++)
11135   {
11136     tape_action[i] = stored_player[i].effective_action;
11137
11138     /* (this may happen in the RND game engine if a player was not present on
11139        the playfield on level start, but appeared later from a custom element */
11140     if (setup.team_mode &&
11141         tape.recording &&
11142         tape_action[i] &&
11143         !tape.player_participates[i])
11144       tape.player_participates[i] = TRUE;
11145   }
11146
11147   /* only record actions from input devices, but not programmed actions */
11148   if (tape.recording)
11149     TapeRecordAction(tape_action);
11150
11151 #if USE_NEW_PLAYER_ASSIGNMENTS
11152   // !!! also map player actions in single player mode !!!
11153   // if (game.team_mode)
11154   {
11155     byte mapped_action[MAX_PLAYERS];
11156
11157 #if DEBUG_PLAYER_ACTIONS
11158     printf(":::");
11159     for (i = 0; i < MAX_PLAYERS; i++)
11160       printf(" %d, ", stored_player[i].effective_action);
11161 #endif
11162
11163     for (i = 0; i < MAX_PLAYERS; i++)
11164       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11165
11166     for (i = 0; i < MAX_PLAYERS; i++)
11167       stored_player[i].effective_action = mapped_action[i];
11168
11169 #if DEBUG_PLAYER_ACTIONS
11170     printf(" =>");
11171     for (i = 0; i < MAX_PLAYERS; i++)
11172       printf(" %d, ", stored_player[i].effective_action);
11173     printf("\n");
11174 #endif
11175   }
11176 #if DEBUG_PLAYER_ACTIONS
11177   else
11178   {
11179     printf(":::");
11180     for (i = 0; i < MAX_PLAYERS; i++)
11181       printf(" %d, ", stored_player[i].effective_action);
11182     printf("\n");
11183   }
11184 #endif
11185 #endif
11186
11187   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11188   {
11189     GameActions_EM_Main();
11190   }
11191   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11192   {
11193     GameActions_SP_Main();
11194   }
11195   else
11196   {
11197     GameActions_RND_Main();
11198   }
11199
11200   redraw_mask |= REDRAW_FIELD;
11201 }
11202
11203 void GameActions_EM_Main()
11204 {
11205   byte effective_action[MAX_PLAYERS];
11206   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11207   int i;
11208
11209   for (i = 0; i < MAX_PLAYERS; i++)
11210     effective_action[i] = stored_player[i].effective_action;
11211
11212   GameActions_EM(effective_action, warp_mode);
11213
11214   CheckLevelTime();
11215
11216   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11217 }
11218
11219 void GameActions_SP_Main()
11220 {
11221   byte effective_action[MAX_PLAYERS];
11222   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11223   int i;
11224
11225   for (i = 0; i < MAX_PLAYERS; i++)
11226     effective_action[i] = stored_player[i].effective_action;
11227
11228   GameActions_SP(effective_action, warp_mode);
11229
11230   CheckLevelTime();
11231
11232   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11233 }
11234
11235 void GameActions_RND_Main()
11236 {
11237   GameActions_RND();
11238
11239   BlitScreenToBitmap_RND(backbuffer);
11240 }
11241
11242 void GameActions_RND()
11243 {
11244   int magic_wall_x = 0, magic_wall_y = 0;
11245   int i, x, y, element, graphic;
11246
11247   InitPlayfieldScanModeVars();
11248
11249   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11250   {
11251     SCAN_PLAYFIELD(x, y)
11252     {
11253       ChangeCount[x][y] = 0;
11254       ChangeEvent[x][y] = -1;
11255     }
11256   }
11257
11258   if (game.set_centered_player)
11259   {
11260     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11261
11262     /* switching to "all players" only possible if all players fit to screen */
11263     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11264     {
11265       game.centered_player_nr_next = game.centered_player_nr;
11266       game.set_centered_player = FALSE;
11267     }
11268
11269     /* do not switch focus to non-existing (or non-active) player */
11270     if (game.centered_player_nr_next >= 0 &&
11271         !stored_player[game.centered_player_nr_next].active)
11272     {
11273       game.centered_player_nr_next = game.centered_player_nr;
11274       game.set_centered_player = FALSE;
11275     }
11276   }
11277
11278   if (game.set_centered_player &&
11279       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11280   {
11281     int sx, sy;
11282
11283     if (game.centered_player_nr_next == -1)
11284     {
11285       setScreenCenteredToAllPlayers(&sx, &sy);
11286     }
11287     else
11288     {
11289       sx = stored_player[game.centered_player_nr_next].jx;
11290       sy = stored_player[game.centered_player_nr_next].jy;
11291     }
11292
11293     game.centered_player_nr = game.centered_player_nr_next;
11294     game.set_centered_player = FALSE;
11295
11296     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11297     DrawGameDoorValues();
11298   }
11299
11300   for (i = 0; i < MAX_PLAYERS; i++)
11301   {
11302     int actual_player_action = stored_player[i].effective_action;
11303
11304 #if 1
11305     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11306        - rnd_equinox_tetrachloride 048
11307        - rnd_equinox_tetrachloride_ii 096
11308        - rnd_emanuel_schmieg 002
11309        - doctor_sloan_ww 001, 020
11310     */
11311     if (stored_player[i].MovPos == 0)
11312       CheckGravityMovement(&stored_player[i]);
11313 #endif
11314
11315     /* overwrite programmed action with tape action */
11316     if (stored_player[i].programmed_action)
11317       actual_player_action = stored_player[i].programmed_action;
11318
11319     PlayerActions(&stored_player[i], actual_player_action);
11320
11321     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11322   }
11323
11324   ScrollScreen(NULL, SCROLL_GO_ON);
11325
11326   /* for backwards compatibility, the following code emulates a fixed bug that
11327      occured when pushing elements (causing elements that just made their last
11328      pushing step to already (if possible) make their first falling step in the
11329      same game frame, which is bad); this code is also needed to use the famous
11330      "spring push bug" which is used in older levels and might be wanted to be
11331      used also in newer levels, but in this case the buggy pushing code is only
11332      affecting the "spring" element and no other elements */
11333
11334   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11335   {
11336     for (i = 0; i < MAX_PLAYERS; i++)
11337     {
11338       struct PlayerInfo *player = &stored_player[i];
11339       int x = player->jx;
11340       int y = player->jy;
11341
11342       if (player->active && player->is_pushing && player->is_moving &&
11343           IS_MOVING(x, y) &&
11344           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11345            Feld[x][y] == EL_SPRING))
11346       {
11347         ContinueMoving(x, y);
11348
11349         /* continue moving after pushing (this is actually a bug) */
11350         if (!IS_MOVING(x, y))
11351           Stop[x][y] = FALSE;
11352       }
11353     }
11354   }
11355
11356   SCAN_PLAYFIELD(x, y)
11357   {
11358     ChangeCount[x][y] = 0;
11359     ChangeEvent[x][y] = -1;
11360
11361     /* this must be handled before main playfield loop */
11362     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11363     {
11364       MovDelay[x][y]--;
11365       if (MovDelay[x][y] <= 0)
11366         RemoveField(x, y);
11367     }
11368
11369     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11370     {
11371       MovDelay[x][y]--;
11372       if (MovDelay[x][y] <= 0)
11373       {
11374         RemoveField(x, y);
11375         TEST_DrawLevelField(x, y);
11376
11377         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11378       }
11379     }
11380
11381 #if DEBUG
11382     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11383     {
11384       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11385       printf("GameActions(): This should never happen!\n");
11386
11387       ChangePage[x][y] = -1;
11388     }
11389 #endif
11390
11391     Stop[x][y] = FALSE;
11392     if (WasJustMoving[x][y] > 0)
11393       WasJustMoving[x][y]--;
11394     if (WasJustFalling[x][y] > 0)
11395       WasJustFalling[x][y]--;
11396     if (CheckCollision[x][y] > 0)
11397       CheckCollision[x][y]--;
11398     if (CheckImpact[x][y] > 0)
11399       CheckImpact[x][y]--;
11400
11401     GfxFrame[x][y]++;
11402
11403     /* reset finished pushing action (not done in ContinueMoving() to allow
11404        continuous pushing animation for elements with zero push delay) */
11405     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11406     {
11407       ResetGfxAnimation(x, y);
11408       TEST_DrawLevelField(x, y);
11409     }
11410
11411 #if DEBUG
11412     if (IS_BLOCKED(x, y))
11413     {
11414       int oldx, oldy;
11415
11416       Blocked2Moving(x, y, &oldx, &oldy);
11417       if (!IS_MOVING(oldx, oldy))
11418       {
11419         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11420         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11421         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11422         printf("GameActions(): This should never happen!\n");
11423       }
11424     }
11425 #endif
11426   }
11427
11428   SCAN_PLAYFIELD(x, y)
11429   {
11430     element = Feld[x][y];
11431     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11432
11433     ResetGfxFrame(x, y, TRUE);
11434
11435     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11436         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11437       ResetRandomAnimationValue(x, y);
11438
11439     SetRandomAnimationValue(x, y);
11440
11441     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11442
11443     if (IS_INACTIVE(element))
11444     {
11445       if (IS_ANIMATED(graphic))
11446         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11447
11448       continue;
11449     }
11450
11451     /* this may take place after moving, so 'element' may have changed */
11452     if (IS_CHANGING(x, y) &&
11453         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11454     {
11455       int page = element_info[element].event_page_nr[CE_DELAY];
11456
11457       HandleElementChange(x, y, page);
11458
11459       element = Feld[x][y];
11460       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11461     }
11462
11463     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11464     {
11465       StartMoving(x, y);
11466
11467       element = Feld[x][y];
11468       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11469
11470       if (IS_ANIMATED(graphic) &&
11471           !IS_MOVING(x, y) &&
11472           !Stop[x][y])
11473         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11474
11475       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11476         TEST_DrawTwinkleOnField(x, y);
11477     }
11478     else if ((element == EL_ACID ||
11479               element == EL_EXIT_OPEN ||
11480               element == EL_EM_EXIT_OPEN ||
11481               element == EL_SP_EXIT_OPEN ||
11482               element == EL_STEEL_EXIT_OPEN ||
11483               element == EL_EM_STEEL_EXIT_OPEN ||
11484               element == EL_SP_TERMINAL ||
11485               element == EL_SP_TERMINAL_ACTIVE ||
11486               element == EL_EXTRA_TIME ||
11487               element == EL_SHIELD_NORMAL ||
11488               element == EL_SHIELD_DEADLY) &&
11489              IS_ANIMATED(graphic))
11490       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11491     else if (IS_MOVING(x, y))
11492       ContinueMoving(x, y);
11493     else if (IS_ACTIVE_BOMB(element))
11494       CheckDynamite(x, y);
11495     else if (element == EL_AMOEBA_GROWING)
11496       AmoebeWaechst(x, y);
11497     else if (element == EL_AMOEBA_SHRINKING)
11498       AmoebaDisappearing(x, y);
11499
11500 #if !USE_NEW_AMOEBA_CODE
11501     else if (IS_AMOEBALIVE(element))
11502       AmoebeAbleger(x, y);
11503 #endif
11504
11505     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11506       Life(x, y);
11507     else if (element == EL_EXIT_CLOSED)
11508       CheckExit(x, y);
11509     else if (element == EL_EM_EXIT_CLOSED)
11510       CheckExitEM(x, y);
11511     else if (element == EL_STEEL_EXIT_CLOSED)
11512       CheckExitSteel(x, y);
11513     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11514       CheckExitSteelEM(x, y);
11515     else if (element == EL_SP_EXIT_CLOSED)
11516       CheckExitSP(x, y);
11517     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11518              element == EL_EXPANDABLE_STEELWALL_GROWING)
11519       MauerWaechst(x, y);
11520     else if (element == EL_EXPANDABLE_WALL ||
11521              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11522              element == EL_EXPANDABLE_WALL_VERTICAL ||
11523              element == EL_EXPANDABLE_WALL_ANY ||
11524              element == EL_BD_EXPANDABLE_WALL)
11525       MauerAbleger(x, y);
11526     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11527              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11528              element == EL_EXPANDABLE_STEELWALL_ANY)
11529       MauerAblegerStahl(x, y);
11530     else if (element == EL_FLAMES)
11531       CheckForDragon(x, y);
11532     else if (element == EL_EXPLOSION)
11533       ; /* drawing of correct explosion animation is handled separately */
11534     else if (element == EL_ELEMENT_SNAPPING ||
11535              element == EL_DIAGONAL_SHRINKING ||
11536              element == EL_DIAGONAL_GROWING)
11537     {
11538       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11539
11540       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11541     }
11542     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11543       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11544
11545     if (IS_BELT_ACTIVE(element))
11546       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11547
11548     if (game.magic_wall_active)
11549     {
11550       int jx = local_player->jx, jy = local_player->jy;
11551
11552       /* play the element sound at the position nearest to the player */
11553       if ((element == EL_MAGIC_WALL_FULL ||
11554            element == EL_MAGIC_WALL_ACTIVE ||
11555            element == EL_MAGIC_WALL_EMPTYING ||
11556            element == EL_BD_MAGIC_WALL_FULL ||
11557            element == EL_BD_MAGIC_WALL_ACTIVE ||
11558            element == EL_BD_MAGIC_WALL_EMPTYING ||
11559            element == EL_DC_MAGIC_WALL_FULL ||
11560            element == EL_DC_MAGIC_WALL_ACTIVE ||
11561            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11562           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11563       {
11564         magic_wall_x = x;
11565         magic_wall_y = y;
11566       }
11567     }
11568   }
11569
11570 #if USE_NEW_AMOEBA_CODE
11571   /* new experimental amoeba growth stuff */
11572   if (!(FrameCounter % 8))
11573   {
11574     static unsigned int random = 1684108901;
11575
11576     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11577     {
11578       x = RND(lev_fieldx);
11579       y = RND(lev_fieldy);
11580       element = Feld[x][y];
11581
11582       if (!IS_PLAYER(x,y) &&
11583           (element == EL_EMPTY ||
11584            CAN_GROW_INTO(element) ||
11585            element == EL_QUICKSAND_EMPTY ||
11586            element == EL_QUICKSAND_FAST_EMPTY ||
11587            element == EL_ACID_SPLASH_LEFT ||
11588            element == EL_ACID_SPLASH_RIGHT))
11589       {
11590         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11591             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11592             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11593             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11594           Feld[x][y] = EL_AMOEBA_DROP;
11595       }
11596
11597       random = random * 129 + 1;
11598     }
11599   }
11600 #endif
11601
11602   game.explosions_delayed = FALSE;
11603
11604   SCAN_PLAYFIELD(x, y)
11605   {
11606     element = Feld[x][y];
11607
11608     if (ExplodeField[x][y])
11609       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11610     else if (element == EL_EXPLOSION)
11611       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11612
11613     ExplodeField[x][y] = EX_TYPE_NONE;
11614   }
11615
11616   game.explosions_delayed = TRUE;
11617
11618   if (game.magic_wall_active)
11619   {
11620     if (!(game.magic_wall_time_left % 4))
11621     {
11622       int element = Feld[magic_wall_x][magic_wall_y];
11623
11624       if (element == EL_BD_MAGIC_WALL_FULL ||
11625           element == EL_BD_MAGIC_WALL_ACTIVE ||
11626           element == EL_BD_MAGIC_WALL_EMPTYING)
11627         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11628       else if (element == EL_DC_MAGIC_WALL_FULL ||
11629                element == EL_DC_MAGIC_WALL_ACTIVE ||
11630                element == EL_DC_MAGIC_WALL_EMPTYING)
11631         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11632       else
11633         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11634     }
11635
11636     if (game.magic_wall_time_left > 0)
11637     {
11638       game.magic_wall_time_left--;
11639
11640       if (!game.magic_wall_time_left)
11641       {
11642         SCAN_PLAYFIELD(x, y)
11643         {
11644           element = Feld[x][y];
11645
11646           if (element == EL_MAGIC_WALL_ACTIVE ||
11647               element == EL_MAGIC_WALL_FULL)
11648           {
11649             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11650             TEST_DrawLevelField(x, y);
11651           }
11652           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11653                    element == EL_BD_MAGIC_WALL_FULL)
11654           {
11655             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11656             TEST_DrawLevelField(x, y);
11657           }
11658           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11659                    element == EL_DC_MAGIC_WALL_FULL)
11660           {
11661             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11662             TEST_DrawLevelField(x, y);
11663           }
11664         }
11665
11666         game.magic_wall_active = FALSE;
11667       }
11668     }
11669   }
11670
11671   if (game.light_time_left > 0)
11672   {
11673     game.light_time_left--;
11674
11675     if (game.light_time_left == 0)
11676       RedrawAllLightSwitchesAndInvisibleElements();
11677   }
11678
11679   if (game.timegate_time_left > 0)
11680   {
11681     game.timegate_time_left--;
11682
11683     if (game.timegate_time_left == 0)
11684       CloseAllOpenTimegates();
11685   }
11686
11687   if (game.lenses_time_left > 0)
11688   {
11689     game.lenses_time_left--;
11690
11691     if (game.lenses_time_left == 0)
11692       RedrawAllInvisibleElementsForLenses();
11693   }
11694
11695   if (game.magnify_time_left > 0)
11696   {
11697     game.magnify_time_left--;
11698
11699     if (game.magnify_time_left == 0)
11700       RedrawAllInvisibleElementsForMagnifier();
11701   }
11702
11703   for (i = 0; i < MAX_PLAYERS; i++)
11704   {
11705     struct PlayerInfo *player = &stored_player[i];
11706
11707     if (SHIELD_ON(player))
11708     {
11709       if (player->shield_deadly_time_left)
11710         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11711       else if (player->shield_normal_time_left)
11712         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11713     }
11714   }
11715
11716 #if USE_DELAYED_GFX_REDRAW
11717   SCAN_PLAYFIELD(x, y)
11718   {
11719     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11720     {
11721       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11722          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11723
11724       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11725         DrawLevelField(x, y);
11726
11727       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11728         DrawLevelFieldCrumbled(x, y);
11729
11730       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11731         DrawLevelFieldCrumbledNeighbours(x, y);
11732
11733       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11734         DrawTwinkleOnField(x, y);
11735     }
11736
11737     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11738   }
11739 #endif
11740
11741   CheckLevelTime();
11742
11743   DrawAllPlayers();
11744   PlayAllPlayersSound();
11745
11746   if (options.debug)                    /* calculate frames per second */
11747   {
11748     static unsigned int fps_counter = 0;
11749     static int fps_frames = 0;
11750     unsigned int fps_delay_ms = Counter() - fps_counter;
11751
11752     fps_frames++;
11753
11754     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11755     {
11756       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11757
11758       fps_frames = 0;
11759       fps_counter = Counter();
11760     }
11761
11762     redraw_mask |= REDRAW_FPS;
11763   }
11764
11765   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11766
11767   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11768   {
11769     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11770
11771     local_player->show_envelope = 0;
11772   }
11773
11774   /* use random number generator in every frame to make it less predictable */
11775   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11776     RND(1);
11777 }
11778
11779 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11780 {
11781   int min_x = x, min_y = y, max_x = x, max_y = y;
11782   int i;
11783
11784   for (i = 0; i < MAX_PLAYERS; i++)
11785   {
11786     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11787
11788     if (!stored_player[i].active || &stored_player[i] == player)
11789       continue;
11790
11791     min_x = MIN(min_x, jx);
11792     min_y = MIN(min_y, jy);
11793     max_x = MAX(max_x, jx);
11794     max_y = MAX(max_y, jy);
11795   }
11796
11797   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11798 }
11799
11800 static boolean AllPlayersInVisibleScreen()
11801 {
11802   int i;
11803
11804   for (i = 0; i < MAX_PLAYERS; i++)
11805   {
11806     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11807
11808     if (!stored_player[i].active)
11809       continue;
11810
11811     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11812       return FALSE;
11813   }
11814
11815   return TRUE;
11816 }
11817
11818 void ScrollLevel(int dx, int dy)
11819 {
11820   int scroll_offset = 2 * TILEX_VAR;
11821   int x, y;
11822
11823   BlitBitmap(drawto_field, drawto_field,
11824              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11825              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11826              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11827              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11828              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11829              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11830
11831   if (dx != 0)
11832   {
11833     x = (dx == 1 ? BX1 : BX2);
11834     for (y = BY1; y <= BY2; y++)
11835       DrawScreenField(x, y);
11836   }
11837
11838   if (dy != 0)
11839   {
11840     y = (dy == 1 ? BY1 : BY2);
11841     for (x = BX1; x <= BX2; x++)
11842       DrawScreenField(x, y);
11843   }
11844
11845   redraw_mask |= REDRAW_FIELD;
11846 }
11847
11848 static boolean canFallDown(struct PlayerInfo *player)
11849 {
11850   int jx = player->jx, jy = player->jy;
11851
11852   return (IN_LEV_FIELD(jx, jy + 1) &&
11853           (IS_FREE(jx, jy + 1) ||
11854            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11855           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11856           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11857 }
11858
11859 static boolean canPassField(int x, int y, int move_dir)
11860 {
11861   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11862   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11863   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11864   int nextx = x + dx;
11865   int nexty = y + dy;
11866   int element = Feld[x][y];
11867
11868   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11869           !CAN_MOVE(element) &&
11870           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11871           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11872           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11873 }
11874
11875 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11876 {
11877   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11878   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11879   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11880   int newx = x + dx;
11881   int newy = y + dy;
11882
11883   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11884           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11885           (IS_DIGGABLE(Feld[newx][newy]) ||
11886            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11887            canPassField(newx, newy, move_dir)));
11888 }
11889
11890 static void CheckGravityMovement(struct PlayerInfo *player)
11891 {
11892   if (player->gravity && !player->programmed_action)
11893   {
11894     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11895     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11896     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11897     int jx = player->jx, jy = player->jy;
11898     boolean player_is_moving_to_valid_field =
11899       (!player_is_snapping &&
11900        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11901         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11902     boolean player_can_fall_down = canFallDown(player);
11903
11904     if (player_can_fall_down &&
11905         !player_is_moving_to_valid_field)
11906       player->programmed_action = MV_DOWN;
11907   }
11908 }
11909
11910 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11911 {
11912   return CheckGravityMovement(player);
11913
11914   if (player->gravity && !player->programmed_action)
11915   {
11916     int jx = player->jx, jy = player->jy;
11917     boolean field_under_player_is_free =
11918       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11919     boolean player_is_standing_on_valid_field =
11920       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11921        (IS_WALKABLE(Feld[jx][jy]) &&
11922         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11923
11924     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11925       player->programmed_action = MV_DOWN;
11926   }
11927 }
11928
11929 /*
11930   MovePlayerOneStep()
11931   -----------------------------------------------------------------------------
11932   dx, dy:               direction (non-diagonal) to try to move the player to
11933   real_dx, real_dy:     direction as read from input device (can be diagonal)
11934 */
11935
11936 boolean MovePlayerOneStep(struct PlayerInfo *player,
11937                           int dx, int dy, int real_dx, int real_dy)
11938 {
11939   int jx = player->jx, jy = player->jy;
11940   int new_jx = jx + dx, new_jy = jy + dy;
11941   int can_move;
11942   boolean player_can_move = !player->cannot_move;
11943
11944   if (!player->active || (!dx && !dy))
11945     return MP_NO_ACTION;
11946
11947   player->MovDir = (dx < 0 ? MV_LEFT :
11948                     dx > 0 ? MV_RIGHT :
11949                     dy < 0 ? MV_UP :
11950                     dy > 0 ? MV_DOWN :  MV_NONE);
11951
11952   if (!IN_LEV_FIELD(new_jx, new_jy))
11953     return MP_NO_ACTION;
11954
11955   if (!player_can_move)
11956   {
11957     if (player->MovPos == 0)
11958     {
11959       player->is_moving = FALSE;
11960       player->is_digging = FALSE;
11961       player->is_collecting = FALSE;
11962       player->is_snapping = FALSE;
11963       player->is_pushing = FALSE;
11964     }
11965   }
11966
11967   if (!options.network && game.centered_player_nr == -1 &&
11968       !AllPlayersInSight(player, new_jx, new_jy))
11969     return MP_NO_ACTION;
11970
11971   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11972   if (can_move != MP_MOVING)
11973     return can_move;
11974
11975   /* check if DigField() has caused relocation of the player */
11976   if (player->jx != jx || player->jy != jy)
11977     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11978
11979   StorePlayer[jx][jy] = 0;
11980   player->last_jx = jx;
11981   player->last_jy = jy;
11982   player->jx = new_jx;
11983   player->jy = new_jy;
11984   StorePlayer[new_jx][new_jy] = player->element_nr;
11985
11986   if (player->move_delay_value_next != -1)
11987   {
11988     player->move_delay_value = player->move_delay_value_next;
11989     player->move_delay_value_next = -1;
11990   }
11991
11992   player->MovPos =
11993     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11994
11995   player->step_counter++;
11996
11997   PlayerVisit[jx][jy] = FrameCounter;
11998
11999   player->is_moving = TRUE;
12000
12001 #if 1
12002   /* should better be called in MovePlayer(), but this breaks some tapes */
12003   ScrollPlayer(player, SCROLL_INIT);
12004 #endif
12005
12006   return MP_MOVING;
12007 }
12008
12009 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12010 {
12011   int jx = player->jx, jy = player->jy;
12012   int old_jx = jx, old_jy = jy;
12013   int moved = MP_NO_ACTION;
12014
12015   if (!player->active)
12016     return FALSE;
12017
12018   if (!dx && !dy)
12019   {
12020     if (player->MovPos == 0)
12021     {
12022       player->is_moving = FALSE;
12023       player->is_digging = FALSE;
12024       player->is_collecting = FALSE;
12025       player->is_snapping = FALSE;
12026       player->is_pushing = FALSE;
12027     }
12028
12029     return FALSE;
12030   }
12031
12032   if (player->move_delay > 0)
12033     return FALSE;
12034
12035   player->move_delay = -1;              /* set to "uninitialized" value */
12036
12037   /* store if player is automatically moved to next field */
12038   player->is_auto_moving = (player->programmed_action != MV_NONE);
12039
12040   /* remove the last programmed player action */
12041   player->programmed_action = 0;
12042
12043   if (player->MovPos)
12044   {
12045     /* should only happen if pre-1.2 tape recordings are played */
12046     /* this is only for backward compatibility */
12047
12048     int original_move_delay_value = player->move_delay_value;
12049
12050 #if DEBUG
12051     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12052            tape.counter);
12053 #endif
12054
12055     /* scroll remaining steps with finest movement resolution */
12056     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12057
12058     while (player->MovPos)
12059     {
12060       ScrollPlayer(player, SCROLL_GO_ON);
12061       ScrollScreen(NULL, SCROLL_GO_ON);
12062
12063       AdvanceFrameAndPlayerCounters(player->index_nr);
12064
12065       DrawAllPlayers();
12066       BackToFront();
12067     }
12068
12069     player->move_delay_value = original_move_delay_value;
12070   }
12071
12072   player->is_active = FALSE;
12073
12074   if (player->last_move_dir & MV_HORIZONTAL)
12075   {
12076     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12077       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12078   }
12079   else
12080   {
12081     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12082       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12083   }
12084
12085   if (!moved && !player->is_active)
12086   {
12087     player->is_moving = FALSE;
12088     player->is_digging = FALSE;
12089     player->is_collecting = FALSE;
12090     player->is_snapping = FALSE;
12091     player->is_pushing = FALSE;
12092   }
12093
12094   jx = player->jx;
12095   jy = player->jy;
12096
12097   if (moved & MP_MOVING && !ScreenMovPos &&
12098       (player->index_nr == game.centered_player_nr ||
12099        game.centered_player_nr == -1))
12100   {
12101     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12102     int offset = game.scroll_delay_value;
12103
12104     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12105     {
12106       /* actual player has left the screen -- scroll in that direction */
12107       if (jx != old_jx)         /* player has moved horizontally */
12108         scroll_x += (jx - old_jx);
12109       else                      /* player has moved vertically */
12110         scroll_y += (jy - old_jy);
12111     }
12112     else
12113     {
12114       if (jx != old_jx)         /* player has moved horizontally */
12115       {
12116         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12117             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12118           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12119
12120         /* don't scroll over playfield boundaries */
12121         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12122           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12123
12124         /* don't scroll more than one field at a time */
12125         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12126
12127         /* don't scroll against the player's moving direction */
12128         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12129             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12130           scroll_x = old_scroll_x;
12131       }
12132       else                      /* player has moved vertically */
12133       {
12134         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12135             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12136           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12137
12138         /* don't scroll over playfield boundaries */
12139         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12140           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12141
12142         /* don't scroll more than one field at a time */
12143         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12144
12145         /* don't scroll against the player's moving direction */
12146         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12147             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12148           scroll_y = old_scroll_y;
12149       }
12150     }
12151
12152     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12153     {
12154       if (!options.network && game.centered_player_nr == -1 &&
12155           !AllPlayersInVisibleScreen())
12156       {
12157         scroll_x = old_scroll_x;
12158         scroll_y = old_scroll_y;
12159       }
12160       else
12161       {
12162         ScrollScreen(player, SCROLL_INIT);
12163         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12164       }
12165     }
12166   }
12167
12168   player->StepFrame = 0;
12169
12170   if (moved & MP_MOVING)
12171   {
12172     if (old_jx != jx && old_jy == jy)
12173       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12174     else if (old_jx == jx && old_jy != jy)
12175       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12176
12177     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12178
12179     player->last_move_dir = player->MovDir;
12180     player->is_moving = TRUE;
12181     player->is_snapping = FALSE;
12182     player->is_switching = FALSE;
12183     player->is_dropping = FALSE;
12184     player->is_dropping_pressed = FALSE;
12185     player->drop_pressed_delay = 0;
12186
12187 #if 0
12188     /* should better be called here than above, but this breaks some tapes */
12189     ScrollPlayer(player, SCROLL_INIT);
12190 #endif
12191   }
12192   else
12193   {
12194     CheckGravityMovementWhenNotMoving(player);
12195
12196     player->is_moving = FALSE;
12197
12198     /* at this point, the player is allowed to move, but cannot move right now
12199        (e.g. because of something blocking the way) -- ensure that the player
12200        is also allowed to move in the next frame (in old versions before 3.1.1,
12201        the player was forced to wait again for eight frames before next try) */
12202
12203     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12204       player->move_delay = 0;   /* allow direct movement in the next frame */
12205   }
12206
12207   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12208     player->move_delay = player->move_delay_value;
12209
12210   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12211   {
12212     TestIfPlayerTouchesBadThing(jx, jy);
12213     TestIfPlayerTouchesCustomElement(jx, jy);
12214   }
12215
12216   if (!player->active)
12217     RemovePlayer(player);
12218
12219   return moved;
12220 }
12221
12222 void ScrollPlayer(struct PlayerInfo *player, int mode)
12223 {
12224   int jx = player->jx, jy = player->jy;
12225   int last_jx = player->last_jx, last_jy = player->last_jy;
12226   int move_stepsize = TILEX / player->move_delay_value;
12227
12228   if (!player->active)
12229     return;
12230
12231   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12232     return;
12233
12234   if (mode == SCROLL_INIT)
12235   {
12236     player->actual_frame_counter = FrameCounter;
12237     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12238
12239     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12240         Feld[last_jx][last_jy] == EL_EMPTY)
12241     {
12242       int last_field_block_delay = 0;   /* start with no blocking at all */
12243       int block_delay_adjustment = player->block_delay_adjustment;
12244
12245       /* if player blocks last field, add delay for exactly one move */
12246       if (player->block_last_field)
12247       {
12248         last_field_block_delay += player->move_delay_value;
12249
12250         /* when blocking enabled, prevent moving up despite gravity */
12251         if (player->gravity && player->MovDir == MV_UP)
12252           block_delay_adjustment = -1;
12253       }
12254
12255       /* add block delay adjustment (also possible when not blocking) */
12256       last_field_block_delay += block_delay_adjustment;
12257
12258       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12259       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12260     }
12261
12262     if (player->MovPos != 0)    /* player has not yet reached destination */
12263       return;
12264   }
12265   else if (!FrameReached(&player->actual_frame_counter, 1))
12266     return;
12267
12268   if (player->MovPos != 0)
12269   {
12270     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12271     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12272
12273     /* before DrawPlayer() to draw correct player graphic for this case */
12274     if (player->MovPos == 0)
12275       CheckGravityMovement(player);
12276   }
12277
12278   if (player->MovPos == 0)      /* player reached destination field */
12279   {
12280     if (player->move_delay_reset_counter > 0)
12281     {
12282       player->move_delay_reset_counter--;
12283
12284       if (player->move_delay_reset_counter == 0)
12285       {
12286         /* continue with normal speed after quickly moving through gate */
12287         HALVE_PLAYER_SPEED(player);
12288
12289         /* be able to make the next move without delay */
12290         player->move_delay = 0;
12291       }
12292     }
12293
12294     player->last_jx = jx;
12295     player->last_jy = jy;
12296
12297     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12298         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12299         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12300         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12301         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12302         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12303         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12304         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12305     {
12306       DrawPlayer(player);       /* needed here only to cleanup last field */
12307       RemovePlayer(player);
12308
12309       if (local_player->friends_still_needed == 0 ||
12310           IS_SP_ELEMENT(Feld[jx][jy]))
12311         PlayerWins(player);
12312     }
12313
12314     /* this breaks one level: "machine", level 000 */
12315     {
12316       int move_direction = player->MovDir;
12317       int enter_side = MV_DIR_OPPOSITE(move_direction);
12318       int leave_side = move_direction;
12319       int old_jx = last_jx;
12320       int old_jy = last_jy;
12321       int old_element = Feld[old_jx][old_jy];
12322       int new_element = Feld[jx][jy];
12323
12324       if (IS_CUSTOM_ELEMENT(old_element))
12325         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12326                                    CE_LEFT_BY_PLAYER,
12327                                    player->index_bit, leave_side);
12328
12329       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12330                                           CE_PLAYER_LEAVES_X,
12331                                           player->index_bit, leave_side);
12332
12333       if (IS_CUSTOM_ELEMENT(new_element))
12334         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12335                                    player->index_bit, enter_side);
12336
12337       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12338                                           CE_PLAYER_ENTERS_X,
12339                                           player->index_bit, enter_side);
12340
12341       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12342                                         CE_MOVE_OF_X, move_direction);
12343     }
12344
12345     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12346     {
12347       TestIfPlayerTouchesBadThing(jx, jy);
12348       TestIfPlayerTouchesCustomElement(jx, jy);
12349
12350       /* needed because pushed element has not yet reached its destination,
12351          so it would trigger a change event at its previous field location */
12352       if (!player->is_pushing)
12353         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12354
12355       if (!player->active)
12356         RemovePlayer(player);
12357     }
12358
12359     if (!local_player->LevelSolved && level.use_step_counter)
12360     {
12361       int i;
12362
12363       TimePlayed++;
12364
12365       if (TimeLeft > 0)
12366       {
12367         TimeLeft--;
12368
12369         if (TimeLeft <= 10 && setup.time_limit)
12370           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12371
12372         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12373
12374         DisplayGameControlValues();
12375
12376         if (!TimeLeft && setup.time_limit)
12377           for (i = 0; i < MAX_PLAYERS; i++)
12378             KillPlayer(&stored_player[i]);
12379       }
12380       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12381       {
12382         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12383
12384         DisplayGameControlValues();
12385       }
12386     }
12387
12388     if (tape.single_step && tape.recording && !tape.pausing &&
12389         !player->programmed_action)
12390       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12391
12392     if (!player->programmed_action)
12393       CheckSaveEngineSnapshot(player);
12394   }
12395 }
12396
12397 void ScrollScreen(struct PlayerInfo *player, int mode)
12398 {
12399   static unsigned int screen_frame_counter = 0;
12400
12401   if (mode == SCROLL_INIT)
12402   {
12403     /* set scrolling step size according to actual player's moving speed */
12404     ScrollStepSize = TILEX / player->move_delay_value;
12405
12406     screen_frame_counter = FrameCounter;
12407     ScreenMovDir = player->MovDir;
12408     ScreenMovPos = player->MovPos;
12409     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12410     return;
12411   }
12412   else if (!FrameReached(&screen_frame_counter, 1))
12413     return;
12414
12415   if (ScreenMovPos)
12416   {
12417     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12418     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12419     redraw_mask |= REDRAW_FIELD;
12420   }
12421   else
12422     ScreenMovDir = MV_NONE;
12423 }
12424
12425 void TestIfPlayerTouchesCustomElement(int x, int y)
12426 {
12427   static int xy[4][2] =
12428   {
12429     { 0, -1 },
12430     { -1, 0 },
12431     { +1, 0 },
12432     { 0, +1 }
12433   };
12434   static int trigger_sides[4][2] =
12435   {
12436     /* center side       border side */
12437     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12438     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12439     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12440     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12441   };
12442   static int touch_dir[4] =
12443   {
12444     MV_LEFT | MV_RIGHT,
12445     MV_UP   | MV_DOWN,
12446     MV_UP   | MV_DOWN,
12447     MV_LEFT | MV_RIGHT
12448   };
12449   int center_element = Feld[x][y];      /* should always be non-moving! */
12450   int i;
12451
12452   for (i = 0; i < NUM_DIRECTIONS; i++)
12453   {
12454     int xx = x + xy[i][0];
12455     int yy = y + xy[i][1];
12456     int center_side = trigger_sides[i][0];
12457     int border_side = trigger_sides[i][1];
12458     int border_element;
12459
12460     if (!IN_LEV_FIELD(xx, yy))
12461       continue;
12462
12463     if (IS_PLAYER(x, y))                /* player found at center element */
12464     {
12465       struct PlayerInfo *player = PLAYERINFO(x, y);
12466
12467       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12468         border_element = Feld[xx][yy];          /* may be moving! */
12469       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12470         border_element = Feld[xx][yy];
12471       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12472         border_element = MovingOrBlocked2Element(xx, yy);
12473       else
12474         continue;               /* center and border element do not touch */
12475
12476       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12477                                  player->index_bit, border_side);
12478       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12479                                           CE_PLAYER_TOUCHES_X,
12480                                           player->index_bit, border_side);
12481
12482       {
12483         /* use player element that is initially defined in the level playfield,
12484            not the player element that corresponds to the runtime player number
12485            (example: a level that contains EL_PLAYER_3 as the only player would
12486            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12487         int player_element = PLAYERINFO(x, y)->initial_element;
12488
12489         CheckElementChangeBySide(xx, yy, border_element, player_element,
12490                                  CE_TOUCHING_X, border_side);
12491       }
12492     }
12493     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12494     {
12495       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12496
12497       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12498       {
12499         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12500           continue;             /* center and border element do not touch */
12501       }
12502
12503       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12504                                  player->index_bit, center_side);
12505       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12506                                           CE_PLAYER_TOUCHES_X,
12507                                           player->index_bit, center_side);
12508
12509       {
12510         /* use player element that is initially defined in the level playfield,
12511            not the player element that corresponds to the runtime player number
12512            (example: a level that contains EL_PLAYER_3 as the only player would
12513            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12514         int player_element = PLAYERINFO(xx, yy)->initial_element;
12515
12516         CheckElementChangeBySide(x, y, center_element, player_element,
12517                                  CE_TOUCHING_X, center_side);
12518       }
12519
12520       break;
12521     }
12522   }
12523 }
12524
12525 void TestIfElementTouchesCustomElement(int x, int y)
12526 {
12527   static int xy[4][2] =
12528   {
12529     { 0, -1 },
12530     { -1, 0 },
12531     { +1, 0 },
12532     { 0, +1 }
12533   };
12534   static int trigger_sides[4][2] =
12535   {
12536     /* center side      border side */
12537     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12538     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12539     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12540     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12541   };
12542   static int touch_dir[4] =
12543   {
12544     MV_LEFT | MV_RIGHT,
12545     MV_UP   | MV_DOWN,
12546     MV_UP   | MV_DOWN,
12547     MV_LEFT | MV_RIGHT
12548   };
12549   boolean change_center_element = FALSE;
12550   int center_element = Feld[x][y];      /* should always be non-moving! */
12551   int border_element_old[NUM_DIRECTIONS];
12552   int i;
12553
12554   for (i = 0; i < NUM_DIRECTIONS; i++)
12555   {
12556     int xx = x + xy[i][0];
12557     int yy = y + xy[i][1];
12558     int border_element;
12559
12560     border_element_old[i] = -1;
12561
12562     if (!IN_LEV_FIELD(xx, yy))
12563       continue;
12564
12565     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12566       border_element = Feld[xx][yy];    /* may be moving! */
12567     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12568       border_element = Feld[xx][yy];
12569     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12570       border_element = MovingOrBlocked2Element(xx, yy);
12571     else
12572       continue;                 /* center and border element do not touch */
12573
12574     border_element_old[i] = border_element;
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 center_side = trigger_sides[i][0];
12582     int border_element = border_element_old[i];
12583
12584     if (border_element == -1)
12585       continue;
12586
12587     /* check for change of border element */
12588     CheckElementChangeBySide(xx, yy, border_element, center_element,
12589                              CE_TOUCHING_X, center_side);
12590
12591     /* (center element cannot be player, so we dont have to check this here) */
12592   }
12593
12594   for (i = 0; i < NUM_DIRECTIONS; i++)
12595   {
12596     int xx = x + xy[i][0];
12597     int yy = y + xy[i][1];
12598     int border_side = trigger_sides[i][1];
12599     int border_element = border_element_old[i];
12600
12601     if (border_element == -1)
12602       continue;
12603
12604     /* check for change of center element (but change it only once) */
12605     if (!change_center_element)
12606       change_center_element =
12607         CheckElementChangeBySide(x, y, center_element, border_element,
12608                                  CE_TOUCHING_X, border_side);
12609
12610     if (IS_PLAYER(xx, yy))
12611     {
12612       /* use player element that is initially defined in the level playfield,
12613          not the player element that corresponds to the runtime player number
12614          (example: a level that contains EL_PLAYER_3 as the only player would
12615          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12616       int player_element = PLAYERINFO(xx, yy)->initial_element;
12617
12618       CheckElementChangeBySide(x, y, center_element, player_element,
12619                                CE_TOUCHING_X, border_side);
12620     }
12621   }
12622 }
12623
12624 void TestIfElementHitsCustomElement(int x, int y, int direction)
12625 {
12626   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12627   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12628   int hitx = x + dx, hity = y + dy;
12629   int hitting_element = Feld[x][y];
12630   int touched_element;
12631
12632   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12633     return;
12634
12635   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12636                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12637
12638   if (IN_LEV_FIELD(hitx, hity))
12639   {
12640     int opposite_direction = MV_DIR_OPPOSITE(direction);
12641     int hitting_side = direction;
12642     int touched_side = opposite_direction;
12643     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12644                           MovDir[hitx][hity] != direction ||
12645                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12646
12647     object_hit = TRUE;
12648
12649     if (object_hit)
12650     {
12651       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12652                                CE_HITTING_X, touched_side);
12653
12654       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12655                                CE_HIT_BY_X, hitting_side);
12656
12657       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12658                                CE_HIT_BY_SOMETHING, opposite_direction);
12659
12660       if (IS_PLAYER(hitx, hity))
12661       {
12662         /* use player element that is initially defined in the level playfield,
12663            not the player element that corresponds to the runtime player number
12664            (example: a level that contains EL_PLAYER_3 as the only player would
12665            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12666         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12667
12668         CheckElementChangeBySide(x, y, hitting_element, player_element,
12669                                  CE_HITTING_X, touched_side);
12670       }
12671     }
12672   }
12673
12674   /* "hitting something" is also true when hitting the playfield border */
12675   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12676                            CE_HITTING_SOMETHING, direction);
12677 }
12678
12679 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12680 {
12681   int i, kill_x = -1, kill_y = -1;
12682
12683   int bad_element = -1;
12684   static int test_xy[4][2] =
12685   {
12686     { 0, -1 },
12687     { -1, 0 },
12688     { +1, 0 },
12689     { 0, +1 }
12690   };
12691   static int test_dir[4] =
12692   {
12693     MV_UP,
12694     MV_LEFT,
12695     MV_RIGHT,
12696     MV_DOWN
12697   };
12698
12699   for (i = 0; i < NUM_DIRECTIONS; i++)
12700   {
12701     int test_x, test_y, test_move_dir, test_element;
12702
12703     test_x = good_x + test_xy[i][0];
12704     test_y = good_y + test_xy[i][1];
12705
12706     if (!IN_LEV_FIELD(test_x, test_y))
12707       continue;
12708
12709     test_move_dir =
12710       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12711
12712     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12713
12714     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12715        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12716     */
12717     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12718         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12719     {
12720       kill_x = test_x;
12721       kill_y = test_y;
12722       bad_element = test_element;
12723
12724       break;
12725     }
12726   }
12727
12728   if (kill_x != -1 || kill_y != -1)
12729   {
12730     if (IS_PLAYER(good_x, good_y))
12731     {
12732       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12733
12734       if (player->shield_deadly_time_left > 0 &&
12735           !IS_INDESTRUCTIBLE(bad_element))
12736         Bang(kill_x, kill_y);
12737       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12738         KillPlayer(player);
12739     }
12740     else
12741       Bang(good_x, good_y);
12742   }
12743 }
12744
12745 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12746 {
12747   int i, kill_x = -1, kill_y = -1;
12748   int bad_element = Feld[bad_x][bad_y];
12749   static int test_xy[4][2] =
12750   {
12751     { 0, -1 },
12752     { -1, 0 },
12753     { +1, 0 },
12754     { 0, +1 }
12755   };
12756   static int touch_dir[4] =
12757   {
12758     MV_LEFT | MV_RIGHT,
12759     MV_UP   | MV_DOWN,
12760     MV_UP   | MV_DOWN,
12761     MV_LEFT | MV_RIGHT
12762   };
12763   static int test_dir[4] =
12764   {
12765     MV_UP,
12766     MV_LEFT,
12767     MV_RIGHT,
12768     MV_DOWN
12769   };
12770
12771   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12772     return;
12773
12774   for (i = 0; i < NUM_DIRECTIONS; i++)
12775   {
12776     int test_x, test_y, test_move_dir, test_element;
12777
12778     test_x = bad_x + test_xy[i][0];
12779     test_y = bad_y + test_xy[i][1];
12780
12781     if (!IN_LEV_FIELD(test_x, test_y))
12782       continue;
12783
12784     test_move_dir =
12785       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12786
12787     test_element = Feld[test_x][test_y];
12788
12789     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12790        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12791     */
12792     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12793         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12794     {
12795       /* good thing is player or penguin that does not move away */
12796       if (IS_PLAYER(test_x, test_y))
12797       {
12798         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12799
12800         if (bad_element == EL_ROBOT && player->is_moving)
12801           continue;     /* robot does not kill player if he is moving */
12802
12803         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12804         {
12805           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12806             continue;           /* center and border element do not touch */
12807         }
12808
12809         kill_x = test_x;
12810         kill_y = test_y;
12811
12812         break;
12813       }
12814       else if (test_element == EL_PENGUIN)
12815       {
12816         kill_x = test_x;
12817         kill_y = test_y;
12818
12819         break;
12820       }
12821     }
12822   }
12823
12824   if (kill_x != -1 || kill_y != -1)
12825   {
12826     if (IS_PLAYER(kill_x, kill_y))
12827     {
12828       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12829
12830       if (player->shield_deadly_time_left > 0 &&
12831           !IS_INDESTRUCTIBLE(bad_element))
12832         Bang(bad_x, bad_y);
12833       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12834         KillPlayer(player);
12835     }
12836     else
12837       Bang(kill_x, kill_y);
12838   }
12839 }
12840
12841 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12842 {
12843   int bad_element = Feld[bad_x][bad_y];
12844   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12845   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12846   int test_x = bad_x + dx, test_y = bad_y + dy;
12847   int test_move_dir, test_element;
12848   int kill_x = -1, kill_y = -1;
12849
12850   if (!IN_LEV_FIELD(test_x, test_y))
12851     return;
12852
12853   test_move_dir =
12854     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12855
12856   test_element = Feld[test_x][test_y];
12857
12858   if (test_move_dir != bad_move_dir)
12859   {
12860     /* good thing can be player or penguin that does not move away */
12861     if (IS_PLAYER(test_x, test_y))
12862     {
12863       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12864
12865       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12866          player as being hit when he is moving towards the bad thing, because
12867          the "get hit by" condition would be lost after the player stops) */
12868       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12869         return;         /* player moves away from bad thing */
12870
12871       kill_x = test_x;
12872       kill_y = test_y;
12873     }
12874     else if (test_element == EL_PENGUIN)
12875     {
12876       kill_x = test_x;
12877       kill_y = test_y;
12878     }
12879   }
12880
12881   if (kill_x != -1 || kill_y != -1)
12882   {
12883     if (IS_PLAYER(kill_x, kill_y))
12884     {
12885       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12886
12887       if (player->shield_deadly_time_left > 0 &&
12888           !IS_INDESTRUCTIBLE(bad_element))
12889         Bang(bad_x, bad_y);
12890       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12891         KillPlayer(player);
12892     }
12893     else
12894       Bang(kill_x, kill_y);
12895   }
12896 }
12897
12898 void TestIfPlayerTouchesBadThing(int x, int y)
12899 {
12900   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12901 }
12902
12903 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12904 {
12905   TestIfGoodThingHitsBadThing(x, y, move_dir);
12906 }
12907
12908 void TestIfBadThingTouchesPlayer(int x, int y)
12909 {
12910   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12911 }
12912
12913 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12914 {
12915   TestIfBadThingHitsGoodThing(x, y, move_dir);
12916 }
12917
12918 void TestIfFriendTouchesBadThing(int x, int y)
12919 {
12920   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12921 }
12922
12923 void TestIfBadThingTouchesFriend(int x, int y)
12924 {
12925   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12926 }
12927
12928 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12929 {
12930   int i, kill_x = bad_x, kill_y = bad_y;
12931   static int xy[4][2] =
12932   {
12933     { 0, -1 },
12934     { -1, 0 },
12935     { +1, 0 },
12936     { 0, +1 }
12937   };
12938
12939   for (i = 0; i < NUM_DIRECTIONS; i++)
12940   {
12941     int x, y, element;
12942
12943     x = bad_x + xy[i][0];
12944     y = bad_y + xy[i][1];
12945     if (!IN_LEV_FIELD(x, y))
12946       continue;
12947
12948     element = Feld[x][y];
12949     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12950         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12951     {
12952       kill_x = x;
12953       kill_y = y;
12954       break;
12955     }
12956   }
12957
12958   if (kill_x != bad_x || kill_y != bad_y)
12959     Bang(bad_x, bad_y);
12960 }
12961
12962 void KillPlayer(struct PlayerInfo *player)
12963 {
12964   int jx = player->jx, jy = player->jy;
12965
12966   if (!player->active)
12967     return;
12968
12969 #if 0
12970   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12971          player->killed, player->active, player->reanimated);
12972 #endif
12973
12974   /* the following code was introduced to prevent an infinite loop when calling
12975      -> Bang()
12976      -> CheckTriggeredElementChangeExt()
12977      -> ExecuteCustomElementAction()
12978      -> KillPlayer()
12979      -> (infinitely repeating the above sequence of function calls)
12980      which occurs when killing the player while having a CE with the setting
12981      "kill player X when explosion of <player X>"; the solution using a new
12982      field "player->killed" was chosen for backwards compatibility, although
12983      clever use of the fields "player->active" etc. would probably also work */
12984 #if 1
12985   if (player->killed)
12986     return;
12987 #endif
12988
12989   player->killed = TRUE;
12990
12991   /* remove accessible field at the player's position */
12992   Feld[jx][jy] = EL_EMPTY;
12993
12994   /* deactivate shield (else Bang()/Explode() would not work right) */
12995   player->shield_normal_time_left = 0;
12996   player->shield_deadly_time_left = 0;
12997
12998 #if 0
12999   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13000          player->killed, player->active, player->reanimated);
13001 #endif
13002
13003   Bang(jx, jy);
13004
13005 #if 0
13006   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13007          player->killed, player->active, player->reanimated);
13008 #endif
13009
13010   if (player->reanimated)       /* killed player may have been reanimated */
13011     player->killed = player->reanimated = FALSE;
13012   else
13013     BuryPlayer(player);
13014 }
13015
13016 static void KillPlayerUnlessEnemyProtected(int x, int y)
13017 {
13018   if (!PLAYER_ENEMY_PROTECTED(x, y))
13019     KillPlayer(PLAYERINFO(x, y));
13020 }
13021
13022 static void KillPlayerUnlessExplosionProtected(int x, int y)
13023 {
13024   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13025     KillPlayer(PLAYERINFO(x, y));
13026 }
13027
13028 void BuryPlayer(struct PlayerInfo *player)
13029 {
13030   int jx = player->jx, jy = player->jy;
13031
13032   if (!player->active)
13033     return;
13034
13035   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13036   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13037
13038   player->GameOver = TRUE;
13039   RemovePlayer(player);
13040 }
13041
13042 void RemovePlayer(struct PlayerInfo *player)
13043 {
13044   int jx = player->jx, jy = player->jy;
13045   int i, found = FALSE;
13046
13047   player->present = FALSE;
13048   player->active = FALSE;
13049
13050   if (!ExplodeField[jx][jy])
13051     StorePlayer[jx][jy] = 0;
13052
13053   if (player->is_moving)
13054     TEST_DrawLevelField(player->last_jx, player->last_jy);
13055
13056   for (i = 0; i < MAX_PLAYERS; i++)
13057     if (stored_player[i].active)
13058       found = TRUE;
13059
13060   if (!found)
13061     AllPlayersGone = TRUE;
13062
13063   ExitX = ZX = jx;
13064   ExitY = ZY = jy;
13065 }
13066
13067 static void setFieldForSnapping(int x, int y, int element, int direction)
13068 {
13069   struct ElementInfo *ei = &element_info[element];
13070   int direction_bit = MV_DIR_TO_BIT(direction);
13071   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13072   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13073                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13074
13075   Feld[x][y] = EL_ELEMENT_SNAPPING;
13076   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13077
13078   ResetGfxAnimation(x, y);
13079
13080   GfxElement[x][y] = element;
13081   GfxAction[x][y] = action;
13082   GfxDir[x][y] = direction;
13083   GfxFrame[x][y] = -1;
13084 }
13085
13086 /*
13087   =============================================================================
13088   checkDiagonalPushing()
13089   -----------------------------------------------------------------------------
13090   check if diagonal input device direction results in pushing of object
13091   (by checking if the alternative direction is walkable, diggable, ...)
13092   =============================================================================
13093 */
13094
13095 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13096                                     int x, int y, int real_dx, int real_dy)
13097 {
13098   int jx, jy, dx, dy, xx, yy;
13099
13100   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13101     return TRUE;
13102
13103   /* diagonal direction: check alternative direction */
13104   jx = player->jx;
13105   jy = player->jy;
13106   dx = x - jx;
13107   dy = y - jy;
13108   xx = jx + (dx == 0 ? real_dx : 0);
13109   yy = jy + (dy == 0 ? real_dy : 0);
13110
13111   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13112 }
13113
13114 /*
13115   =============================================================================
13116   DigField()
13117   -----------------------------------------------------------------------------
13118   x, y:                 field next to player (non-diagonal) to try to dig to
13119   real_dx, real_dy:     direction as read from input device (can be diagonal)
13120   =============================================================================
13121 */
13122
13123 static int DigField(struct PlayerInfo *player,
13124                     int oldx, int oldy, int x, int y,
13125                     int real_dx, int real_dy, int mode)
13126 {
13127   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13128   boolean player_was_pushing = player->is_pushing;
13129   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13130   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13131   int jx = oldx, jy = oldy;
13132   int dx = x - jx, dy = y - jy;
13133   int nextx = x + dx, nexty = y + dy;
13134   int move_direction = (dx == -1 ? MV_LEFT  :
13135                         dx == +1 ? MV_RIGHT :
13136                         dy == -1 ? MV_UP    :
13137                         dy == +1 ? MV_DOWN  : MV_NONE);
13138   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13139   int dig_side = MV_DIR_OPPOSITE(move_direction);
13140   int old_element = Feld[jx][jy];
13141   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13142   int collect_count;
13143
13144   if (is_player)                /* function can also be called by EL_PENGUIN */
13145   {
13146     if (player->MovPos == 0)
13147     {
13148       player->is_digging = FALSE;
13149       player->is_collecting = FALSE;
13150     }
13151
13152     if (player->MovPos == 0)    /* last pushing move finished */
13153       player->is_pushing = FALSE;
13154
13155     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13156     {
13157       player->is_switching = FALSE;
13158       player->push_delay = -1;
13159
13160       return MP_NO_ACTION;
13161     }
13162   }
13163
13164   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13165     old_element = Back[jx][jy];
13166
13167   /* in case of element dropped at player position, check background */
13168   else if (Back[jx][jy] != EL_EMPTY &&
13169            game.engine_version >= VERSION_IDENT(2,2,0,0))
13170     old_element = Back[jx][jy];
13171
13172   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13173     return MP_NO_ACTION;        /* field has no opening in this direction */
13174
13175   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13176     return MP_NO_ACTION;        /* field has no opening in this direction */
13177
13178   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13179   {
13180     SplashAcid(x, y);
13181
13182     Feld[jx][jy] = player->artwork_element;
13183     InitMovingField(jx, jy, MV_DOWN);
13184     Store[jx][jy] = EL_ACID;
13185     ContinueMoving(jx, jy);
13186     BuryPlayer(player);
13187
13188     return MP_DONT_RUN_INTO;
13189   }
13190
13191   if (player_can_move && DONT_RUN_INTO(element))
13192   {
13193     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13194
13195     return MP_DONT_RUN_INTO;
13196   }
13197
13198   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13199     return MP_NO_ACTION;
13200
13201   collect_count = element_info[element].collect_count_initial;
13202
13203   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13204     return MP_NO_ACTION;
13205
13206   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13207     player_can_move = player_can_move_or_snap;
13208
13209   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13210       game.engine_version >= VERSION_IDENT(2,2,0,0))
13211   {
13212     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13213                                player->index_bit, dig_side);
13214     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13215                                         player->index_bit, dig_side);
13216
13217     if (element == EL_DC_LANDMINE)
13218       Bang(x, y);
13219
13220     if (Feld[x][y] != element)          /* field changed by snapping */
13221       return MP_ACTION;
13222
13223     return MP_NO_ACTION;
13224   }
13225
13226   if (player->gravity && is_player && !player->is_auto_moving &&
13227       canFallDown(player) && move_direction != MV_DOWN &&
13228       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13229     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13230
13231   if (player_can_move &&
13232       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13233   {
13234     int sound_element = SND_ELEMENT(element);
13235     int sound_action = ACTION_WALKING;
13236
13237     if (IS_RND_GATE(element))
13238     {
13239       if (!player->key[RND_GATE_NR(element)])
13240         return MP_NO_ACTION;
13241     }
13242     else if (IS_RND_GATE_GRAY(element))
13243     {
13244       if (!player->key[RND_GATE_GRAY_NR(element)])
13245         return MP_NO_ACTION;
13246     }
13247     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13248     {
13249       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13250         return MP_NO_ACTION;
13251     }
13252     else if (element == EL_EXIT_OPEN ||
13253              element == EL_EM_EXIT_OPEN ||
13254              element == EL_EM_EXIT_OPENING ||
13255              element == EL_STEEL_EXIT_OPEN ||
13256              element == EL_EM_STEEL_EXIT_OPEN ||
13257              element == EL_EM_STEEL_EXIT_OPENING ||
13258              element == EL_SP_EXIT_OPEN ||
13259              element == EL_SP_EXIT_OPENING)
13260     {
13261       sound_action = ACTION_PASSING;    /* player is passing exit */
13262     }
13263     else if (element == EL_EMPTY)
13264     {
13265       sound_action = ACTION_MOVING;             /* nothing to walk on */
13266     }
13267
13268     /* play sound from background or player, whatever is available */
13269     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13270       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13271     else
13272       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13273   }
13274   else if (player_can_move &&
13275            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13276   {
13277     if (!ACCESS_FROM(element, opposite_direction))
13278       return MP_NO_ACTION;      /* field not accessible from this direction */
13279
13280     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13281       return MP_NO_ACTION;
13282
13283     if (IS_EM_GATE(element))
13284     {
13285       if (!player->key[EM_GATE_NR(element)])
13286         return MP_NO_ACTION;
13287     }
13288     else if (IS_EM_GATE_GRAY(element))
13289     {
13290       if (!player->key[EM_GATE_GRAY_NR(element)])
13291         return MP_NO_ACTION;
13292     }
13293     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13294     {
13295       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13296         return MP_NO_ACTION;
13297     }
13298     else if (IS_EMC_GATE(element))
13299     {
13300       if (!player->key[EMC_GATE_NR(element)])
13301         return MP_NO_ACTION;
13302     }
13303     else if (IS_EMC_GATE_GRAY(element))
13304     {
13305       if (!player->key[EMC_GATE_GRAY_NR(element)])
13306         return MP_NO_ACTION;
13307     }
13308     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13309     {
13310       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13311         return MP_NO_ACTION;
13312     }
13313     else if (element == EL_DC_GATE_WHITE ||
13314              element == EL_DC_GATE_WHITE_GRAY ||
13315              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13316     {
13317       if (player->num_white_keys == 0)
13318         return MP_NO_ACTION;
13319
13320       player->num_white_keys--;
13321     }
13322     else if (IS_SP_PORT(element))
13323     {
13324       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13325           element == EL_SP_GRAVITY_PORT_RIGHT ||
13326           element == EL_SP_GRAVITY_PORT_UP ||
13327           element == EL_SP_GRAVITY_PORT_DOWN)
13328         player->gravity = !player->gravity;
13329       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13330                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13331                element == EL_SP_GRAVITY_ON_PORT_UP ||
13332                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13333         player->gravity = TRUE;
13334       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13335                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13336                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13337                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13338         player->gravity = FALSE;
13339     }
13340
13341     /* automatically move to the next field with double speed */
13342     player->programmed_action = move_direction;
13343
13344     if (player->move_delay_reset_counter == 0)
13345     {
13346       player->move_delay_reset_counter = 2;     /* two double speed steps */
13347
13348       DOUBLE_PLAYER_SPEED(player);
13349     }
13350
13351     PlayLevelSoundAction(x, y, ACTION_PASSING);
13352   }
13353   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13354   {
13355     RemoveField(x, y);
13356
13357     if (mode != DF_SNAP)
13358     {
13359       GfxElement[x][y] = GFX_ELEMENT(element);
13360       player->is_digging = TRUE;
13361     }
13362
13363     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13364
13365     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13366                                         player->index_bit, dig_side);
13367
13368     if (mode == DF_SNAP)
13369     {
13370       if (level.block_snap_field)
13371         setFieldForSnapping(x, y, element, move_direction);
13372       else
13373         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13374
13375       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13376                                           player->index_bit, dig_side);
13377     }
13378   }
13379   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13380   {
13381     RemoveField(x, y);
13382
13383     if (is_player && mode != DF_SNAP)
13384     {
13385       GfxElement[x][y] = element;
13386       player->is_collecting = TRUE;
13387     }
13388
13389     if (element == EL_SPEED_PILL)
13390     {
13391       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13392     }
13393     else if (element == EL_EXTRA_TIME && level.time > 0)
13394     {
13395       TimeLeft += level.extra_time;
13396
13397       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13398
13399       DisplayGameControlValues();
13400     }
13401     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13402     {
13403       player->shield_normal_time_left += level.shield_normal_time;
13404       if (element == EL_SHIELD_DEADLY)
13405         player->shield_deadly_time_left += level.shield_deadly_time;
13406     }
13407     else if (element == EL_DYNAMITE ||
13408              element == EL_EM_DYNAMITE ||
13409              element == EL_SP_DISK_RED)
13410     {
13411       if (player->inventory_size < MAX_INVENTORY_SIZE)
13412         player->inventory_element[player->inventory_size++] = element;
13413
13414       DrawGameDoorValues();
13415     }
13416     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13417     {
13418       player->dynabomb_count++;
13419       player->dynabombs_left++;
13420     }
13421     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13422     {
13423       player->dynabomb_size++;
13424     }
13425     else if (element == EL_DYNABOMB_INCREASE_POWER)
13426     {
13427       player->dynabomb_xl = TRUE;
13428     }
13429     else if (IS_KEY(element))
13430     {
13431       player->key[KEY_NR(element)] = TRUE;
13432
13433       DrawGameDoorValues();
13434     }
13435     else if (element == EL_DC_KEY_WHITE)
13436     {
13437       player->num_white_keys++;
13438
13439       /* display white keys? */
13440       /* DrawGameDoorValues(); */
13441     }
13442     else if (IS_ENVELOPE(element))
13443     {
13444       player->show_envelope = element;
13445     }
13446     else if (element == EL_EMC_LENSES)
13447     {
13448       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13449
13450       RedrawAllInvisibleElementsForLenses();
13451     }
13452     else if (element == EL_EMC_MAGNIFIER)
13453     {
13454       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13455
13456       RedrawAllInvisibleElementsForMagnifier();
13457     }
13458     else if (IS_DROPPABLE(element) ||
13459              IS_THROWABLE(element))     /* can be collected and dropped */
13460     {
13461       int i;
13462
13463       if (collect_count == 0)
13464         player->inventory_infinite_element = element;
13465       else
13466         for (i = 0; i < collect_count; i++)
13467           if (player->inventory_size < MAX_INVENTORY_SIZE)
13468             player->inventory_element[player->inventory_size++] = element;
13469
13470       DrawGameDoorValues();
13471     }
13472     else if (collect_count > 0)
13473     {
13474       local_player->gems_still_needed -= collect_count;
13475       if (local_player->gems_still_needed < 0)
13476         local_player->gems_still_needed = 0;
13477
13478       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13479
13480       DisplayGameControlValues();
13481     }
13482
13483     RaiseScoreElement(element);
13484     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13485
13486     if (is_player)
13487       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13488                                           player->index_bit, dig_side);
13489
13490     if (mode == DF_SNAP)
13491     {
13492       if (level.block_snap_field)
13493         setFieldForSnapping(x, y, element, move_direction);
13494       else
13495         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13496
13497       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13498                                           player->index_bit, dig_side);
13499     }
13500   }
13501   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13502   {
13503     if (mode == DF_SNAP && element != EL_BD_ROCK)
13504       return MP_NO_ACTION;
13505
13506     if (CAN_FALL(element) && dy)
13507       return MP_NO_ACTION;
13508
13509     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13510         !(element == EL_SPRING && level.use_spring_bug))
13511       return MP_NO_ACTION;
13512
13513     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13514         ((move_direction & MV_VERTICAL &&
13515           ((element_info[element].move_pattern & MV_LEFT &&
13516             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13517            (element_info[element].move_pattern & MV_RIGHT &&
13518             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13519          (move_direction & MV_HORIZONTAL &&
13520           ((element_info[element].move_pattern & MV_UP &&
13521             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13522            (element_info[element].move_pattern & MV_DOWN &&
13523             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13524       return MP_NO_ACTION;
13525
13526     /* do not push elements already moving away faster than player */
13527     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13528         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13529       return MP_NO_ACTION;
13530
13531     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13532     {
13533       if (player->push_delay_value == -1 || !player_was_pushing)
13534         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13535     }
13536     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13537     {
13538       if (player->push_delay_value == -1)
13539         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13540     }
13541     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13542     {
13543       if (!player->is_pushing)
13544         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13545     }
13546
13547     player->is_pushing = TRUE;
13548     player->is_active = TRUE;
13549
13550     if (!(IN_LEV_FIELD(nextx, nexty) &&
13551           (IS_FREE(nextx, nexty) ||
13552            (IS_SB_ELEMENT(element) &&
13553             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13554            (IS_CUSTOM_ELEMENT(element) &&
13555             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13556       return MP_NO_ACTION;
13557
13558     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13559       return MP_NO_ACTION;
13560
13561     if (player->push_delay == -1)       /* new pushing; restart delay */
13562       player->push_delay = 0;
13563
13564     if (player->push_delay < player->push_delay_value &&
13565         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13566         element != EL_SPRING && element != EL_BALLOON)
13567     {
13568       /* make sure that there is no move delay before next try to push */
13569       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13570         player->move_delay = 0;
13571
13572       return MP_NO_ACTION;
13573     }
13574
13575     if (IS_CUSTOM_ELEMENT(element) &&
13576         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13577     {
13578       if (!DigFieldByCE(nextx, nexty, element))
13579         return MP_NO_ACTION;
13580     }
13581
13582     if (IS_SB_ELEMENT(element))
13583     {
13584       if (element == EL_SOKOBAN_FIELD_FULL)
13585       {
13586         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13587         local_player->sokobanfields_still_needed++;
13588       }
13589
13590       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13591       {
13592         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13593         local_player->sokobanfields_still_needed--;
13594       }
13595
13596       Feld[x][y] = EL_SOKOBAN_OBJECT;
13597
13598       if (Back[x][y] == Back[nextx][nexty])
13599         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13600       else if (Back[x][y] != 0)
13601         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13602                                     ACTION_EMPTYING);
13603       else
13604         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13605                                     ACTION_FILLING);
13606
13607       if (local_player->sokobanfields_still_needed == 0 &&
13608           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13609       {
13610         PlayerWins(player);
13611
13612         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13613       }
13614     }
13615     else
13616       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13617
13618     InitMovingField(x, y, move_direction);
13619     GfxAction[x][y] = ACTION_PUSHING;
13620
13621     if (mode == DF_SNAP)
13622       ContinueMoving(x, y);
13623     else
13624       MovPos[x][y] = (dx != 0 ? dx : dy);
13625
13626     Pushed[x][y] = TRUE;
13627     Pushed[nextx][nexty] = TRUE;
13628
13629     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13630       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13631     else
13632       player->push_delay_value = -1;    /* get new value later */
13633
13634     /* check for element change _after_ element has been pushed */
13635     if (game.use_change_when_pushing_bug)
13636     {
13637       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13638                                  player->index_bit, dig_side);
13639       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13640                                           player->index_bit, dig_side);
13641     }
13642   }
13643   else if (IS_SWITCHABLE(element))
13644   {
13645     if (PLAYER_SWITCHING(player, x, y))
13646     {
13647       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13648                                           player->index_bit, dig_side);
13649
13650       return MP_ACTION;
13651     }
13652
13653     player->is_switching = TRUE;
13654     player->switch_x = x;
13655     player->switch_y = y;
13656
13657     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13658
13659     if (element == EL_ROBOT_WHEEL)
13660     {
13661       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13662       ZX = x;
13663       ZY = y;
13664
13665       game.robot_wheel_active = TRUE;
13666
13667       TEST_DrawLevelField(x, y);
13668     }
13669     else if (element == EL_SP_TERMINAL)
13670     {
13671       int xx, yy;
13672
13673       SCAN_PLAYFIELD(xx, yy)
13674       {
13675         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13676           Bang(xx, yy);
13677         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13678           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13679       }
13680     }
13681     else if (IS_BELT_SWITCH(element))
13682     {
13683       ToggleBeltSwitch(x, y);
13684     }
13685     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13686              element == EL_SWITCHGATE_SWITCH_DOWN ||
13687              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13688              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13689     {
13690       ToggleSwitchgateSwitch(x, y);
13691     }
13692     else if (element == EL_LIGHT_SWITCH ||
13693              element == EL_LIGHT_SWITCH_ACTIVE)
13694     {
13695       ToggleLightSwitch(x, y);
13696     }
13697     else if (element == EL_TIMEGATE_SWITCH ||
13698              element == EL_DC_TIMEGATE_SWITCH)
13699     {
13700       ActivateTimegateSwitch(x, y);
13701     }
13702     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13703              element == EL_BALLOON_SWITCH_RIGHT ||
13704              element == EL_BALLOON_SWITCH_UP    ||
13705              element == EL_BALLOON_SWITCH_DOWN  ||
13706              element == EL_BALLOON_SWITCH_NONE  ||
13707              element == EL_BALLOON_SWITCH_ANY)
13708     {
13709       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13710                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13711                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13712                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13713                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13714                              move_direction);
13715     }
13716     else if (element == EL_LAMP)
13717     {
13718       Feld[x][y] = EL_LAMP_ACTIVE;
13719       local_player->lights_still_needed--;
13720
13721       ResetGfxAnimation(x, y);
13722       TEST_DrawLevelField(x, y);
13723     }
13724     else if (element == EL_TIME_ORB_FULL)
13725     {
13726       Feld[x][y] = EL_TIME_ORB_EMPTY;
13727
13728       if (level.time > 0 || level.use_time_orb_bug)
13729       {
13730         TimeLeft += level.time_orb_time;
13731         game.no_time_limit = FALSE;
13732
13733         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13734
13735         DisplayGameControlValues();
13736       }
13737
13738       ResetGfxAnimation(x, y);
13739       TEST_DrawLevelField(x, y);
13740     }
13741     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13742              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13743     {
13744       int xx, yy;
13745
13746       game.ball_state = !game.ball_state;
13747
13748       SCAN_PLAYFIELD(xx, yy)
13749       {
13750         int e = Feld[xx][yy];
13751
13752         if (game.ball_state)
13753         {
13754           if (e == EL_EMC_MAGIC_BALL)
13755             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13756           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13757             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13758         }
13759         else
13760         {
13761           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13762             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13763           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13764             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13765         }
13766       }
13767     }
13768
13769     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13770                                         player->index_bit, dig_side);
13771
13772     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13773                                         player->index_bit, dig_side);
13774
13775     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13776                                         player->index_bit, dig_side);
13777
13778     return MP_ACTION;
13779   }
13780   else
13781   {
13782     if (!PLAYER_SWITCHING(player, x, y))
13783     {
13784       player->is_switching = TRUE;
13785       player->switch_x = x;
13786       player->switch_y = y;
13787
13788       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13789                                  player->index_bit, dig_side);
13790       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13791                                           player->index_bit, dig_side);
13792
13793       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13794                                  player->index_bit, dig_side);
13795       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13796                                           player->index_bit, dig_side);
13797     }
13798
13799     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13800                                player->index_bit, dig_side);
13801     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13802                                         player->index_bit, dig_side);
13803
13804     return MP_NO_ACTION;
13805   }
13806
13807   player->push_delay = -1;
13808
13809   if (is_player)                /* function can also be called by EL_PENGUIN */
13810   {
13811     if (Feld[x][y] != element)          /* really digged/collected something */
13812     {
13813       player->is_collecting = !player->is_digging;
13814       player->is_active = TRUE;
13815     }
13816   }
13817
13818   return MP_MOVING;
13819 }
13820
13821 static boolean DigFieldByCE(int x, int y, int digging_element)
13822 {
13823   int element = Feld[x][y];
13824
13825   if (!IS_FREE(x, y))
13826   {
13827     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13828                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13829                   ACTION_BREAKING);
13830
13831     /* no element can dig solid indestructible elements */
13832     if (IS_INDESTRUCTIBLE(element) &&
13833         !IS_DIGGABLE(element) &&
13834         !IS_COLLECTIBLE(element))
13835       return FALSE;
13836
13837     if (AmoebaNr[x][y] &&
13838         (element == EL_AMOEBA_FULL ||
13839          element == EL_BD_AMOEBA ||
13840          element == EL_AMOEBA_GROWING))
13841     {
13842       AmoebaCnt[AmoebaNr[x][y]]--;
13843       AmoebaCnt2[AmoebaNr[x][y]]--;
13844     }
13845
13846     if (IS_MOVING(x, y))
13847       RemoveMovingField(x, y);
13848     else
13849     {
13850       RemoveField(x, y);
13851       TEST_DrawLevelField(x, y);
13852     }
13853
13854     /* if digged element was about to explode, prevent the explosion */
13855     ExplodeField[x][y] = EX_TYPE_NONE;
13856
13857     PlayLevelSoundAction(x, y, action);
13858   }
13859
13860   Store[x][y] = EL_EMPTY;
13861
13862   /* this makes it possible to leave the removed element again */
13863   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13864     Store[x][y] = element;
13865
13866   return TRUE;
13867 }
13868
13869 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13870 {
13871   int jx = player->jx, jy = player->jy;
13872   int x = jx + dx, y = jy + dy;
13873   int snap_direction = (dx == -1 ? MV_LEFT  :
13874                         dx == +1 ? MV_RIGHT :
13875                         dy == -1 ? MV_UP    :
13876                         dy == +1 ? MV_DOWN  : MV_NONE);
13877   boolean can_continue_snapping = (level.continuous_snapping &&
13878                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13879
13880   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13881     return FALSE;
13882
13883   if (!player->active || !IN_LEV_FIELD(x, y))
13884     return FALSE;
13885
13886   if (dx && dy)
13887     return FALSE;
13888
13889   if (!dx && !dy)
13890   {
13891     if (player->MovPos == 0)
13892       player->is_pushing = FALSE;
13893
13894     player->is_snapping = FALSE;
13895
13896     if (player->MovPos == 0)
13897     {
13898       player->is_moving = FALSE;
13899       player->is_digging = FALSE;
13900       player->is_collecting = FALSE;
13901     }
13902
13903     return FALSE;
13904   }
13905
13906   /* prevent snapping with already pressed snap key when not allowed */
13907   if (player->is_snapping && !can_continue_snapping)
13908     return FALSE;
13909
13910   player->MovDir = snap_direction;
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   player->is_dropping = FALSE;
13920   player->is_dropping_pressed = FALSE;
13921   player->drop_pressed_delay = 0;
13922
13923   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13924     return FALSE;
13925
13926   player->is_snapping = TRUE;
13927   player->is_active = TRUE;
13928
13929   if (player->MovPos == 0)
13930   {
13931     player->is_moving = FALSE;
13932     player->is_digging = FALSE;
13933     player->is_collecting = FALSE;
13934   }
13935
13936   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13937     TEST_DrawLevelField(player->last_jx, player->last_jy);
13938
13939   TEST_DrawLevelField(x, y);
13940
13941   return TRUE;
13942 }
13943
13944 static boolean DropElement(struct PlayerInfo *player)
13945 {
13946   int old_element, new_element;
13947   int dropx = player->jx, dropy = player->jy;
13948   int drop_direction = player->MovDir;
13949   int drop_side = drop_direction;
13950   int drop_element = get_next_dropped_element(player);
13951
13952   player->is_dropping_pressed = TRUE;
13953
13954   /* do not drop an element on top of another element; when holding drop key
13955      pressed without moving, dropped element must move away before the next
13956      element can be dropped (this is especially important if the next element
13957      is dynamite, which can be placed on background for historical reasons) */
13958   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13959     return MP_ACTION;
13960
13961   if (IS_THROWABLE(drop_element))
13962   {
13963     dropx += GET_DX_FROM_DIR(drop_direction);
13964     dropy += GET_DY_FROM_DIR(drop_direction);
13965
13966     if (!IN_LEV_FIELD(dropx, dropy))
13967       return FALSE;
13968   }
13969
13970   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13971   new_element = drop_element;           /* default: no change when dropping */
13972
13973   /* check if player is active, not moving and ready to drop */
13974   if (!player->active || player->MovPos || player->drop_delay > 0)
13975     return FALSE;
13976
13977   /* check if player has anything that can be dropped */
13978   if (new_element == EL_UNDEFINED)
13979     return FALSE;
13980
13981   /* check if drop key was pressed long enough for EM style dynamite */
13982   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13983     return FALSE;
13984
13985   /* check if anything can be dropped at the current position */
13986   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13987     return FALSE;
13988
13989   /* collected custom elements can only be dropped on empty fields */
13990   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13991     return FALSE;
13992
13993   if (old_element != EL_EMPTY)
13994     Back[dropx][dropy] = old_element;   /* store old element on this field */
13995
13996   ResetGfxAnimation(dropx, dropy);
13997   ResetRandomAnimationValue(dropx, dropy);
13998
13999   if (player->inventory_size > 0 ||
14000       player->inventory_infinite_element != EL_UNDEFINED)
14001   {
14002     if (player->inventory_size > 0)
14003     {
14004       player->inventory_size--;
14005
14006       DrawGameDoorValues();
14007
14008       if (new_element == EL_DYNAMITE)
14009         new_element = EL_DYNAMITE_ACTIVE;
14010       else if (new_element == EL_EM_DYNAMITE)
14011         new_element = EL_EM_DYNAMITE_ACTIVE;
14012       else if (new_element == EL_SP_DISK_RED)
14013         new_element = EL_SP_DISK_RED_ACTIVE;
14014     }
14015
14016     Feld[dropx][dropy] = new_element;
14017
14018     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14019       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14020                           el2img(Feld[dropx][dropy]), 0);
14021
14022     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14023
14024     /* needed if previous element just changed to "empty" in the last frame */
14025     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14026
14027     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14028                                player->index_bit, drop_side);
14029     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14030                                         CE_PLAYER_DROPS_X,
14031                                         player->index_bit, drop_side);
14032
14033     TestIfElementTouchesCustomElement(dropx, dropy);
14034   }
14035   else          /* player is dropping a dyna bomb */
14036   {
14037     player->dynabombs_left--;
14038
14039     Feld[dropx][dropy] = new_element;
14040
14041     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14042       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14043                           el2img(Feld[dropx][dropy]), 0);
14044
14045     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14046   }
14047
14048   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14049     InitField_WithBug1(dropx, dropy, FALSE);
14050
14051   new_element = Feld[dropx][dropy];     /* element might have changed */
14052
14053   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14054       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14055   {
14056     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14057       MovDir[dropx][dropy] = drop_direction;
14058
14059     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14060
14061     /* do not cause impact style collision by dropping elements that can fall */
14062     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14063   }
14064
14065   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14066   player->is_dropping = TRUE;
14067
14068   player->drop_pressed_delay = 0;
14069   player->is_dropping_pressed = FALSE;
14070
14071   player->drop_x = dropx;
14072   player->drop_y = dropy;
14073
14074   return TRUE;
14075 }
14076
14077 /* ------------------------------------------------------------------------- */
14078 /* game sound playing functions                                              */
14079 /* ------------------------------------------------------------------------- */
14080
14081 static int *loop_sound_frame = NULL;
14082 static int *loop_sound_volume = NULL;
14083
14084 void InitPlayLevelSound()
14085 {
14086   int num_sounds = getSoundListSize();
14087
14088   checked_free(loop_sound_frame);
14089   checked_free(loop_sound_volume);
14090
14091   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14092   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14093 }
14094
14095 static void PlayLevelSound(int x, int y, int nr)
14096 {
14097   int sx = SCREENX(x), sy = SCREENY(y);
14098   int volume, stereo_position;
14099   int max_distance = 8;
14100   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14101
14102   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14103       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14104     return;
14105
14106   if (!IN_LEV_FIELD(x, y) ||
14107       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14108       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14109     return;
14110
14111   volume = SOUND_MAX_VOLUME;
14112
14113   if (!IN_SCR_FIELD(sx, sy))
14114   {
14115     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14116     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14117
14118     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14119   }
14120
14121   stereo_position = (SOUND_MAX_LEFT +
14122                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14123                      (SCR_FIELDX + 2 * max_distance));
14124
14125   if (IS_LOOP_SOUND(nr))
14126   {
14127     /* This assures that quieter loop sounds do not overwrite louder ones,
14128        while restarting sound volume comparison with each new game frame. */
14129
14130     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14131       return;
14132
14133     loop_sound_volume[nr] = volume;
14134     loop_sound_frame[nr] = FrameCounter;
14135   }
14136
14137   PlaySoundExt(nr, volume, stereo_position, type);
14138 }
14139
14140 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14141 {
14142   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14143                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14144                  y < LEVELY(BY1) ? LEVELY(BY1) :
14145                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14146                  sound_action);
14147 }
14148
14149 static void PlayLevelSoundAction(int x, int y, int action)
14150 {
14151   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14152 }
14153
14154 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14155 {
14156   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14157
14158   if (sound_effect != SND_UNDEFINED)
14159     PlayLevelSound(x, y, sound_effect);
14160 }
14161
14162 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14163                                               int action)
14164 {
14165   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14166
14167   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14168     PlayLevelSound(x, y, sound_effect);
14169 }
14170
14171 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14172 {
14173   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14174
14175   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14176     PlayLevelSound(x, y, sound_effect);
14177 }
14178
14179 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14180 {
14181   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14182
14183   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14184     StopSound(sound_effect);
14185 }
14186
14187 static void PlayLevelMusic()
14188 {
14189   if (levelset.music[level_nr] != MUS_UNDEFINED)
14190     PlayMusic(levelset.music[level_nr]);        /* from config file */
14191   else
14192     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14193 }
14194
14195 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14196 {
14197   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14198   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14199   int x = xx - 1 - offset;
14200   int y = yy - 1 - offset;
14201
14202   switch (sample)
14203   {
14204     case SAMPLE_blank:
14205       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14206       break;
14207
14208     case SAMPLE_roll:
14209       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14210       break;
14211
14212     case SAMPLE_stone:
14213       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14214       break;
14215
14216     case SAMPLE_nut:
14217       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14218       break;
14219
14220     case SAMPLE_crack:
14221       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14222       break;
14223
14224     case SAMPLE_bug:
14225       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14226       break;
14227
14228     case SAMPLE_tank:
14229       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14230       break;
14231
14232     case SAMPLE_android_clone:
14233       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14234       break;
14235
14236     case SAMPLE_android_move:
14237       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14238       break;
14239
14240     case SAMPLE_spring:
14241       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14242       break;
14243
14244     case SAMPLE_slurp:
14245       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14246       break;
14247
14248     case SAMPLE_eater:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14250       break;
14251
14252     case SAMPLE_eater_eat:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14254       break;
14255
14256     case SAMPLE_alien:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14258       break;
14259
14260     case SAMPLE_collect:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14262       break;
14263
14264     case SAMPLE_diamond:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14266       break;
14267
14268     case SAMPLE_squash:
14269       /* !!! CHECK THIS !!! */
14270 #if 1
14271       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14272 #else
14273       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14274 #endif
14275       break;
14276
14277     case SAMPLE_wonderfall:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14279       break;
14280
14281     case SAMPLE_drip:
14282       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14283       break;
14284
14285     case SAMPLE_push:
14286       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14287       break;
14288
14289     case SAMPLE_dirt:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14291       break;
14292
14293     case SAMPLE_acid:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14295       break;
14296
14297     case SAMPLE_ball:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14299       break;
14300
14301     case SAMPLE_grow:
14302       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14303       break;
14304
14305     case SAMPLE_wonder:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14307       break;
14308
14309     case SAMPLE_door:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14311       break;
14312
14313     case SAMPLE_exit_open:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14315       break;
14316
14317     case SAMPLE_exit_leave:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14319       break;
14320
14321     case SAMPLE_dynamite:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14323       break;
14324
14325     case SAMPLE_tick:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14327       break;
14328
14329     case SAMPLE_press:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14331       break;
14332
14333     case SAMPLE_wheel:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14335       break;
14336
14337     case SAMPLE_boom:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14339       break;
14340
14341     case SAMPLE_die:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14343       break;
14344
14345     case SAMPLE_time:
14346       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14347       break;
14348
14349     default:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14351       break;
14352   }
14353 }
14354
14355 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14356 {
14357   int element = map_element_SP_to_RND(element_sp);
14358   int action = map_action_SP_to_RND(action_sp);
14359   int offset = (setup.sp_show_border_elements ? 0 : 1);
14360   int x = xx - offset;
14361   int y = yy - offset;
14362
14363   PlayLevelSoundElementAction(x, y, element, action);
14364 }
14365
14366 void RaiseScore(int value)
14367 {
14368   local_player->score += value;
14369
14370   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14371
14372   DisplayGameControlValues();
14373 }
14374
14375 void RaiseScoreElement(int element)
14376 {
14377   switch (element)
14378   {
14379     case EL_EMERALD:
14380     case EL_BD_DIAMOND:
14381     case EL_EMERALD_YELLOW:
14382     case EL_EMERALD_RED:
14383     case EL_EMERALD_PURPLE:
14384     case EL_SP_INFOTRON:
14385       RaiseScore(level.score[SC_EMERALD]);
14386       break;
14387     case EL_DIAMOND:
14388       RaiseScore(level.score[SC_DIAMOND]);
14389       break;
14390     case EL_CRYSTAL:
14391       RaiseScore(level.score[SC_CRYSTAL]);
14392       break;
14393     case EL_PEARL:
14394       RaiseScore(level.score[SC_PEARL]);
14395       break;
14396     case EL_BUG:
14397     case EL_BD_BUTTERFLY:
14398     case EL_SP_ELECTRON:
14399       RaiseScore(level.score[SC_BUG]);
14400       break;
14401     case EL_SPACESHIP:
14402     case EL_BD_FIREFLY:
14403     case EL_SP_SNIKSNAK:
14404       RaiseScore(level.score[SC_SPACESHIP]);
14405       break;
14406     case EL_YAMYAM:
14407     case EL_DARK_YAMYAM:
14408       RaiseScore(level.score[SC_YAMYAM]);
14409       break;
14410     case EL_ROBOT:
14411       RaiseScore(level.score[SC_ROBOT]);
14412       break;
14413     case EL_PACMAN:
14414       RaiseScore(level.score[SC_PACMAN]);
14415       break;
14416     case EL_NUT:
14417       RaiseScore(level.score[SC_NUT]);
14418       break;
14419     case EL_DYNAMITE:
14420     case EL_EM_DYNAMITE:
14421     case EL_SP_DISK_RED:
14422     case EL_DYNABOMB_INCREASE_NUMBER:
14423     case EL_DYNABOMB_INCREASE_SIZE:
14424     case EL_DYNABOMB_INCREASE_POWER:
14425       RaiseScore(level.score[SC_DYNAMITE]);
14426       break;
14427     case EL_SHIELD_NORMAL:
14428     case EL_SHIELD_DEADLY:
14429       RaiseScore(level.score[SC_SHIELD]);
14430       break;
14431     case EL_EXTRA_TIME:
14432       RaiseScore(level.extra_time_score);
14433       break;
14434     case EL_KEY_1:
14435     case EL_KEY_2:
14436     case EL_KEY_3:
14437     case EL_KEY_4:
14438     case EL_EM_KEY_1:
14439     case EL_EM_KEY_2:
14440     case EL_EM_KEY_3:
14441     case EL_EM_KEY_4:
14442     case EL_EMC_KEY_5:
14443     case EL_EMC_KEY_6:
14444     case EL_EMC_KEY_7:
14445     case EL_EMC_KEY_8:
14446     case EL_DC_KEY_WHITE:
14447       RaiseScore(level.score[SC_KEY]);
14448       break;
14449     default:
14450       RaiseScore(element_info[element].collect_score);
14451       break;
14452   }
14453 }
14454
14455 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14456 {
14457   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14458   {
14459     /* closing door required in case of envelope style request dialogs */
14460     if (!skip_request)
14461       CloseDoor(DOOR_CLOSE_1);
14462
14463 #if defined(NETWORK_AVALIABLE)
14464     if (options.network)
14465       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14466     else
14467 #endif
14468     {
14469       if (quick_quit)
14470       {
14471         FadeSkipNextFadeIn();
14472
14473         game_status = GAME_MODE_MAIN;
14474
14475         DrawAndFadeInMainMenu(REDRAW_FIELD);
14476       }
14477       else
14478       {
14479         game_status = GAME_MODE_MAIN;
14480
14481         DrawAndFadeInMainMenu(REDRAW_FIELD);
14482       }
14483     }
14484   }
14485   else          /* continue playing the game */
14486   {
14487     if (tape.playing && tape.deactivate_display)
14488       TapeDeactivateDisplayOff(TRUE);
14489
14490     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14491
14492     if (tape.playing && tape.deactivate_display)
14493       TapeDeactivateDisplayOn();
14494   }
14495 }
14496
14497 void RequestQuitGame(boolean ask_if_really_quit)
14498 {
14499   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14500   boolean skip_request = AllPlayersGone || quick_quit;
14501
14502   RequestQuitGameExt(skip_request, quick_quit,
14503                      "Do you really want to quit the game?");
14504 }
14505
14506
14507 /* ------------------------------------------------------------------------- */
14508 /* random generator functions                                                */
14509 /* ------------------------------------------------------------------------- */
14510
14511 unsigned int InitEngineRandom_RND(int seed)
14512 {
14513   game.num_random_calls = 0;
14514
14515   return InitEngineRandom(seed);
14516 }
14517
14518 unsigned int RND(int max)
14519 {
14520   if (max > 0)
14521   {
14522     game.num_random_calls++;
14523
14524     return GetEngineRandom(max);
14525   }
14526
14527   return 0;
14528 }
14529
14530
14531 /* ------------------------------------------------------------------------- */
14532 /* game engine snapshot handling functions                                   */
14533 /* ------------------------------------------------------------------------- */
14534
14535 struct EngineSnapshotInfo
14536 {
14537   /* runtime values for custom element collect score */
14538   int collect_score[NUM_CUSTOM_ELEMENTS];
14539
14540   /* runtime values for group element choice position */
14541   int choice_pos[NUM_GROUP_ELEMENTS];
14542
14543   /* runtime values for belt position animations */
14544   int belt_graphic[4][NUM_BELT_PARTS];
14545   int belt_anim_mode[4][NUM_BELT_PARTS];
14546 };
14547
14548 static struct EngineSnapshotInfo engine_snapshot_rnd;
14549 static char *snapshot_level_identifier = NULL;
14550 static int snapshot_level_nr = -1;
14551
14552 static void SaveEngineSnapshotValues_RND()
14553 {
14554   static int belt_base_active_element[4] =
14555   {
14556     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14557     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14558     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14559     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14560   };
14561   int i, j;
14562
14563   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14564   {
14565     int element = EL_CUSTOM_START + i;
14566
14567     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14568   }
14569
14570   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14571   {
14572     int element = EL_GROUP_START + i;
14573
14574     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14575   }
14576
14577   for (i = 0; i < 4; i++)
14578   {
14579     for (j = 0; j < NUM_BELT_PARTS; j++)
14580     {
14581       int element = belt_base_active_element[i] + j;
14582       int graphic = el2img(element);
14583       int anim_mode = graphic_info[graphic].anim_mode;
14584
14585       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14586       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14587     }
14588   }
14589 }
14590
14591 static void LoadEngineSnapshotValues_RND()
14592 {
14593   unsigned int num_random_calls = game.num_random_calls;
14594   int i, j;
14595
14596   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14597   {
14598     int element = EL_CUSTOM_START + i;
14599
14600     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14601   }
14602
14603   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14604   {
14605     int element = EL_GROUP_START + i;
14606
14607     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14608   }
14609
14610   for (i = 0; i < 4; i++)
14611   {
14612     for (j = 0; j < NUM_BELT_PARTS; j++)
14613     {
14614       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14615       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14616
14617       graphic_info[graphic].anim_mode = anim_mode;
14618     }
14619   }
14620
14621   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14622   {
14623     InitRND(tape.random_seed);
14624     for (i = 0; i < num_random_calls; i++)
14625       RND(1);
14626   }
14627
14628   if (game.num_random_calls != num_random_calls)
14629   {
14630     Error(ERR_INFO, "number of random calls out of sync");
14631     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14632     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14633     Error(ERR_EXIT, "this should not happen -- please debug");
14634   }
14635 }
14636
14637 void FreeEngineSnapshotSingle()
14638 {
14639   FreeSnapshotSingle();
14640
14641   setString(&snapshot_level_identifier, NULL);
14642   snapshot_level_nr = -1;
14643 }
14644
14645 void FreeEngineSnapshotList()
14646 {
14647   FreeSnapshotList();
14648 }
14649
14650 ListNode *SaveEngineSnapshotBuffers()
14651 {
14652   ListNode *buffers = NULL;
14653
14654   /* copy some special values to a structure better suited for the snapshot */
14655
14656   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14657     SaveEngineSnapshotValues_RND();
14658   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14659     SaveEngineSnapshotValues_EM();
14660   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14661     SaveEngineSnapshotValues_SP(&buffers);
14662
14663   /* save values stored in special snapshot structure */
14664
14665   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14666     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14667   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14668     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14669   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14670     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14671
14672   /* save further RND engine values */
14673
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14677
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14682
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14688
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14692
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14694
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14696
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14699
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14721
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14725
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14728
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14734
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14737
14738 #if 0
14739   ListNode *node = engine_snapshot_list_rnd;
14740   int num_bytes = 0;
14741
14742   while (node != NULL)
14743   {
14744     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14745
14746     node = node->next;
14747   }
14748
14749   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14750 #endif
14751
14752   return buffers;
14753 }
14754
14755 void SaveEngineSnapshotSingle()
14756 {
14757   ListNode *buffers = SaveEngineSnapshotBuffers();
14758
14759   /* finally save all snapshot buffers to single snapshot */
14760   SaveSnapshotSingle(buffers);
14761
14762   /* save level identification information */
14763   setString(&snapshot_level_identifier, leveldir_current->identifier);
14764   snapshot_level_nr = level_nr;
14765 }
14766
14767 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14768 {
14769   boolean save_snapshot =
14770     (initial_snapshot ||
14771      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14772      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14773       game.snapshot.changed_action));
14774
14775   game.snapshot.changed_action = FALSE;
14776
14777   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14778       tape.quick_resume ||
14779       !save_snapshot)
14780     return FALSE;
14781
14782   ListNode *buffers = SaveEngineSnapshotBuffers();
14783
14784   /* finally save all snapshot buffers to snapshot list */
14785   SaveSnapshotToList(buffers);
14786
14787   return TRUE;
14788 }
14789
14790 boolean SaveEngineSnapshotToList()
14791 {
14792   return SaveEngineSnapshotToListExt(FALSE);
14793 }
14794
14795 void SaveEngineSnapshotToListInitial()
14796 {
14797   FreeEngineSnapshotList();
14798
14799   SaveEngineSnapshotToListExt(TRUE);
14800 }
14801
14802 void LoadEngineSnapshotValues()
14803 {
14804   /* restore special values from snapshot structure */
14805
14806   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14807     LoadEngineSnapshotValues_RND();
14808   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14809     LoadEngineSnapshotValues_EM();
14810   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14811     LoadEngineSnapshotValues_SP();
14812 }
14813
14814 void LoadEngineSnapshotSingle()
14815 {
14816   LoadSnapshotSingle();
14817
14818   LoadEngineSnapshotValues();
14819 }
14820
14821 void LoadEngineSnapshot_Undo(int steps)
14822 {
14823   LoadSnapshotFromList_Older(steps);
14824
14825   LoadEngineSnapshotValues();
14826 }
14827
14828 void LoadEngineSnapshot_Redo(int steps)
14829 {
14830   LoadSnapshotFromList_Newer(steps);
14831
14832   LoadEngineSnapshotValues();
14833 }
14834
14835 boolean CheckEngineSnapshotSingle()
14836 {
14837   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14838           snapshot_level_nr == level_nr);
14839 }
14840
14841 boolean CheckEngineSnapshotList()
14842 {
14843   return CheckSnapshotList();
14844 }
14845
14846
14847 /* ---------- new game button stuff ---------------------------------------- */
14848
14849 static struct
14850 {
14851   int graphic;
14852   struct XY *pos;
14853   int gadget_id;
14854   char *infotext;
14855 } gamebutton_info[NUM_GAME_BUTTONS] =
14856 {
14857   {
14858     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14859     GAME_CTRL_ID_STOP,                  "stop game"
14860   },
14861   {
14862     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14863     GAME_CTRL_ID_PAUSE,                 "pause game"
14864   },
14865   {
14866     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14867     GAME_CTRL_ID_PLAY,                  "play game"
14868   },
14869   {
14870     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14871     GAME_CTRL_ID_UNDO,                  "undo step"
14872   },
14873   {
14874     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14875     GAME_CTRL_ID_REDO,                  "redo step"
14876   },
14877   {
14878     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14879     GAME_CTRL_ID_SAVE,                  "save game"
14880   },
14881   {
14882     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14883     GAME_CTRL_ID_LOAD,                  "load game"
14884   },
14885   {
14886     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14887     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14888   },
14889   {
14890     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14891     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14892   },
14893   {
14894     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14895     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14896   }
14897 };
14898
14899 void CreateGameButtons()
14900 {
14901   int i;
14902
14903   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14904   {
14905     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14906     struct XY *pos = gamebutton_info[i].pos;
14907     struct GadgetInfo *gi;
14908     int button_type;
14909     boolean checked;
14910     unsigned int event_mask;
14911     int base_x = (tape.show_game_buttons ? VX : DX);
14912     int base_y = (tape.show_game_buttons ? VY : DY);
14913     int gd_x   = gfx->src_x;
14914     int gd_y   = gfx->src_y;
14915     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14916     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14917     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14918     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14919     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14920     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14921     int id = i;
14922
14923     if (gfx->bitmap == NULL)
14924     {
14925       game_gadget[id] = NULL;
14926
14927       continue;
14928     }
14929
14930     if (id == GAME_CTRL_ID_STOP ||
14931         id == GAME_CTRL_ID_PAUSE ||
14932         id == GAME_CTRL_ID_PLAY ||
14933         id == GAME_CTRL_ID_SAVE ||
14934         id == GAME_CTRL_ID_LOAD)
14935     {
14936       button_type = GD_TYPE_NORMAL_BUTTON;
14937       checked = FALSE;
14938       event_mask = GD_EVENT_RELEASED;
14939     }
14940     else if (id == GAME_CTRL_ID_UNDO ||
14941              id == GAME_CTRL_ID_REDO)
14942     {
14943       button_type = GD_TYPE_NORMAL_BUTTON;
14944       checked = FALSE;
14945       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14946     }
14947     else
14948     {
14949       button_type = GD_TYPE_CHECK_BUTTON;
14950       checked =
14951         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14952          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14953          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14954       event_mask = GD_EVENT_PRESSED;
14955     }
14956
14957     gi = CreateGadget(GDI_CUSTOM_ID, id,
14958                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14959                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14960                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14961                       GDI_WIDTH, gfx->width,
14962                       GDI_HEIGHT, gfx->height,
14963                       GDI_TYPE, button_type,
14964                       GDI_STATE, GD_BUTTON_UNPRESSED,
14965                       GDI_CHECKED, checked,
14966                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14967                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14968                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14969                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14970                       GDI_DIRECT_DRAW, FALSE,
14971                       GDI_EVENT_MASK, event_mask,
14972                       GDI_CALLBACK_ACTION, HandleGameButtons,
14973                       GDI_END);
14974
14975     if (gi == NULL)
14976       Error(ERR_EXIT, "cannot create gadget");
14977
14978     game_gadget[id] = gi;
14979   }
14980 }
14981
14982 void FreeGameButtons()
14983 {
14984   int i;
14985
14986   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14987     FreeGadget(game_gadget[i]);
14988 }
14989
14990 static void MapGameButtonsAtSamePosition(int id)
14991 {
14992   int i;
14993
14994   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14995     if (i != id &&
14996         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14997         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14998       MapGadget(game_gadget[i]);
14999 }
15000
15001 static void UnmapGameButtonsAtSamePosition(int id)
15002 {
15003   int i;
15004
15005   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15006     if (i != id &&
15007         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15008         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15009       UnmapGadget(game_gadget[i]);
15010 }
15011
15012 void MapUndoRedoButtons()
15013 {
15014   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15015   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15016   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15017
15018   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15019   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15020   MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
15021 }
15022
15023 void UnmapUndoRedoButtons()
15024 {
15025   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15026   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15027   UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
15028
15029   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15030   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15031   MapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15032 }
15033
15034 void MapGameButtons()
15035 {
15036   int i;
15037
15038   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15039     if (i != GAME_CTRL_ID_UNDO &&
15040         i != GAME_CTRL_ID_REDO &&
15041         i != GAME_CTRL_ID_PLAY)
15042       MapGadget(game_gadget[i]);
15043 }
15044
15045 void UnmapGameButtons()
15046 {
15047   int i;
15048
15049   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15050     UnmapGadget(game_gadget[i]);
15051 }
15052
15053 void RedrawGameButtons()
15054 {
15055   int i;
15056
15057   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15058     RedrawGadget(game_gadget[i]);
15059
15060   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15061   redraw_mask &= ~REDRAW_ALL;
15062 }
15063
15064 void GameUndoRedoExt()
15065 {
15066   ClearPlayerAction();
15067
15068   tape.pausing = TRUE;
15069
15070   RedrawPlayfield();
15071   UpdateAndDisplayGameControlValues();
15072
15073   DrawCompleteVideoDisplay();
15074   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15075   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15076   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15077                     VIDEO_STATE_1STEP_OFF), 0);
15078
15079   BackToFront();
15080 }
15081
15082 void GameUndo(int steps)
15083 {
15084   if (!CheckEngineSnapshotList())
15085     return;
15086
15087   LoadEngineSnapshot_Undo(steps);
15088
15089   GameUndoRedoExt();
15090 }
15091
15092 void GameRedo(int steps)
15093 {
15094   if (!CheckEngineSnapshotList())
15095     return;
15096
15097   LoadEngineSnapshot_Redo(steps);
15098
15099   GameUndoRedoExt();
15100 }
15101
15102 static void HandleGameButtonsExt(int id, int button)
15103 {
15104   int steps = BUTTON_STEPSIZE(button);
15105   boolean handle_game_buttons =
15106     (game_status == GAME_MODE_PLAYING ||
15107      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15108
15109   if (!handle_game_buttons)
15110     return;
15111
15112   switch (id)
15113   {
15114     case GAME_CTRL_ID_STOP:
15115       if (game_status == GAME_MODE_MAIN)
15116         break;
15117
15118       if (tape.playing)
15119         TapeStop();
15120       else
15121         RequestQuitGame(TRUE);
15122
15123       break;
15124
15125     case GAME_CTRL_ID_PAUSE:
15126       if (options.network && game_status == GAME_MODE_PLAYING)
15127       {
15128 #if defined(NETWORK_AVALIABLE)
15129         if (tape.pausing)
15130           SendToServer_ContinuePlaying();
15131         else
15132           SendToServer_PausePlaying();
15133 #endif
15134       }
15135       else
15136         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15137       break;
15138
15139     case GAME_CTRL_ID_PLAY:
15140       if (game_status == GAME_MODE_MAIN)
15141       {
15142         StartGameActions(options.network, setup.autorecord, level.random_seed);
15143       }
15144       else if (tape.pausing)
15145       {
15146 #if defined(NETWORK_AVALIABLE)
15147         if (options.network)
15148           SendToServer_ContinuePlaying();
15149         else
15150 #endif
15151           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15152       }
15153       break;
15154
15155     case GAME_CTRL_ID_UNDO:
15156       GameUndo(steps);
15157       break;
15158
15159     case GAME_CTRL_ID_REDO:
15160       GameRedo(steps);
15161       break;
15162
15163     case GAME_CTRL_ID_SAVE:
15164       TapeQuickSave();
15165       break;
15166
15167     case GAME_CTRL_ID_LOAD:
15168       TapeQuickLoad();
15169       break;
15170
15171     case SOUND_CTRL_ID_MUSIC:
15172       if (setup.sound_music)
15173       { 
15174         setup.sound_music = FALSE;
15175
15176         FadeMusic();
15177       }
15178       else if (audio.music_available)
15179       { 
15180         setup.sound = setup.sound_music = TRUE;
15181
15182         SetAudioMode(setup.sound);
15183
15184         PlayLevelMusic();
15185       }
15186       break;
15187
15188     case SOUND_CTRL_ID_LOOPS:
15189       if (setup.sound_loops)
15190         setup.sound_loops = FALSE;
15191       else if (audio.loops_available)
15192       {
15193         setup.sound = setup.sound_loops = TRUE;
15194
15195         SetAudioMode(setup.sound);
15196       }
15197       break;
15198
15199     case SOUND_CTRL_ID_SIMPLE:
15200       if (setup.sound_simple)
15201         setup.sound_simple = FALSE;
15202       else if (audio.sound_available)
15203       {
15204         setup.sound = setup.sound_simple = TRUE;
15205
15206         SetAudioMode(setup.sound);
15207       }
15208       break;
15209
15210     default:
15211       break;
15212   }
15213 }
15214
15215 static void HandleGameButtons(struct GadgetInfo *gi)
15216 {
15217   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15218 }
15219
15220 void HandleSoundButtonKeys(Key key)
15221 {
15222
15223   if (key == setup.shortcut.sound_simple)
15224     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15225   else if (key == setup.shortcut.sound_loops)
15226     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15227   else if (key == setup.shortcut.sound_music)
15228     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15229 }