added optionally configurable game frame counter to tape display
[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 "files.h"
19 #include "tape.h"
20 #include "network.h"
21
22
23 /* DEBUG SETTINGS */
24 #define DEBUG_INIT_PLAYER       1
25 #define DEBUG_PLAYER_ACTIONS    0
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
32 #define USE_QUICKSAND_IMPACT_BUGFIX     0
33 #define USE_DELAYED_GFX_REDRAW          0
34 #define USE_NEW_PLAYER_ASSIGNMENTS      1
35
36 #if USE_DELAYED_GFX_REDRAW
37 #define TEST_DrawLevelField(x, y)                               \
38         GfxRedraw[x][y] |= GFX_REDRAW_TILE
39 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
41 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
43 #define TEST_DrawTwinkleOnField(x, y)                           \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
45 #else
46 #define TEST_DrawLevelField(x, y)                               \
47              DrawLevelField(x, y)
48 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
49              DrawLevelFieldCrumbled(x, y)
50 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
51              DrawLevelFieldCrumbledNeighbours(x, y)
52 #define TEST_DrawTwinkleOnField(x, y)                           \
53              DrawTwinkleOnField(x, y)
54 #endif
55
56
57 /* for DigField() */
58 #define DF_NO_PUSH              0
59 #define DF_DIG                  1
60 #define DF_SNAP                 2
61
62 /* for MovePlayer() */
63 #define MP_NO_ACTION            0
64 #define MP_MOVING               1
65 #define MP_ACTION               2
66 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
67
68 /* for ScrollPlayer() */
69 #define SCROLL_INIT             0
70 #define SCROLL_GO_ON            1
71
72 /* for Bang()/Explode() */
73 #define EX_PHASE_START          0
74 #define EX_TYPE_NONE            0
75 #define EX_TYPE_NORMAL          (1 << 0)
76 #define EX_TYPE_CENTER          (1 << 1)
77 #define EX_TYPE_BORDER          (1 << 2)
78 #define EX_TYPE_CROSS           (1 << 3)
79 #define EX_TYPE_DYNA            (1 << 4)
80 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
81
82 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
83 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
84 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
85 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
86
87 /* game panel display and control definitions */
88 #define GAME_PANEL_LEVEL_NUMBER                 0
89 #define GAME_PANEL_GEMS                         1
90 #define GAME_PANEL_INVENTORY_COUNT              2
91 #define GAME_PANEL_INVENTORY_FIRST_1            3
92 #define GAME_PANEL_INVENTORY_FIRST_2            4
93 #define GAME_PANEL_INVENTORY_FIRST_3            5
94 #define GAME_PANEL_INVENTORY_FIRST_4            6
95 #define GAME_PANEL_INVENTORY_FIRST_5            7
96 #define GAME_PANEL_INVENTORY_FIRST_6            8
97 #define GAME_PANEL_INVENTORY_FIRST_7            9
98 #define GAME_PANEL_INVENTORY_FIRST_8            10
99 #define GAME_PANEL_INVENTORY_LAST_1             11
100 #define GAME_PANEL_INVENTORY_LAST_2             12
101 #define GAME_PANEL_INVENTORY_LAST_3             13
102 #define GAME_PANEL_INVENTORY_LAST_4             14
103 #define GAME_PANEL_INVENTORY_LAST_5             15
104 #define GAME_PANEL_INVENTORY_LAST_6             16
105 #define GAME_PANEL_INVENTORY_LAST_7             17
106 #define GAME_PANEL_INVENTORY_LAST_8             18
107 #define GAME_PANEL_KEY_1                        19
108 #define GAME_PANEL_KEY_2                        20
109 #define GAME_PANEL_KEY_3                        21
110 #define GAME_PANEL_KEY_4                        22
111 #define GAME_PANEL_KEY_5                        23
112 #define GAME_PANEL_KEY_6                        24
113 #define GAME_PANEL_KEY_7                        25
114 #define GAME_PANEL_KEY_8                        26
115 #define GAME_PANEL_KEY_WHITE                    27
116 #define GAME_PANEL_KEY_WHITE_COUNT              28
117 #define GAME_PANEL_SCORE                        29
118 #define GAME_PANEL_HIGHSCORE                    30
119 #define GAME_PANEL_TIME                         31
120 #define GAME_PANEL_TIME_HH                      32
121 #define GAME_PANEL_TIME_MM                      33
122 #define GAME_PANEL_TIME_SS                      34
123 #define GAME_PANEL_FRAME                        35
124 #define GAME_PANEL_SHIELD_NORMAL                36
125 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
126 #define GAME_PANEL_SHIELD_DEADLY                38
127 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
128 #define GAME_PANEL_EXIT                         40
129 #define GAME_PANEL_EMC_MAGIC_BALL               41
130 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
131 #define GAME_PANEL_LIGHT_SWITCH                 43
132 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
133 #define GAME_PANEL_TIMEGATE_SWITCH              45
134 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
135 #define GAME_PANEL_SWITCHGATE_SWITCH            47
136 #define GAME_PANEL_EMC_LENSES                   48
137 #define GAME_PANEL_EMC_LENSES_TIME              49
138 #define GAME_PANEL_EMC_MAGNIFIER                50
139 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
140 #define GAME_PANEL_BALLOON_SWITCH               52
141 #define GAME_PANEL_DYNABOMB_NUMBER              53
142 #define GAME_PANEL_DYNABOMB_SIZE                54
143 #define GAME_PANEL_DYNABOMB_POWER               55
144 #define GAME_PANEL_PENGUINS                     56
145 #define GAME_PANEL_SOKOBAN_OBJECTS              57
146 #define GAME_PANEL_SOKOBAN_FIELDS               58
147 #define GAME_PANEL_ROBOT_WHEEL                  59
148 #define GAME_PANEL_CONVEYOR_BELT_1              60
149 #define GAME_PANEL_CONVEYOR_BELT_2              61
150 #define GAME_PANEL_CONVEYOR_BELT_3              62
151 #define GAME_PANEL_CONVEYOR_BELT_4              63
152 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
153 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
154 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
155 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
156 #define GAME_PANEL_MAGIC_WALL                   68
157 #define GAME_PANEL_MAGIC_WALL_TIME              69
158 #define GAME_PANEL_GRAVITY_STATE                70
159 #define GAME_PANEL_GRAPHIC_1                    71
160 #define GAME_PANEL_GRAPHIC_2                    72
161 #define GAME_PANEL_GRAPHIC_3                    73
162 #define GAME_PANEL_GRAPHIC_4                    74
163 #define GAME_PANEL_GRAPHIC_5                    75
164 #define GAME_PANEL_GRAPHIC_6                    76
165 #define GAME_PANEL_GRAPHIC_7                    77
166 #define GAME_PANEL_GRAPHIC_8                    78
167 #define GAME_PANEL_ELEMENT_1                    79
168 #define GAME_PANEL_ELEMENT_2                    80
169 #define GAME_PANEL_ELEMENT_3                    81
170 #define GAME_PANEL_ELEMENT_4                    82
171 #define GAME_PANEL_ELEMENT_5                    83
172 #define GAME_PANEL_ELEMENT_6                    84
173 #define GAME_PANEL_ELEMENT_7                    85
174 #define GAME_PANEL_ELEMENT_8                    86
175 #define GAME_PANEL_ELEMENT_COUNT_1              87
176 #define GAME_PANEL_ELEMENT_COUNT_2              88
177 #define GAME_PANEL_ELEMENT_COUNT_3              89
178 #define GAME_PANEL_ELEMENT_COUNT_4              90
179 #define GAME_PANEL_ELEMENT_COUNT_5              91
180 #define GAME_PANEL_ELEMENT_COUNT_6              92
181 #define GAME_PANEL_ELEMENT_COUNT_7              93
182 #define GAME_PANEL_ELEMENT_COUNT_8              94
183 #define GAME_PANEL_CE_SCORE_1                   95
184 #define GAME_PANEL_CE_SCORE_2                   96
185 #define GAME_PANEL_CE_SCORE_3                   97
186 #define GAME_PANEL_CE_SCORE_4                   98
187 #define GAME_PANEL_CE_SCORE_5                   99
188 #define GAME_PANEL_CE_SCORE_6                   100
189 #define GAME_PANEL_CE_SCORE_7                   101
190 #define GAME_PANEL_CE_SCORE_8                   102
191 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
192 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
193 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
194 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
195 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
196 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
197 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
198 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
199 #define GAME_PANEL_PLAYER_NAME                  111
200 #define GAME_PANEL_LEVEL_NAME                   112
201 #define GAME_PANEL_LEVEL_AUTHOR                 113
202
203 #define NUM_GAME_PANEL_CONTROLS                 114
204
205 struct GamePanelOrderInfo
206 {
207   int nr;
208   int sort_priority;
209 };
210
211 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
212
213 struct GamePanelControlInfo
214 {
215   int nr;
216
217   struct TextPosInfo *pos;
218   int type;
219
220   int value, last_value;
221   int frame, last_frame;
222   int gfx_frame;
223   int gfx_random;
224 };
225
226 static struct GamePanelControlInfo game_panel_controls[] =
227 {
228   {
229     GAME_PANEL_LEVEL_NUMBER,
230     &game.panel.level_number,
231     TYPE_INTEGER,
232   },
233   {
234     GAME_PANEL_GEMS,
235     &game.panel.gems,
236     TYPE_INTEGER,
237   },
238   {
239     GAME_PANEL_INVENTORY_COUNT,
240     &game.panel.inventory_count,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_INVENTORY_FIRST_1,
245     &game.panel.inventory_first[0],
246     TYPE_ELEMENT,
247   },
248   {
249     GAME_PANEL_INVENTORY_FIRST_2,
250     &game.panel.inventory_first[1],
251     TYPE_ELEMENT,
252   },
253   {
254     GAME_PANEL_INVENTORY_FIRST_3,
255     &game.panel.inventory_first[2],
256     TYPE_ELEMENT,
257   },
258   {
259     GAME_PANEL_INVENTORY_FIRST_4,
260     &game.panel.inventory_first[3],
261     TYPE_ELEMENT,
262   },
263   {
264     GAME_PANEL_INVENTORY_FIRST_5,
265     &game.panel.inventory_first[4],
266     TYPE_ELEMENT,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_6,
270     &game.panel.inventory_first[5],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_7,
275     &game.panel.inventory_first[6],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_8,
280     &game.panel.inventory_first[7],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_LAST_1,
285     &game.panel.inventory_last[0],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_LAST_2,
290     &game.panel.inventory_last[1],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_LAST_3,
295     &game.panel.inventory_last[2],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_LAST_4,
300     &game.panel.inventory_last[3],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_LAST_5,
305     &game.panel.inventory_last[4],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_6,
310     &game.panel.inventory_last[5],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_7,
315     &game.panel.inventory_last[6],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_8,
320     &game.panel.inventory_last[7],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_KEY_1,
325     &game.panel.key[0],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_KEY_2,
330     &game.panel.key[1],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_KEY_3,
335     &game.panel.key[2],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_KEY_4,
340     &game.panel.key[3],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_KEY_5,
345     &game.panel.key[4],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_6,
350     &game.panel.key[5],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_7,
355     &game.panel.key[6],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_8,
360     &game.panel.key[7],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_WHITE,
365     &game.panel.key_white,
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_WHITE_COUNT,
370     &game.panel.key_white_count,
371     TYPE_INTEGER,
372   },
373   {
374     GAME_PANEL_SCORE,
375     &game.panel.score,
376     TYPE_INTEGER,
377   },
378   {
379     GAME_PANEL_HIGHSCORE,
380     &game.panel.highscore,
381     TYPE_INTEGER,
382   },
383   {
384     GAME_PANEL_TIME,
385     &game.panel.time,
386     TYPE_INTEGER,
387   },
388   {
389     GAME_PANEL_TIME_HH,
390     &game.panel.time_hh,
391     TYPE_INTEGER,
392   },
393   {
394     GAME_PANEL_TIME_MM,
395     &game.panel.time_mm,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_TIME_SS,
400     &game.panel.time_ss,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_FRAME,
405     &game.panel.frame,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_SHIELD_NORMAL,
410     &game.panel.shield_normal,
411     TYPE_ELEMENT,
412   },
413   {
414     GAME_PANEL_SHIELD_NORMAL_TIME,
415     &game.panel.shield_normal_time,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_SHIELD_DEADLY,
420     &game.panel.shield_deadly,
421     TYPE_ELEMENT,
422   },
423   {
424     GAME_PANEL_SHIELD_DEADLY_TIME,
425     &game.panel.shield_deadly_time,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_EXIT,
430     &game.panel.exit,
431     TYPE_ELEMENT,
432   },
433   {
434     GAME_PANEL_EMC_MAGIC_BALL,
435     &game.panel.emc_magic_ball,
436     TYPE_ELEMENT,
437   },
438   {
439     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
440     &game.panel.emc_magic_ball_switch,
441     TYPE_ELEMENT,
442   },
443   {
444     GAME_PANEL_LIGHT_SWITCH,
445     &game.panel.light_switch,
446     TYPE_ELEMENT,
447   },
448   {
449     GAME_PANEL_LIGHT_SWITCH_TIME,
450     &game.panel.light_switch_time,
451     TYPE_INTEGER,
452   },
453   {
454     GAME_PANEL_TIMEGATE_SWITCH,
455     &game.panel.timegate_switch,
456     TYPE_ELEMENT,
457   },
458   {
459     GAME_PANEL_TIMEGATE_SWITCH_TIME,
460     &game.panel.timegate_switch_time,
461     TYPE_INTEGER,
462   },
463   {
464     GAME_PANEL_SWITCHGATE_SWITCH,
465     &game.panel.switchgate_switch,
466     TYPE_ELEMENT,
467   },
468   {
469     GAME_PANEL_EMC_LENSES,
470     &game.panel.emc_lenses,
471     TYPE_ELEMENT,
472   },
473   {
474     GAME_PANEL_EMC_LENSES_TIME,
475     &game.panel.emc_lenses_time,
476     TYPE_INTEGER,
477   },
478   {
479     GAME_PANEL_EMC_MAGNIFIER,
480     &game.panel.emc_magnifier,
481     TYPE_ELEMENT,
482   },
483   {
484     GAME_PANEL_EMC_MAGNIFIER_TIME,
485     &game.panel.emc_magnifier_time,
486     TYPE_INTEGER,
487   },
488   {
489     GAME_PANEL_BALLOON_SWITCH,
490     &game.panel.balloon_switch,
491     TYPE_ELEMENT,
492   },
493   {
494     GAME_PANEL_DYNABOMB_NUMBER,
495     &game.panel.dynabomb_number,
496     TYPE_INTEGER,
497   },
498   {
499     GAME_PANEL_DYNABOMB_SIZE,
500     &game.panel.dynabomb_size,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_DYNABOMB_POWER,
505     &game.panel.dynabomb_power,
506     TYPE_ELEMENT,
507   },
508   {
509     GAME_PANEL_PENGUINS,
510     &game.panel.penguins,
511     TYPE_INTEGER,
512   },
513   {
514     GAME_PANEL_SOKOBAN_OBJECTS,
515     &game.panel.sokoban_objects,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_SOKOBAN_FIELDS,
520     &game.panel.sokoban_fields,
521     TYPE_INTEGER,
522   },
523   {
524     GAME_PANEL_ROBOT_WHEEL,
525     &game.panel.robot_wheel,
526     TYPE_ELEMENT,
527   },
528   {
529     GAME_PANEL_CONVEYOR_BELT_1,
530     &game.panel.conveyor_belt[0],
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_CONVEYOR_BELT_2,
535     &game.panel.conveyor_belt[1],
536     TYPE_ELEMENT,
537   },
538   {
539     GAME_PANEL_CONVEYOR_BELT_3,
540     &game.panel.conveyor_belt[2],
541     TYPE_ELEMENT,
542   },
543   {
544     GAME_PANEL_CONVEYOR_BELT_4,
545     &game.panel.conveyor_belt[3],
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
550     &game.panel.conveyor_belt_switch[0],
551     TYPE_ELEMENT,
552   },
553   {
554     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
555     &game.panel.conveyor_belt_switch[1],
556     TYPE_ELEMENT,
557   },
558   {
559     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
560     &game.panel.conveyor_belt_switch[2],
561     TYPE_ELEMENT,
562   },
563   {
564     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
565     &game.panel.conveyor_belt_switch[3],
566     TYPE_ELEMENT,
567   },
568   {
569     GAME_PANEL_MAGIC_WALL,
570     &game.panel.magic_wall,
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_MAGIC_WALL_TIME,
575     &game.panel.magic_wall_time,
576     TYPE_INTEGER,
577   },
578   {
579     GAME_PANEL_GRAVITY_STATE,
580     &game.panel.gravity_state,
581     TYPE_STRING,
582   },
583   {
584     GAME_PANEL_GRAPHIC_1,
585     &game.panel.graphic[0],
586     TYPE_ELEMENT,
587   },
588   {
589     GAME_PANEL_GRAPHIC_2,
590     &game.panel.graphic[1],
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_GRAPHIC_3,
595     &game.panel.graphic[2],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_GRAPHIC_4,
600     &game.panel.graphic[3],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_GRAPHIC_5,
605     &game.panel.graphic[4],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_GRAPHIC_6,
610     &game.panel.graphic[5],
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_GRAPHIC_7,
615     &game.panel.graphic[6],
616     TYPE_ELEMENT,
617   },
618   {
619     GAME_PANEL_GRAPHIC_8,
620     &game.panel.graphic[7],
621     TYPE_ELEMENT,
622   },
623   {
624     GAME_PANEL_ELEMENT_1,
625     &game.panel.element[0],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_ELEMENT_2,
630     &game.panel.element[1],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_ELEMENT_3,
635     &game.panel.element[2],
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_ELEMENT_4,
640     &game.panel.element[3],
641     TYPE_ELEMENT,
642   },
643   {
644     GAME_PANEL_ELEMENT_5,
645     &game.panel.element[4],
646     TYPE_ELEMENT,
647   },
648   {
649     GAME_PANEL_ELEMENT_6,
650     &game.panel.element[5],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_ELEMENT_7,
655     &game.panel.element[6],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_ELEMENT_8,
660     &game.panel.element[7],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_ELEMENT_COUNT_1,
665     &game.panel.element_count[0],
666     TYPE_INTEGER,
667   },
668   {
669     GAME_PANEL_ELEMENT_COUNT_2,
670     &game.panel.element_count[1],
671     TYPE_INTEGER,
672   },
673   {
674     GAME_PANEL_ELEMENT_COUNT_3,
675     &game.panel.element_count[2],
676     TYPE_INTEGER,
677   },
678   {
679     GAME_PANEL_ELEMENT_COUNT_4,
680     &game.panel.element_count[3],
681     TYPE_INTEGER,
682   },
683   {
684     GAME_PANEL_ELEMENT_COUNT_5,
685     &game.panel.element_count[4],
686     TYPE_INTEGER,
687   },
688   {
689     GAME_PANEL_ELEMENT_COUNT_6,
690     &game.panel.element_count[5],
691     TYPE_INTEGER,
692   },
693   {
694     GAME_PANEL_ELEMENT_COUNT_7,
695     &game.panel.element_count[6],
696     TYPE_INTEGER,
697   },
698   {
699     GAME_PANEL_ELEMENT_COUNT_8,
700     &game.panel.element_count[7],
701     TYPE_INTEGER,
702   },
703   {
704     GAME_PANEL_CE_SCORE_1,
705     &game.panel.ce_score[0],
706     TYPE_INTEGER,
707   },
708   {
709     GAME_PANEL_CE_SCORE_2,
710     &game.panel.ce_score[1],
711     TYPE_INTEGER,
712   },
713   {
714     GAME_PANEL_CE_SCORE_3,
715     &game.panel.ce_score[2],
716     TYPE_INTEGER,
717   },
718   {
719     GAME_PANEL_CE_SCORE_4,
720     &game.panel.ce_score[3],
721     TYPE_INTEGER,
722   },
723   {
724     GAME_PANEL_CE_SCORE_5,
725     &game.panel.ce_score[4],
726     TYPE_INTEGER,
727   },
728   {
729     GAME_PANEL_CE_SCORE_6,
730     &game.panel.ce_score[5],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_CE_SCORE_7,
735     &game.panel.ce_score[6],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_CE_SCORE_8,
740     &game.panel.ce_score[7],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_CE_SCORE_1_ELEMENT,
745     &game.panel.ce_score_element[0],
746     TYPE_ELEMENT,
747   },
748   {
749     GAME_PANEL_CE_SCORE_2_ELEMENT,
750     &game.panel.ce_score_element[1],
751     TYPE_ELEMENT,
752   },
753   {
754     GAME_PANEL_CE_SCORE_3_ELEMENT,
755     &game.panel.ce_score_element[2],
756     TYPE_ELEMENT,
757   },
758   {
759     GAME_PANEL_CE_SCORE_4_ELEMENT,
760     &game.panel.ce_score_element[3],
761     TYPE_ELEMENT,
762   },
763   {
764     GAME_PANEL_CE_SCORE_5_ELEMENT,
765     &game.panel.ce_score_element[4],
766     TYPE_ELEMENT,
767   },
768   {
769     GAME_PANEL_CE_SCORE_6_ELEMENT,
770     &game.panel.ce_score_element[5],
771     TYPE_ELEMENT,
772   },
773   {
774     GAME_PANEL_CE_SCORE_7_ELEMENT,
775     &game.panel.ce_score_element[6],
776     TYPE_ELEMENT,
777   },
778   {
779     GAME_PANEL_CE_SCORE_8_ELEMENT,
780     &game.panel.ce_score_element[7],
781     TYPE_ELEMENT,
782   },
783   {
784     GAME_PANEL_PLAYER_NAME,
785     &game.panel.player_name,
786     TYPE_STRING,
787   },
788   {
789     GAME_PANEL_LEVEL_NAME,
790     &game.panel.level_name,
791     TYPE_STRING,
792   },
793   {
794     GAME_PANEL_LEVEL_AUTHOR,
795     &game.panel.level_author,
796     TYPE_STRING,
797   },
798
799   {
800     -1,
801     NULL,
802     -1,
803   }
804 };
805
806 /* values for delayed check of falling and moving elements and for collision */
807 #define CHECK_DELAY_MOVING      3
808 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
809 #define CHECK_DELAY_COLLISION   2
810 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
811
812 /* values for initial player move delay (initial delay counter value) */
813 #define INITIAL_MOVE_DELAY_OFF  -1
814 #define INITIAL_MOVE_DELAY_ON   0
815
816 /* values for player movement speed (which is in fact a delay value) */
817 #define MOVE_DELAY_MIN_SPEED    32
818 #define MOVE_DELAY_NORMAL_SPEED 8
819 #define MOVE_DELAY_HIGH_SPEED   4
820 #define MOVE_DELAY_MAX_SPEED    1
821
822 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
823 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
824
825 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
826 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
827
828 /* values for other actions */
829 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
830 #define MOVE_STEPSIZE_MIN       (1)
831 #define MOVE_STEPSIZE_MAX       (TILEX)
832
833 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
834 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
835
836 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
837
838 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
839                                  RND(element_info[e].push_delay_random))
840 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
841                                  RND(element_info[e].drop_delay_random))
842 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
843                                  RND(element_info[e].move_delay_random))
844 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
845                                     (element_info[e].move_delay_random))
846 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
847                                  RND(element_info[e].ce_value_random_initial))
848 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
849 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
850                                  RND((c)->delay_random * (c)->delay_frames))
851 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
852                                  RND((c)->delay_random))
853
854
855 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
856          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
857
858 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
859         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
860          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
861          (be) + (e) - EL_SELF)
862
863 #define GET_PLAYER_FROM_BITS(p)                                         \
864         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
865
866 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
867         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
868          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
869          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
870          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
871          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
872          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
873          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
874          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
875          (e))
876
877 #define CAN_GROW_INTO(e)                                                \
878         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
879
880 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
881                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
882                                         (condition)))
883
884 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
885                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
886                                         (CAN_MOVE_INTO_ACID(e) &&       \
887                                          Feld[x][y] == EL_ACID) ||      \
888                                         (condition)))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
892                                         (CAN_MOVE_INTO_ACID(e) &&       \
893                                          Feld[x][y] == EL_ACID) ||      \
894                                         (condition)))
895
896 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
897                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
898                                         (condition) ||                  \
899                                         (CAN_MOVE_INTO_ACID(e) &&       \
900                                          Feld[x][y] == EL_ACID) ||      \
901                                         (DONT_COLLIDE_WITH(e) &&        \
902                                          IS_PLAYER(x, y) &&             \
903                                          !PLAYER_ENEMY_PROTECTED(x, y))))
904
905 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
906         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
907
908 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
909         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
910
911 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
912         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
913
914 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
915         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
916                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
917
918 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
920
921 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
922         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
923
924 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
925         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
926
927 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
928         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
929
930 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
931         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
932
933 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
934         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
935                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
938                                                  IS_FOOD_PENGUIN(Feld[x][y])))
939 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
940         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
941
942 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
943         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
944
945 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
946         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
947
948 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
949         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
950                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
951
952 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
953
954 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
955                 (!IS_PLAYER(x, y) &&                                    \
956                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
957
958 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
959         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
960
961 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
962 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
963
964 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
965 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
966 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
968
969 /* game button identifiers */
970 #define GAME_CTRL_ID_STOP               0
971 #define GAME_CTRL_ID_PAUSE              1
972 #define GAME_CTRL_ID_PLAY               2
973 #define SOUND_CTRL_ID_MUSIC             3
974 #define SOUND_CTRL_ID_LOOPS             4
975 #define SOUND_CTRL_ID_SIMPLE            5
976 #define GAME_CTRL_ID_SAVE               6
977 #define GAME_CTRL_ID_LOAD               7
978
979 #define NUM_GAME_BUTTONS                8
980
981
982 /* forward declaration for internal use */
983
984 static void CreateField(int, int, int);
985
986 static void ResetGfxAnimation(int, int);
987
988 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
989 static void AdvanceFrameAndPlayerCounters(int);
990
991 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
992 static boolean MovePlayer(struct PlayerInfo *, int, int);
993 static void ScrollPlayer(struct PlayerInfo *, int);
994 static void ScrollScreen(struct PlayerInfo *, int);
995
996 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
997 static boolean DigFieldByCE(int, int, int);
998 static boolean SnapField(struct PlayerInfo *, int, int);
999 static boolean DropElement(struct PlayerInfo *);
1000
1001 static void InitBeltMovement(void);
1002 static void CloseAllOpenTimegates(void);
1003 static void CheckGravityMovement(struct PlayerInfo *);
1004 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1005 static void KillPlayerUnlessEnemyProtected(int, int);
1006 static void KillPlayerUnlessExplosionProtected(int, int);
1007
1008 static void TestIfPlayerTouchesCustomElement(int, int);
1009 static void TestIfElementTouchesCustomElement(int, int);
1010 static void TestIfElementHitsCustomElement(int, int, int);
1011
1012 static void HandleElementChange(int, int, int);
1013 static void ExecuteCustomElementAction(int, int, int, int);
1014 static boolean ChangeElement(int, int, int, int);
1015
1016 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1017 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1018         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1019 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1020         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1021 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1022         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1023 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1024         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1025
1026 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1027 #define CheckElementChange(x, y, e, te, ev)                             \
1028         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1029 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1030         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1031 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1033
1034 static void PlayLevelSound(int, int, int);
1035 static void PlayLevelSoundNearest(int, int, int);
1036 static void PlayLevelSoundAction(int, int, int);
1037 static void PlayLevelSoundElementAction(int, int, int, int);
1038 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1039 static void PlayLevelSoundActionIfLoop(int, int, int);
1040 static void StopLevelSoundActionIfLoop(int, int, int);
1041 static void PlayLevelMusic();
1042
1043 static void HandleGameButtons(struct GadgetInfo *);
1044
1045 int AmoebeNachbarNr(int, int);
1046 void AmoebeUmwandeln(int, int);
1047 void ContinueMoving(int, int);
1048 void Bang(int, int);
1049 void InitMovDir(int, int);
1050 void InitAmoebaNr(int, int);
1051 int NewHiScore(void);
1052
1053 void TestIfGoodThingHitsBadThing(int, int, int);
1054 void TestIfBadThingHitsGoodThing(int, int, int);
1055 void TestIfPlayerTouchesBadThing(int, int);
1056 void TestIfPlayerRunsIntoBadThing(int, int, int);
1057 void TestIfBadThingTouchesPlayer(int, int);
1058 void TestIfBadThingRunsIntoPlayer(int, int, int);
1059 void TestIfFriendTouchesBadThing(int, int);
1060 void TestIfBadThingTouchesFriend(int, int);
1061 void TestIfBadThingTouchesOtherBadThing(int, int);
1062 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1063
1064 void KillPlayer(struct PlayerInfo *);
1065 void BuryPlayer(struct PlayerInfo *);
1066 void RemovePlayer(struct PlayerInfo *);
1067
1068 static int getInvisibleActiveFromInvisibleElement(int);
1069 static int getInvisibleFromInvisibleActiveElement(int);
1070
1071 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1072
1073 /* for detection of endless loops, caused by custom element programming */
1074 /* (using maximal playfield width x 10 is just a rough approximation) */
1075 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1076
1077 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1078 {                                                                       \
1079   if (recursion_loop_detected)                                          \
1080     return (rc);                                                        \
1081                                                                         \
1082   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1083   {                                                                     \
1084     recursion_loop_detected = TRUE;                                     \
1085     recursion_loop_element = (e);                                       \
1086   }                                                                     \
1087                                                                         \
1088   recursion_loop_depth++;                                               \
1089 }
1090
1091 #define RECURSION_LOOP_DETECTION_END()                                  \
1092 {                                                                       \
1093   recursion_loop_depth--;                                               \
1094 }
1095
1096 static int recursion_loop_depth;
1097 static boolean recursion_loop_detected;
1098 static boolean recursion_loop_element;
1099
1100 static int map_player_action[MAX_PLAYERS];
1101
1102
1103 /* ------------------------------------------------------------------------- */
1104 /* definition of elements that automatically change to other elements after  */
1105 /* a specified time, eventually calling a function when changing             */
1106 /* ------------------------------------------------------------------------- */
1107
1108 /* forward declaration for changer functions */
1109 static void InitBuggyBase(int, int);
1110 static void WarnBuggyBase(int, int);
1111
1112 static void InitTrap(int, int);
1113 static void ActivateTrap(int, int);
1114 static void ChangeActiveTrap(int, int);
1115
1116 static void InitRobotWheel(int, int);
1117 static void RunRobotWheel(int, int);
1118 static void StopRobotWheel(int, int);
1119
1120 static void InitTimegateWheel(int, int);
1121 static void RunTimegateWheel(int, int);
1122
1123 static void InitMagicBallDelay(int, int);
1124 static void ActivateMagicBall(int, int);
1125
1126 struct ChangingElementInfo
1127 {
1128   int element;
1129   int target_element;
1130   int change_delay;
1131   void (*pre_change_function)(int x, int y);
1132   void (*change_function)(int x, int y);
1133   void (*post_change_function)(int x, int y);
1134 };
1135
1136 static struct ChangingElementInfo change_delay_list[] =
1137 {
1138   {
1139     EL_NUT_BREAKING,
1140     EL_EMERALD,
1141     6,
1142     NULL,
1143     NULL,
1144     NULL
1145   },
1146   {
1147     EL_PEARL_BREAKING,
1148     EL_EMPTY,
1149     8,
1150     NULL,
1151     NULL,
1152     NULL
1153   },
1154   {
1155     EL_EXIT_OPENING,
1156     EL_EXIT_OPEN,
1157     29,
1158     NULL,
1159     NULL,
1160     NULL
1161   },
1162   {
1163     EL_EXIT_CLOSING,
1164     EL_EXIT_CLOSED,
1165     29,
1166     NULL,
1167     NULL,
1168     NULL
1169   },
1170   {
1171     EL_STEEL_EXIT_OPENING,
1172     EL_STEEL_EXIT_OPEN,
1173     29,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_STEEL_EXIT_CLOSING,
1180     EL_STEEL_EXIT_CLOSED,
1181     29,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_EM_EXIT_OPENING,
1188     EL_EM_EXIT_OPEN,
1189     29,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EM_EXIT_CLOSING,
1196     EL_EMPTY,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EM_STEEL_EXIT_OPENING,
1204     EL_EM_STEEL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EM_STEEL_EXIT_CLOSING,
1212     EL_STEELWALL,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_SP_EXIT_OPENING,
1220     EL_SP_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_SP_EXIT_CLOSING,
1228     EL_SP_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_SWITCHGATE_OPENING,
1236     EL_SWITCHGATE_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_SWITCHGATE_CLOSING,
1244     EL_SWITCHGATE_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_TIMEGATE_OPENING,
1252     EL_TIMEGATE_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_TIMEGATE_CLOSING,
1260     EL_TIMEGATE_CLOSED,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266
1267   {
1268     EL_ACID_SPLASH_LEFT,
1269     EL_EMPTY,
1270     8,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_ACID_SPLASH_RIGHT,
1277     EL_EMPTY,
1278     8,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SP_BUGGY_BASE,
1285     EL_SP_BUGGY_BASE_ACTIVATING,
1286     0,
1287     InitBuggyBase,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SP_BUGGY_BASE_ACTIVATING,
1293     EL_SP_BUGGY_BASE_ACTIVE,
1294     0,
1295     InitBuggyBase,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_SP_BUGGY_BASE_ACTIVE,
1301     EL_SP_BUGGY_BASE,
1302     0,
1303     InitBuggyBase,
1304     WarnBuggyBase,
1305     NULL
1306   },
1307   {
1308     EL_TRAP,
1309     EL_TRAP_ACTIVE,
1310     0,
1311     InitTrap,
1312     NULL,
1313     ActivateTrap
1314   },
1315   {
1316     EL_TRAP_ACTIVE,
1317     EL_TRAP,
1318     31,
1319     NULL,
1320     ChangeActiveTrap,
1321     NULL
1322   },
1323   {
1324     EL_ROBOT_WHEEL_ACTIVE,
1325     EL_ROBOT_WHEEL,
1326     0,
1327     InitRobotWheel,
1328     RunRobotWheel,
1329     StopRobotWheel
1330   },
1331   {
1332     EL_TIMEGATE_SWITCH_ACTIVE,
1333     EL_TIMEGATE_SWITCH,
1334     0,
1335     InitTimegateWheel,
1336     RunTimegateWheel,
1337     NULL
1338   },
1339   {
1340     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1341     EL_DC_TIMEGATE_SWITCH,
1342     0,
1343     InitTimegateWheel,
1344     RunTimegateWheel,
1345     NULL
1346   },
1347   {
1348     EL_EMC_MAGIC_BALL_ACTIVE,
1349     EL_EMC_MAGIC_BALL_ACTIVE,
1350     0,
1351     InitMagicBallDelay,
1352     NULL,
1353     ActivateMagicBall
1354   },
1355   {
1356     EL_EMC_SPRING_BUMPER_ACTIVE,
1357     EL_EMC_SPRING_BUMPER,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_DIAGONAL_SHRINKING,
1365     EL_UNDEFINED,
1366     0,
1367     NULL,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_DIAGONAL_GROWING,
1373     EL_UNDEFINED,
1374     0,
1375     NULL,
1376     NULL,
1377     NULL,
1378   },
1379
1380   {
1381     EL_UNDEFINED,
1382     EL_UNDEFINED,
1383     -1,
1384     NULL,
1385     NULL,
1386     NULL
1387   }
1388 };
1389
1390 struct
1391 {
1392   int element;
1393   int push_delay_fixed, push_delay_random;
1394 }
1395 push_delay_list[] =
1396 {
1397   { EL_SPRING,                  0, 0 },
1398   { EL_BALLOON,                 0, 0 },
1399
1400   { EL_SOKOBAN_OBJECT,          2, 0 },
1401   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1402   { EL_SATELLITE,               2, 0 },
1403   { EL_SP_DISK_YELLOW,          2, 0 },
1404
1405   { EL_UNDEFINED,               0, 0 },
1406 };
1407
1408 struct
1409 {
1410   int element;
1411   int move_stepsize;
1412 }
1413 move_stepsize_list[] =
1414 {
1415   { EL_AMOEBA_DROP,             2 },
1416   { EL_AMOEBA_DROPPING,         2 },
1417   { EL_QUICKSAND_FILLING,       1 },
1418   { EL_QUICKSAND_EMPTYING,      1 },
1419   { EL_QUICKSAND_FAST_FILLING,  2 },
1420   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1421   { EL_MAGIC_WALL_FILLING,      2 },
1422   { EL_MAGIC_WALL_EMPTYING,     2 },
1423   { EL_BD_MAGIC_WALL_FILLING,   2 },
1424   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1425   { EL_DC_MAGIC_WALL_FILLING,   2 },
1426   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1427
1428   { EL_UNDEFINED,               0 },
1429 };
1430
1431 struct
1432 {
1433   int element;
1434   int count;
1435 }
1436 collect_count_list[] =
1437 {
1438   { EL_EMERALD,                 1 },
1439   { EL_BD_DIAMOND,              1 },
1440   { EL_EMERALD_YELLOW,          1 },
1441   { EL_EMERALD_RED,             1 },
1442   { EL_EMERALD_PURPLE,          1 },
1443   { EL_DIAMOND,                 3 },
1444   { EL_SP_INFOTRON,             1 },
1445   { EL_PEARL,                   5 },
1446   { EL_CRYSTAL,                 8 },
1447
1448   { EL_UNDEFINED,               0 },
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int direction;
1455 }
1456 access_direction_list[] =
1457 {
1458   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1459   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1460   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1461   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1464   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1465   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1466   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1467   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1468   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1469
1470   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1471   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1472   { EL_SP_PORT_UP,                                                   MV_DOWN },
1473   { EL_SP_PORT_DOWN,                                         MV_UP           },
1474   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1475   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1476   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1477   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1478   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1479   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1481   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1482   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1483   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1484   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1485   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1486   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1487   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1488   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1489
1490   { EL_UNDEFINED,                       MV_NONE                              }
1491 };
1492
1493 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1494
1495 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1496 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1497 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1498                                  IS_JUST_CHANGING(x, y))
1499
1500 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1501
1502 /* static variables for playfield scan mode (scanning forward or backward) */
1503 static int playfield_scan_start_x = 0;
1504 static int playfield_scan_start_y = 0;
1505 static int playfield_scan_delta_x = 1;
1506 static int playfield_scan_delta_y = 1;
1507
1508 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1509                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1510                                      (y) += playfield_scan_delta_y)     \
1511                                 for ((x) = playfield_scan_start_x;      \
1512                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1513                                      (x) += playfield_scan_delta_x)
1514
1515 #ifdef DEBUG
1516 void DEBUG_SetMaximumDynamite()
1517 {
1518   int i;
1519
1520   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1521     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1522       local_player->inventory_element[local_player->inventory_size++] =
1523         EL_DYNAMITE;
1524 }
1525 #endif
1526
1527 static void InitPlayfieldScanModeVars()
1528 {
1529   if (game.use_reverse_scan_direction)
1530   {
1531     playfield_scan_start_x = lev_fieldx - 1;
1532     playfield_scan_start_y = lev_fieldy - 1;
1533
1534     playfield_scan_delta_x = -1;
1535     playfield_scan_delta_y = -1;
1536   }
1537   else
1538   {
1539     playfield_scan_start_x = 0;
1540     playfield_scan_start_y = 0;
1541
1542     playfield_scan_delta_x = 1;
1543     playfield_scan_delta_y = 1;
1544   }
1545 }
1546
1547 static void InitPlayfieldScanMode(int mode)
1548 {
1549   game.use_reverse_scan_direction =
1550     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1551
1552   InitPlayfieldScanModeVars();
1553 }
1554
1555 static int get_move_delay_from_stepsize(int move_stepsize)
1556 {
1557   move_stepsize =
1558     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1559
1560   /* make sure that stepsize value is always a power of 2 */
1561   move_stepsize = (1 << log_2(move_stepsize));
1562
1563   return TILEX / move_stepsize;
1564 }
1565
1566 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1567                                boolean init_game)
1568 {
1569   int player_nr = player->index_nr;
1570   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1571   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1572
1573   /* do no immediately change move delay -- the player might just be moving */
1574   player->move_delay_value_next = move_delay;
1575
1576   /* information if player can move must be set separately */
1577   player->cannot_move = cannot_move;
1578
1579   if (init_game)
1580   {
1581     player->move_delay       = game.initial_move_delay[player_nr];
1582     player->move_delay_value = game.initial_move_delay_value[player_nr];
1583
1584     player->move_delay_value_next = -1;
1585
1586     player->move_delay_reset_counter = 0;
1587   }
1588 }
1589
1590 void GetPlayerConfig()
1591 {
1592   GameFrameDelay = setup.game_frame_delay;
1593
1594   if (!audio.sound_available)
1595     setup.sound_simple = FALSE;
1596
1597   if (!audio.loops_available)
1598     setup.sound_loops = FALSE;
1599
1600   if (!audio.music_available)
1601     setup.sound_music = FALSE;
1602
1603   if (!video.fullscreen_available)
1604     setup.fullscreen = FALSE;
1605
1606   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1607
1608   SetAudioMode(setup.sound);
1609   InitJoysticks();
1610 }
1611
1612 int GetElementFromGroupElement(int element)
1613 {
1614   if (IS_GROUP_ELEMENT(element))
1615   {
1616     struct ElementGroupInfo *group = element_info[element].group;
1617     int last_anim_random_frame = gfx.anim_random_frame;
1618     int element_pos;
1619
1620     if (group->choice_mode == ANIM_RANDOM)
1621       gfx.anim_random_frame = RND(group->num_elements_resolved);
1622
1623     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1624                                     group->choice_mode, 0,
1625                                     group->choice_pos);
1626
1627     if (group->choice_mode == ANIM_RANDOM)
1628       gfx.anim_random_frame = last_anim_random_frame;
1629
1630     group->choice_pos++;
1631
1632     element = group->element_resolved[element_pos];
1633   }
1634
1635   return element;
1636 }
1637
1638 static void InitPlayerField(int x, int y, int element, boolean init_game)
1639 {
1640   if (element == EL_SP_MURPHY)
1641   {
1642     if (init_game)
1643     {
1644       if (stored_player[0].present)
1645       {
1646         Feld[x][y] = EL_SP_MURPHY_CLONE;
1647
1648         return;
1649       }
1650       else
1651       {
1652         stored_player[0].initial_element = element;
1653         stored_player[0].use_murphy = TRUE;
1654
1655         if (!level.use_artwork_element[0])
1656           stored_player[0].artwork_element = EL_SP_MURPHY;
1657       }
1658
1659       Feld[x][y] = EL_PLAYER_1;
1660     }
1661   }
1662
1663   if (init_game)
1664   {
1665     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1666     int jx = player->jx, jy = player->jy;
1667
1668     player->present = TRUE;
1669
1670     player->block_last_field = (element == EL_SP_MURPHY ?
1671                                 level.sp_block_last_field :
1672                                 level.block_last_field);
1673
1674     /* ---------- initialize player's last field block delay --------------- */
1675
1676     /* always start with reliable default value (no adjustment needed) */
1677     player->block_delay_adjustment = 0;
1678
1679     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1680     if (player->block_last_field && element == EL_SP_MURPHY)
1681       player->block_delay_adjustment = 1;
1682
1683     /* special case 2: in game engines before 3.1.1, blocking was different */
1684     if (game.use_block_last_field_bug)
1685       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1686
1687     if (!options.network || player->connected)
1688     {
1689       player->active = TRUE;
1690
1691       /* remove potentially duplicate players */
1692       if (StorePlayer[jx][jy] == Feld[x][y])
1693         StorePlayer[jx][jy] = 0;
1694
1695       StorePlayer[x][y] = Feld[x][y];
1696
1697 #if DEBUG_INIT_PLAYER
1698       if (options.debug)
1699       {
1700         printf("- player element %d activated", player->element_nr);
1701         printf(" (local player is %d and currently %s)\n",
1702                local_player->element_nr,
1703                local_player->active ? "active" : "not active");
1704       }
1705     }
1706 #endif
1707
1708     Feld[x][y] = EL_EMPTY;
1709
1710     player->jx = player->last_jx = x;
1711     player->jy = player->last_jy = y;
1712   }
1713
1714   if (!init_game)
1715   {
1716     int player_nr = GET_PLAYER_NR(element);
1717     struct PlayerInfo *player = &stored_player[player_nr];
1718
1719     if (player->active && player->killed)
1720       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1721   }
1722 }
1723
1724 static void InitField(int x, int y, boolean init_game)
1725 {
1726   int element = Feld[x][y];
1727
1728   switch (element)
1729   {
1730     case EL_SP_MURPHY:
1731     case EL_PLAYER_1:
1732     case EL_PLAYER_2:
1733     case EL_PLAYER_3:
1734     case EL_PLAYER_4:
1735       InitPlayerField(x, y, element, init_game);
1736       break;
1737
1738     case EL_SOKOBAN_FIELD_PLAYER:
1739       element = Feld[x][y] = EL_PLAYER_1;
1740       InitField(x, y, init_game);
1741
1742       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1743       InitField(x, y, init_game);
1744       break;
1745
1746     case EL_SOKOBAN_FIELD_EMPTY:
1747       local_player->sokobanfields_still_needed++;
1748       break;
1749
1750     case EL_STONEBLOCK:
1751       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1752         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1753       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1754         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1755       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1756         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1757       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1761       break;
1762
1763     case EL_BUG:
1764     case EL_BUG_RIGHT:
1765     case EL_BUG_UP:
1766     case EL_BUG_LEFT:
1767     case EL_BUG_DOWN:
1768     case EL_SPACESHIP:
1769     case EL_SPACESHIP_RIGHT:
1770     case EL_SPACESHIP_UP:
1771     case EL_SPACESHIP_LEFT:
1772     case EL_SPACESHIP_DOWN:
1773     case EL_BD_BUTTERFLY:
1774     case EL_BD_BUTTERFLY_RIGHT:
1775     case EL_BD_BUTTERFLY_UP:
1776     case EL_BD_BUTTERFLY_LEFT:
1777     case EL_BD_BUTTERFLY_DOWN:
1778     case EL_BD_FIREFLY:
1779     case EL_BD_FIREFLY_RIGHT:
1780     case EL_BD_FIREFLY_UP:
1781     case EL_BD_FIREFLY_LEFT:
1782     case EL_BD_FIREFLY_DOWN:
1783     case EL_PACMAN_RIGHT:
1784     case EL_PACMAN_UP:
1785     case EL_PACMAN_LEFT:
1786     case EL_PACMAN_DOWN:
1787     case EL_YAMYAM:
1788     case EL_YAMYAM_LEFT:
1789     case EL_YAMYAM_RIGHT:
1790     case EL_YAMYAM_UP:
1791     case EL_YAMYAM_DOWN:
1792     case EL_DARK_YAMYAM:
1793     case EL_ROBOT:
1794     case EL_PACMAN:
1795     case EL_SP_SNIKSNAK:
1796     case EL_SP_ELECTRON:
1797     case EL_MOLE:
1798     case EL_MOLE_LEFT:
1799     case EL_MOLE_RIGHT:
1800     case EL_MOLE_UP:
1801     case EL_MOLE_DOWN:
1802       InitMovDir(x, y);
1803       break;
1804
1805     case EL_AMOEBA_FULL:
1806     case EL_BD_AMOEBA:
1807       InitAmoebaNr(x, y);
1808       break;
1809
1810     case EL_AMOEBA_DROP:
1811       if (y == lev_fieldy - 1)
1812       {
1813         Feld[x][y] = EL_AMOEBA_GROWING;
1814         Store[x][y] = EL_AMOEBA_WET;
1815       }
1816       break;
1817
1818     case EL_DYNAMITE_ACTIVE:
1819     case EL_SP_DISK_RED_ACTIVE:
1820     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1821     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1822     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1824       MovDelay[x][y] = 96;
1825       break;
1826
1827     case EL_EM_DYNAMITE_ACTIVE:
1828       MovDelay[x][y] = 32;
1829       break;
1830
1831     case EL_LAMP:
1832       local_player->lights_still_needed++;
1833       break;
1834
1835     case EL_PENGUIN:
1836       local_player->friends_still_needed++;
1837       break;
1838
1839     case EL_PIG:
1840     case EL_DRAGON:
1841       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1842       break;
1843
1844     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1845     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1846     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1847     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1856       if (init_game)
1857       {
1858         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1859         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1860         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1861
1862         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1863         {
1864           game.belt_dir[belt_nr] = belt_dir;
1865           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1866         }
1867         else    /* more than one switch -- set it like the first switch */
1868         {
1869           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1870         }
1871       }
1872       break;
1873
1874     case EL_LIGHT_SWITCH_ACTIVE:
1875       if (init_game)
1876         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1877       break;
1878
1879     case EL_INVISIBLE_STEELWALL:
1880     case EL_INVISIBLE_WALL:
1881     case EL_INVISIBLE_SAND:
1882       if (game.light_time_left > 0 ||
1883           game.lenses_time_left > 0)
1884         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1885       break;
1886
1887     case EL_EMC_MAGIC_BALL:
1888       if (game.ball_state)
1889         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1890       break;
1891
1892     case EL_EMC_MAGIC_BALL_SWITCH:
1893       if (game.ball_state)
1894         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1895       break;
1896
1897     case EL_TRIGGER_PLAYER:
1898     case EL_TRIGGER_ELEMENT:
1899     case EL_TRIGGER_CE_VALUE:
1900     case EL_TRIGGER_CE_SCORE:
1901     case EL_SELF:
1902     case EL_ANY_ELEMENT:
1903     case EL_CURRENT_CE_VALUE:
1904     case EL_CURRENT_CE_SCORE:
1905     case EL_PREV_CE_1:
1906     case EL_PREV_CE_2:
1907     case EL_PREV_CE_3:
1908     case EL_PREV_CE_4:
1909     case EL_PREV_CE_5:
1910     case EL_PREV_CE_6:
1911     case EL_PREV_CE_7:
1912     case EL_PREV_CE_8:
1913     case EL_NEXT_CE_1:
1914     case EL_NEXT_CE_2:
1915     case EL_NEXT_CE_3:
1916     case EL_NEXT_CE_4:
1917     case EL_NEXT_CE_5:
1918     case EL_NEXT_CE_6:
1919     case EL_NEXT_CE_7:
1920     case EL_NEXT_CE_8:
1921       /* reference elements should not be used on the playfield */
1922       Feld[x][y] = EL_EMPTY;
1923       break;
1924
1925     default:
1926       if (IS_CUSTOM_ELEMENT(element))
1927       {
1928         if (CAN_MOVE(element))
1929           InitMovDir(x, y);
1930
1931         if (!element_info[element].use_last_ce_value || init_game)
1932           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1933       }
1934       else if (IS_GROUP_ELEMENT(element))
1935       {
1936         Feld[x][y] = GetElementFromGroupElement(element);
1937
1938         InitField(x, y, init_game);
1939       }
1940
1941       break;
1942   }
1943
1944   if (!init_game)
1945     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1946 }
1947
1948 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1949 {
1950   InitField(x, y, init_game);
1951
1952   /* not needed to call InitMovDir() -- already done by InitField()! */
1953   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1954       CAN_MOVE(Feld[x][y]))
1955     InitMovDir(x, y);
1956 }
1957
1958 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1959 {
1960   int old_element = Feld[x][y];
1961
1962   InitField(x, y, init_game);
1963
1964   /* not needed to call InitMovDir() -- already done by InitField()! */
1965   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966       CAN_MOVE(old_element) &&
1967       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1968     InitMovDir(x, y);
1969
1970   /* this case is in fact a combination of not less than three bugs:
1971      first, it calls InitMovDir() for elements that can move, although this is
1972      already done by InitField(); then, it checks the element that was at this
1973      field _before_ the call to InitField() (which can change it); lastly, it
1974      was not called for "mole with direction" elements, which were treated as
1975      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1976   */
1977 }
1978
1979 static int get_key_element_from_nr(int key_nr)
1980 {
1981   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1982                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1983                           EL_EM_KEY_1 : EL_KEY_1);
1984
1985   return key_base_element + key_nr;
1986 }
1987
1988 static int get_next_dropped_element(struct PlayerInfo *player)
1989 {
1990   return (player->inventory_size > 0 ?
1991           player->inventory_element[player->inventory_size - 1] :
1992           player->inventory_infinite_element != EL_UNDEFINED ?
1993           player->inventory_infinite_element :
1994           player->dynabombs_left > 0 ?
1995           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1996           EL_UNDEFINED);
1997 }
1998
1999 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2000 {
2001   /* pos >= 0: get element from bottom of the stack;
2002      pos <  0: get element from top of the stack */
2003
2004   if (pos < 0)
2005   {
2006     int min_inventory_size = -pos;
2007     int inventory_pos = player->inventory_size - min_inventory_size;
2008     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2009
2010     return (player->inventory_size >= min_inventory_size ?
2011             player->inventory_element[inventory_pos] :
2012             player->inventory_infinite_element != EL_UNDEFINED ?
2013             player->inventory_infinite_element :
2014             player->dynabombs_left >= min_dynabombs_left ?
2015             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2016             EL_UNDEFINED);
2017   }
2018   else
2019   {
2020     int min_dynabombs_left = pos + 1;
2021     int min_inventory_size = pos + 1 - player->dynabombs_left;
2022     int inventory_pos = pos - player->dynabombs_left;
2023
2024     return (player->inventory_infinite_element != EL_UNDEFINED ?
2025             player->inventory_infinite_element :
2026             player->dynabombs_left >= min_dynabombs_left ?
2027             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2028             player->inventory_size >= min_inventory_size ?
2029             player->inventory_element[inventory_pos] :
2030             EL_UNDEFINED);
2031   }
2032 }
2033
2034 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2035 {
2036   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2037   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2038   int compare_result;
2039
2040   if (gpo1->sort_priority != gpo2->sort_priority)
2041     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2042   else
2043     compare_result = gpo1->nr - gpo2->nr;
2044
2045   return compare_result;
2046 }
2047
2048 void InitGameControlValues()
2049 {
2050   int i;
2051
2052   for (i = 0; game_panel_controls[i].nr != -1; i++)
2053   {
2054     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2055     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2056     struct TextPosInfo *pos = gpc->pos;
2057     int nr = gpc->nr;
2058     int type = gpc->type;
2059
2060     if (nr != i)
2061     {
2062       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2063       Error(ERR_EXIT, "this should not happen -- please debug");
2064     }
2065
2066     /* force update of game controls after initialization */
2067     gpc->value = gpc->last_value = -1;
2068     gpc->frame = gpc->last_frame = -1;
2069     gpc->gfx_frame = -1;
2070
2071     /* determine panel value width for later calculation of alignment */
2072     if (type == TYPE_INTEGER || type == TYPE_STRING)
2073     {
2074       pos->width = pos->size * getFontWidth(pos->font);
2075       pos->height = getFontHeight(pos->font);
2076     }
2077     else if (type == TYPE_ELEMENT)
2078     {
2079       pos->width = pos->size;
2080       pos->height = pos->size;
2081     }
2082
2083     /* fill structure for game panel draw order */
2084     gpo->nr = gpc->nr;
2085     gpo->sort_priority = pos->sort_priority;
2086   }
2087
2088   /* sort game panel controls according to sort_priority and control number */
2089   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2090         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2091 }
2092
2093 void UpdatePlayfieldElementCount()
2094 {
2095   boolean use_element_count = FALSE;
2096   int i, j, x, y;
2097
2098   /* first check if it is needed at all to calculate playfield element count */
2099   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2100     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2101       use_element_count = TRUE;
2102
2103   if (!use_element_count)
2104     return;
2105
2106   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2107     element_info[i].element_count = 0;
2108
2109   SCAN_PLAYFIELD(x, y)
2110   {
2111     element_info[Feld[x][y]].element_count++;
2112   }
2113
2114   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2115     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2116       if (IS_IN_GROUP(j, i))
2117         element_info[EL_GROUP_START + i].element_count +=
2118           element_info[j].element_count;
2119 }
2120
2121 void UpdateGameControlValues()
2122 {
2123   int i, k;
2124   int time = (local_player->LevelSolved ?
2125               local_player->LevelSolved_CountingTime :
2126               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2127               level.native_em_level->lev->time :
2128               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2129               level.native_sp_level->game_sp->time_played :
2130               game.no_time_limit ? TimePlayed : TimeLeft);
2131   int score = (local_player->LevelSolved ?
2132                local_player->LevelSolved_CountingScore :
2133                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                level.native_em_level->lev->score :
2135                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2136                level.native_sp_level->game_sp->score :
2137                local_player->score);
2138   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139               level.native_em_level->lev->required :
2140               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2141               level.native_sp_level->game_sp->infotrons_still_needed :
2142               local_player->gems_still_needed);
2143   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2144                      level.native_em_level->lev->required > 0 :
2145                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2146                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2147                      local_player->gems_still_needed > 0 ||
2148                      local_player->sokobanfields_still_needed > 0 ||
2149                      local_player->lights_still_needed > 0);
2150
2151   UpdatePlayfieldElementCount();
2152
2153   /* update game panel control values */
2154
2155   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2156   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2157
2158   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2159   for (i = 0; i < MAX_NUM_KEYS; i++)
2160     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2161   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2162   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2163
2164   if (game.centered_player_nr == -1)
2165   {
2166     for (i = 0; i < MAX_PLAYERS; i++)
2167     {
2168       /* only one player in Supaplex game engine */
2169       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2170         break;
2171
2172       for (k = 0; k < MAX_NUM_KEYS; k++)
2173       {
2174         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2175         {
2176           if (level.native_em_level->ply[i]->keys & (1 << k))
2177             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2178               get_key_element_from_nr(k);
2179         }
2180         else if (stored_player[i].key[k])
2181           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182             get_key_element_from_nr(k);
2183       }
2184
2185       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2186         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2187           level.native_em_level->ply[i]->dynamite;
2188       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_sp_level->game_sp->red_disk_count;
2191       else
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           stored_player[i].inventory_size;
2194
2195       if (stored_player[i].num_white_keys > 0)
2196         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2197           EL_DC_KEY_WHITE;
2198
2199       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2200         stored_player[i].num_white_keys;
2201     }
2202   }
2203   else
2204   {
2205     int player_nr = game.centered_player_nr;
2206
2207     for (k = 0; k < MAX_NUM_KEYS; k++)
2208     {
2209       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2210       {
2211         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2212           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2213             get_key_element_from_nr(k);
2214       }
2215       else if (stored_player[player_nr].key[k])
2216         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217           get_key_element_from_nr(k);
2218     }
2219
2220     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2221       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2222         level.native_em_level->ply[player_nr]->dynamite;
2223     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_sp_level->game_sp->red_disk_count;
2226     else
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         stored_player[player_nr].inventory_size;
2229
2230     if (stored_player[player_nr].num_white_keys > 0)
2231       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2232
2233     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2234       stored_player[player_nr].num_white_keys;
2235   }
2236
2237   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2238   {
2239     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2240       get_inventory_element_from_pos(local_player, i);
2241     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2242       get_inventory_element_from_pos(local_player, -i - 1);
2243   }
2244
2245   game_panel_controls[GAME_PANEL_SCORE].value = score;
2246   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2247
2248   game_panel_controls[GAME_PANEL_TIME].value = time;
2249
2250   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2253
2254   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2255
2256   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2257     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2258      EL_EMPTY);
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2260     local_player->shield_normal_time_left;
2261   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2262     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2263      EL_EMPTY);
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2265     local_player->shield_deadly_time_left;
2266
2267   game_panel_controls[GAME_PANEL_EXIT].value =
2268     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2269
2270   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2271     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2272   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2273     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2274      EL_EMC_MAGIC_BALL_SWITCH);
2275
2276   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2277     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2278   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2279     game.light_time_left;
2280
2281   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2282     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2283   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2284     game.timegate_time_left;
2285
2286   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2287     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2288
2289   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2290     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2291   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2292     game.lenses_time_left;
2293
2294   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2295     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2296   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2297     game.magnify_time_left;
2298
2299   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2300     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2301      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2302      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2303      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2304      EL_BALLOON_SWITCH_NONE);
2305
2306   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2307     local_player->dynabomb_count;
2308   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2309     local_player->dynabomb_size;
2310   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2311     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2312
2313   game_panel_controls[GAME_PANEL_PENGUINS].value =
2314     local_player->friends_still_needed;
2315
2316   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2317     local_player->sokobanfields_still_needed;
2318   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2319     local_player->sokobanfields_still_needed;
2320
2321   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2322     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2323
2324   for (i = 0; i < NUM_BELTS; i++)
2325   {
2326     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2327       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2328        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2330       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2334     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2335   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2336     game.magic_wall_time_left;
2337
2338   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2339     local_player->gravity;
2340
2341   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2342     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2343
2344   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2345     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2346       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2347        game.panel.element[i].id : EL_UNDEFINED);
2348
2349   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2350     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2351       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2352        element_info[game.panel.element_count[i].id].element_count : 0);
2353
2354   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2355     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2356       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2357        element_info[game.panel.ce_score[i].id].collect_score : 0);
2358
2359   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2360     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2361       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2362        element_info[game.panel.ce_score_element[i].id].collect_score :
2363        EL_UNDEFINED);
2364
2365   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2366   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2367   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2368
2369   /* update game panel control frames */
2370
2371   for (i = 0; game_panel_controls[i].nr != -1; i++)
2372   {
2373     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2374
2375     if (gpc->type == TYPE_ELEMENT)
2376     {
2377       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2378       {
2379         int last_anim_random_frame = gfx.anim_random_frame;
2380         int element = gpc->value;
2381         int graphic = el2panelimg(element);
2382
2383         if (gpc->value != gpc->last_value)
2384         {
2385           gpc->gfx_frame = 0;
2386           gpc->gfx_random = INIT_GFX_RANDOM();
2387         }
2388         else
2389         {
2390           gpc->gfx_frame++;
2391
2392           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394             gpc->gfx_random = INIT_GFX_RANDOM();
2395         }
2396
2397         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398           gfx.anim_random_frame = gpc->gfx_random;
2399
2400         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401           gpc->gfx_frame = element_info[element].collect_score;
2402
2403         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2404                                               gpc->gfx_frame);
2405
2406         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407           gfx.anim_random_frame = last_anim_random_frame;
2408       }
2409     }
2410   }
2411 }
2412
2413 void DisplayGameControlValues()
2414 {
2415   boolean redraw_panel = FALSE;
2416   int i;
2417
2418   for (i = 0; game_panel_controls[i].nr != -1; i++)
2419   {
2420     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2421
2422     if (PANEL_DEACTIVATED(gpc->pos))
2423       continue;
2424
2425     if (gpc->value == gpc->last_value &&
2426         gpc->frame == gpc->last_frame)
2427       continue;
2428
2429     redraw_panel = TRUE;
2430   }
2431
2432   if (!redraw_panel)
2433     return;
2434
2435   /* copy default game door content to main double buffer */
2436
2437   /* !!! CHECK AGAIN !!! */
2438   SetPanelBackground();
2439   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2440   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2441
2442   /* redraw game control buttons */
2443   RedrawGameButtons();
2444
2445   game_status = GAME_MODE_PSEUDO_PANEL;
2446
2447   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2448   {
2449     int nr = game_panel_order[i].nr;
2450     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2451     struct TextPosInfo *pos = gpc->pos;
2452     int type = gpc->type;
2453     int value = gpc->value;
2454     int frame = gpc->frame;
2455     int size = pos->size;
2456     int font = pos->font;
2457     boolean draw_masked = pos->draw_masked;
2458     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2459
2460     if (PANEL_DEACTIVATED(pos))
2461       continue;
2462
2463     gpc->last_value = value;
2464     gpc->last_frame = frame;
2465
2466     if (type == TYPE_INTEGER)
2467     {
2468       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2469           nr == GAME_PANEL_TIME)
2470       {
2471         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2472
2473         if (use_dynamic_size)           /* use dynamic number of digits */
2474         {
2475           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2476           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2477           int size2 = size1 + 1;
2478           int font1 = pos->font;
2479           int font2 = pos->font_alt;
2480
2481           size = (value < value_change ? size1 : size2);
2482           font = (value < value_change ? font1 : font2);
2483         }
2484       }
2485
2486       /* correct text size if "digits" is zero or less */
2487       if (size <= 0)
2488         size = strlen(int2str(value, size));
2489
2490       /* dynamically correct text alignment */
2491       pos->width = size * getFontWidth(font);
2492
2493       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2494                   int2str(value, size), font, mask_mode);
2495     }
2496     else if (type == TYPE_ELEMENT)
2497     {
2498       int element, graphic;
2499       Bitmap *src_bitmap;
2500       int src_x, src_y;
2501       int width, height;
2502       int dst_x = PANEL_XPOS(pos);
2503       int dst_y = PANEL_YPOS(pos);
2504
2505       if (value != EL_UNDEFINED && value != EL_EMPTY)
2506       {
2507         element = value;
2508         graphic = el2panelimg(value);
2509
2510         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2511
2512         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2513           size = TILESIZE;
2514
2515         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2516                               &src_x, &src_y);
2517
2518         width  = graphic_info[graphic].width  * size / TILESIZE;
2519         height = graphic_info[graphic].height * size / TILESIZE;
2520
2521         if (draw_masked)
2522           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2523                            dst_x, dst_y);
2524         else
2525           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2526                      dst_x, dst_y);
2527       }
2528     }
2529     else if (type == TYPE_STRING)
2530     {
2531       boolean active = (value != 0);
2532       char *state_normal = "off";
2533       char *state_active = "on";
2534       char *state = (active ? state_active : state_normal);
2535       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2536                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2537                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2538                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2539
2540       if (nr == GAME_PANEL_GRAVITY_STATE)
2541       {
2542         int font1 = pos->font;          /* (used for normal state) */
2543         int font2 = pos->font_alt;      /* (used for active state) */
2544
2545         font = (active ? font2 : font1);
2546       }
2547
2548       if (s != NULL)
2549       {
2550         char *s_cut;
2551
2552         if (size <= 0)
2553         {
2554           /* don't truncate output if "chars" is zero or less */
2555           size = strlen(s);
2556
2557           /* dynamically correct text alignment */
2558           pos->width = size * getFontWidth(font);
2559         }
2560
2561         s_cut = getStringCopyN(s, size);
2562
2563         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2564                     s_cut, font, mask_mode);
2565
2566         free(s_cut);
2567       }
2568     }
2569
2570     redraw_mask |= REDRAW_DOOR_1;
2571   }
2572
2573   game_status = GAME_MODE_PLAYING;
2574 }
2575
2576 void UpdateAndDisplayGameControlValues()
2577 {
2578   if (tape.warp_forward)
2579     return;
2580
2581   UpdateGameControlValues();
2582   DisplayGameControlValues();
2583 }
2584
2585 void UpdateGameDoorValues()
2586 {
2587   UpdateGameControlValues();
2588 }
2589
2590 void DrawGameDoorValues()
2591 {
2592   DisplayGameControlValues();
2593 }
2594
2595
2596 /*
2597   =============================================================================
2598   InitGameEngine()
2599   -----------------------------------------------------------------------------
2600   initialize game engine due to level / tape version number
2601   =============================================================================
2602 */
2603
2604 static void InitGameEngine()
2605 {
2606   int i, j, k, l, x, y;
2607
2608   /* set game engine from tape file when re-playing, else from level file */
2609   game.engine_version = (tape.playing ? tape.engine_version :
2610                          level.game_version);
2611
2612   /* set single or multi-player game mode (needed for re-playing tapes) */
2613   game.team_mode = setup.team_mode;
2614
2615   if (tape.playing)
2616   {
2617     int num_players = 0;
2618
2619     for (i = 0; i < MAX_PLAYERS; i++)
2620       if (tape.player_participates[i])
2621         num_players++;
2622
2623     /* multi-player tapes contain input data for more than one player */
2624     game.team_mode = (num_players > 1);
2625   }
2626
2627   /* ---------------------------------------------------------------------- */
2628   /* set flags for bugs and changes according to active game engine version */
2629   /* ---------------------------------------------------------------------- */
2630
2631   /*
2632     Summary of bugfix/change:
2633     Fixed handling for custom elements that change when pushed by the player.
2634
2635     Fixed/changed in version:
2636     3.1.0
2637
2638     Description:
2639     Before 3.1.0, custom elements that "change when pushing" changed directly
2640     after the player started pushing them (until then handled in "DigField()").
2641     Since 3.1.0, these custom elements are not changed until the "pushing"
2642     move of the element is finished (now handled in "ContinueMoving()").
2643
2644     Affected levels/tapes:
2645     The first condition is generally needed for all levels/tapes before version
2646     3.1.0, which might use the old behaviour before it was changed; known tapes
2647     that are affected are some tapes from the level set "Walpurgis Gardens" by
2648     Jamie Cullen.
2649     The second condition is an exception from the above case and is needed for
2650     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2651     above (including some development versions of 3.1.0), but before it was
2652     known that this change would break tapes like the above and was fixed in
2653     3.1.1, so that the changed behaviour was active although the engine version
2654     while recording maybe was before 3.1.0. There is at least one tape that is
2655     affected by this exception, which is the tape for the one-level set "Bug
2656     Machine" by Juergen Bonhagen.
2657   */
2658
2659   game.use_change_when_pushing_bug =
2660     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2661      !(tape.playing &&
2662        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2663        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2664
2665   /*
2666     Summary of bugfix/change:
2667     Fixed handling for blocking the field the player leaves when moving.
2668
2669     Fixed/changed in version:
2670     3.1.1
2671
2672     Description:
2673     Before 3.1.1, when "block last field when moving" was enabled, the field
2674     the player is leaving when moving was blocked for the time of the move,
2675     and was directly unblocked afterwards. This resulted in the last field
2676     being blocked for exactly one less than the number of frames of one player
2677     move. Additionally, even when blocking was disabled, the last field was
2678     blocked for exactly one frame.
2679     Since 3.1.1, due to changes in player movement handling, the last field
2680     is not blocked at all when blocking is disabled. When blocking is enabled,
2681     the last field is blocked for exactly the number of frames of one player
2682     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2683     last field is blocked for exactly one more than the number of frames of
2684     one player move.
2685
2686     Affected levels/tapes:
2687     (!!! yet to be determined -- probably many !!!)
2688   */
2689
2690   game.use_block_last_field_bug =
2691     (game.engine_version < VERSION_IDENT(3,1,1,0));
2692
2693   /* ---------------------------------------------------------------------- */
2694
2695   /* set maximal allowed number of custom element changes per game frame */
2696   game.max_num_changes_per_frame = 1;
2697
2698   /* default scan direction: scan playfield from top/left to bottom/right */
2699   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2700
2701   /* dynamically adjust element properties according to game engine version */
2702   InitElementPropertiesEngine(game.engine_version);
2703
2704 #if 0
2705   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2706   printf("          tape version == %06d [%s] [file: %06d]\n",
2707          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2708          tape.file_version);
2709   printf("       => game.engine_version == %06d\n", game.engine_version);
2710 #endif
2711
2712   /* ---------- initialize player's initial move delay --------------------- */
2713
2714   /* dynamically adjust player properties according to level information */
2715   for (i = 0; i < MAX_PLAYERS; i++)
2716     game.initial_move_delay_value[i] =
2717       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2718
2719   /* dynamically adjust player properties according to game engine version */
2720   for (i = 0; i < MAX_PLAYERS; i++)
2721     game.initial_move_delay[i] =
2722       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2723        game.initial_move_delay_value[i] : 0);
2724
2725   /* ---------- initialize player's initial push delay --------------------- */
2726
2727   /* dynamically adjust player properties according to game engine version */
2728   game.initial_push_delay_value =
2729     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2730
2731   /* ---------- initialize changing elements ------------------------------- */
2732
2733   /* initialize changing elements information */
2734   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2735   {
2736     struct ElementInfo *ei = &element_info[i];
2737
2738     /* this pointer might have been changed in the level editor */
2739     ei->change = &ei->change_page[0];
2740
2741     if (!IS_CUSTOM_ELEMENT(i))
2742     {
2743       ei->change->target_element = EL_EMPTY_SPACE;
2744       ei->change->delay_fixed = 0;
2745       ei->change->delay_random = 0;
2746       ei->change->delay_frames = 1;
2747     }
2748
2749     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2750     {
2751       ei->has_change_event[j] = FALSE;
2752
2753       ei->event_page_nr[j] = 0;
2754       ei->event_page[j] = &ei->change_page[0];
2755     }
2756   }
2757
2758   /* add changing elements from pre-defined list */
2759   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2760   {
2761     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2762     struct ElementInfo *ei = &element_info[ch_delay->element];
2763
2764     ei->change->target_element       = ch_delay->target_element;
2765     ei->change->delay_fixed          = ch_delay->change_delay;
2766
2767     ei->change->pre_change_function  = ch_delay->pre_change_function;
2768     ei->change->change_function      = ch_delay->change_function;
2769     ei->change->post_change_function = ch_delay->post_change_function;
2770
2771     ei->change->can_change = TRUE;
2772     ei->change->can_change_or_has_action = TRUE;
2773
2774     ei->has_change_event[CE_DELAY] = TRUE;
2775
2776     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2777     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2778   }
2779
2780   /* ---------- initialize internal run-time variables --------------------- */
2781
2782   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2783   {
2784     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2785
2786     for (j = 0; j < ei->num_change_pages; j++)
2787     {
2788       ei->change_page[j].can_change_or_has_action =
2789         (ei->change_page[j].can_change |
2790          ei->change_page[j].has_action);
2791     }
2792   }
2793
2794   /* add change events from custom element configuration */
2795   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2796   {
2797     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2798
2799     for (j = 0; j < ei->num_change_pages; j++)
2800     {
2801       if (!ei->change_page[j].can_change_or_has_action)
2802         continue;
2803
2804       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2805       {
2806         /* only add event page for the first page found with this event */
2807         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2808         {
2809           ei->has_change_event[k] = TRUE;
2810
2811           ei->event_page_nr[k] = j;
2812           ei->event_page[k] = &ei->change_page[j];
2813         }
2814       }
2815     }
2816   }
2817
2818   /* ---------- initialize reference elements in change conditions --------- */
2819
2820   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2821   {
2822     int element = EL_CUSTOM_START + i;
2823     struct ElementInfo *ei = &element_info[element];
2824
2825     for (j = 0; j < ei->num_change_pages; j++)
2826     {
2827       int trigger_element = ei->change_page[j].initial_trigger_element;
2828
2829       if (trigger_element >= EL_PREV_CE_8 &&
2830           trigger_element <= EL_NEXT_CE_8)
2831         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2832
2833       ei->change_page[j].trigger_element = trigger_element;
2834     }
2835   }
2836
2837   /* ---------- initialize run-time trigger player and element ------------- */
2838
2839   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2840   {
2841     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2842
2843     for (j = 0; j < ei->num_change_pages; j++)
2844     {
2845       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2846       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2847       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2848       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2849       ei->change_page[j].actual_trigger_ce_value = 0;
2850       ei->change_page[j].actual_trigger_ce_score = 0;
2851     }
2852   }
2853
2854   /* ---------- initialize trigger events ---------------------------------- */
2855
2856   /* initialize trigger events information */
2857   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2858     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2859       trigger_events[i][j] = FALSE;
2860
2861   /* add trigger events from element change event properties */
2862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863   {
2864     struct ElementInfo *ei = &element_info[i];
2865
2866     for (j = 0; j < ei->num_change_pages; j++)
2867     {
2868       if (!ei->change_page[j].can_change_or_has_action)
2869         continue;
2870
2871       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2872       {
2873         int trigger_element = ei->change_page[j].trigger_element;
2874
2875         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2876         {
2877           if (ei->change_page[j].has_event[k])
2878           {
2879             if (IS_GROUP_ELEMENT(trigger_element))
2880             {
2881               struct ElementGroupInfo *group =
2882                 element_info[trigger_element].group;
2883
2884               for (l = 0; l < group->num_elements_resolved; l++)
2885                 trigger_events[group->element_resolved[l]][k] = TRUE;
2886             }
2887             else if (trigger_element == EL_ANY_ELEMENT)
2888               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2889                 trigger_events[l][k] = TRUE;
2890             else
2891               trigger_events[trigger_element][k] = TRUE;
2892           }
2893         }
2894       }
2895     }
2896   }
2897
2898   /* ---------- initialize push delay -------------------------------------- */
2899
2900   /* initialize push delay values to default */
2901   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2902   {
2903     if (!IS_CUSTOM_ELEMENT(i))
2904     {
2905       /* set default push delay values (corrected since version 3.0.7-1) */
2906       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2907       {
2908         element_info[i].push_delay_fixed = 2;
2909         element_info[i].push_delay_random = 8;
2910       }
2911       else
2912       {
2913         element_info[i].push_delay_fixed = 8;
2914         element_info[i].push_delay_random = 8;
2915       }
2916     }
2917   }
2918
2919   /* set push delay value for certain elements from pre-defined list */
2920   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2921   {
2922     int e = push_delay_list[i].element;
2923
2924     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2925     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2926   }
2927
2928   /* set push delay value for Supaplex elements for newer engine versions */
2929   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2930   {
2931     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2932     {
2933       if (IS_SP_ELEMENT(i))
2934       {
2935         /* set SP push delay to just enough to push under a falling zonk */
2936         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2937
2938         element_info[i].push_delay_fixed  = delay;
2939         element_info[i].push_delay_random = 0;
2940       }
2941     }
2942   }
2943
2944   /* ---------- initialize move stepsize ----------------------------------- */
2945
2946   /* initialize move stepsize values to default */
2947   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2948     if (!IS_CUSTOM_ELEMENT(i))
2949       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2950
2951   /* set move stepsize value for certain elements from pre-defined list */
2952   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2953   {
2954     int e = move_stepsize_list[i].element;
2955
2956     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2957   }
2958
2959   /* ---------- initialize collect score ----------------------------------- */
2960
2961   /* initialize collect score values for custom elements from initial value */
2962   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2963     if (IS_CUSTOM_ELEMENT(i))
2964       element_info[i].collect_score = element_info[i].collect_score_initial;
2965
2966   /* ---------- initialize collect count ----------------------------------- */
2967
2968   /* initialize collect count values for non-custom elements */
2969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2970     if (!IS_CUSTOM_ELEMENT(i))
2971       element_info[i].collect_count_initial = 0;
2972
2973   /* add collect count values for all elements from pre-defined list */
2974   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2975     element_info[collect_count_list[i].element].collect_count_initial =
2976       collect_count_list[i].count;
2977
2978   /* ---------- initialize access direction -------------------------------- */
2979
2980   /* initialize access direction values to default (access from every side) */
2981   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2982     if (!IS_CUSTOM_ELEMENT(i))
2983       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2984
2985   /* set access direction value for certain elements from pre-defined list */
2986   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2987     element_info[access_direction_list[i].element].access_direction =
2988       access_direction_list[i].direction;
2989
2990   /* ---------- initialize explosion content ------------------------------- */
2991   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2992   {
2993     if (IS_CUSTOM_ELEMENT(i))
2994       continue;
2995
2996     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2997     {
2998       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2999
3000       element_info[i].content.e[x][y] =
3001         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3002          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3003          i == EL_PLAYER_3 ? EL_EMERALD :
3004          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3005          i == EL_MOLE ? EL_EMERALD_RED :
3006          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3007          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3008          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3009          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3010          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3011          i == EL_WALL_EMERALD ? EL_EMERALD :
3012          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3013          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3014          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3015          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3016          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3017          i == EL_WALL_PEARL ? EL_PEARL :
3018          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3019          EL_EMPTY);
3020     }
3021   }
3022
3023   /* ---------- initialize recursion detection ------------------------------ */
3024   recursion_loop_depth = 0;
3025   recursion_loop_detected = FALSE;
3026   recursion_loop_element = EL_UNDEFINED;
3027
3028   /* ---------- initialize graphics engine ---------------------------------- */
3029   game.scroll_delay_value =
3030     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3031      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3032   game.scroll_delay_value =
3033     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3034 }
3035
3036 int get_num_special_action(int element, int action_first, int action_last)
3037 {
3038   int num_special_action = 0;
3039   int i, j;
3040
3041   for (i = action_first; i <= action_last; i++)
3042   {
3043     boolean found = FALSE;
3044
3045     for (j = 0; j < NUM_DIRECTIONS; j++)
3046       if (el_act_dir2img(element, i, j) !=
3047           el_act_dir2img(element, ACTION_DEFAULT, j))
3048         found = TRUE;
3049
3050     if (found)
3051       num_special_action++;
3052     else
3053       break;
3054   }
3055
3056   return num_special_action;
3057 }
3058
3059
3060 /*
3061   =============================================================================
3062   InitGame()
3063   -----------------------------------------------------------------------------
3064   initialize and start new game
3065   =============================================================================
3066 */
3067
3068 void InitGame()
3069 {
3070   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3071   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3072
3073   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3074   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3075   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3076   int initial_move_dir = MV_DOWN;
3077   int i, j, x, y;
3078
3079   game_status = GAME_MODE_PLAYING;
3080
3081   StopAnimation();
3082
3083   if (!game.restart_level)
3084     CloseDoor(DOOR_CLOSE_1);
3085
3086   if (level_editor_test_game)
3087     FadeSkipNextFadeIn();
3088   else
3089     FadeSetEnterScreen();
3090
3091   FadeOut(REDRAW_FIELD);
3092
3093   /* needed if different viewport properties defined for playing */
3094   ChangeViewportPropertiesIfNeeded();
3095
3096   DrawCompleteVideoDisplay();
3097
3098   InitGameEngine();
3099   InitGameControlValues();
3100
3101   /* don't play tapes over network */
3102   network_playing = (options.network && !tape.playing);
3103
3104   for (i = 0; i < MAX_PLAYERS; i++)
3105   {
3106     struct PlayerInfo *player = &stored_player[i];
3107
3108     player->index_nr = i;
3109     player->index_bit = (1 << i);
3110     player->element_nr = EL_PLAYER_1 + i;
3111
3112     player->present = FALSE;
3113     player->active = FALSE;
3114     player->mapped = FALSE;
3115
3116     player->killed = FALSE;
3117     player->reanimated = FALSE;
3118
3119     player->action = 0;
3120     player->effective_action = 0;
3121     player->programmed_action = 0;
3122
3123     player->score = 0;
3124     player->score_final = 0;
3125
3126     player->gems_still_needed = level.gems_needed;
3127     player->sokobanfields_still_needed = 0;
3128     player->lights_still_needed = 0;
3129     player->friends_still_needed = 0;
3130
3131     for (j = 0; j < MAX_NUM_KEYS; j++)
3132       player->key[j] = FALSE;
3133
3134     player->num_white_keys = 0;
3135
3136     player->dynabomb_count = 0;
3137     player->dynabomb_size = 1;
3138     player->dynabombs_left = 0;
3139     player->dynabomb_xl = FALSE;
3140
3141     player->MovDir = initial_move_dir;
3142     player->MovPos = 0;
3143     player->GfxPos = 0;
3144     player->GfxDir = initial_move_dir;
3145     player->GfxAction = ACTION_DEFAULT;
3146     player->Frame = 0;
3147     player->StepFrame = 0;
3148
3149     player->initial_element = player->element_nr;
3150     player->artwork_element =
3151       (level.use_artwork_element[i] ? level.artwork_element[i] :
3152        player->element_nr);
3153     player->use_murphy = FALSE;
3154
3155     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3156     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3157
3158     player->gravity = level.initial_player_gravity[i];
3159
3160     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3161
3162     player->actual_frame_counter = 0;
3163
3164     player->step_counter = 0;
3165
3166     player->last_move_dir = initial_move_dir;
3167
3168     player->is_active = FALSE;
3169
3170     player->is_waiting = FALSE;
3171     player->is_moving = FALSE;
3172     player->is_auto_moving = FALSE;
3173     player->is_digging = FALSE;
3174     player->is_snapping = FALSE;
3175     player->is_collecting = FALSE;
3176     player->is_pushing = FALSE;
3177     player->is_switching = FALSE;
3178     player->is_dropping = FALSE;
3179     player->is_dropping_pressed = FALSE;
3180
3181     player->is_bored = FALSE;
3182     player->is_sleeping = FALSE;
3183
3184     player->frame_counter_bored = -1;
3185     player->frame_counter_sleeping = -1;
3186
3187     player->anim_delay_counter = 0;
3188     player->post_delay_counter = 0;
3189
3190     player->dir_waiting = initial_move_dir;
3191     player->action_waiting = ACTION_DEFAULT;
3192     player->last_action_waiting = ACTION_DEFAULT;
3193     player->special_action_bored = ACTION_DEFAULT;
3194     player->special_action_sleeping = ACTION_DEFAULT;
3195
3196     player->switch_x = -1;
3197     player->switch_y = -1;
3198
3199     player->drop_x = -1;
3200     player->drop_y = -1;
3201
3202     player->show_envelope = 0;
3203
3204     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3205
3206     player->push_delay       = -1;      /* initialized when pushing starts */
3207     player->push_delay_value = game.initial_push_delay_value;
3208
3209     player->drop_delay = 0;
3210     player->drop_pressed_delay = 0;
3211
3212     player->last_jx = -1;
3213     player->last_jy = -1;
3214     player->jx = -1;
3215     player->jy = -1;
3216
3217     player->shield_normal_time_left = 0;
3218     player->shield_deadly_time_left = 0;
3219
3220     player->inventory_infinite_element = EL_UNDEFINED;
3221     player->inventory_size = 0;
3222
3223     if (level.use_initial_inventory[i])
3224     {
3225       for (j = 0; j < level.initial_inventory_size[i]; j++)
3226       {
3227         int element = level.initial_inventory_content[i][j];
3228         int collect_count = element_info[element].collect_count_initial;
3229         int k;
3230
3231         if (!IS_CUSTOM_ELEMENT(element))
3232           collect_count = 1;
3233
3234         if (collect_count == 0)
3235           player->inventory_infinite_element = element;
3236         else
3237           for (k = 0; k < collect_count; k++)
3238             if (player->inventory_size < MAX_INVENTORY_SIZE)
3239               player->inventory_element[player->inventory_size++] = element;
3240       }
3241     }
3242
3243     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3244     SnapField(player, 0, 0);
3245
3246     player->LevelSolved = FALSE;
3247     player->GameOver = FALSE;
3248
3249     player->LevelSolved_GameWon = FALSE;
3250     player->LevelSolved_GameEnd = FALSE;
3251     player->LevelSolved_PanelOff = FALSE;
3252     player->LevelSolved_SaveTape = FALSE;
3253     player->LevelSolved_SaveScore = FALSE;
3254     player->LevelSolved_CountingTime = 0;
3255     player->LevelSolved_CountingScore = 0;
3256
3257     map_player_action[i] = i;
3258   }
3259
3260   network_player_action_received = FALSE;
3261
3262 #if defined(NETWORK_AVALIABLE)
3263   /* initial null action */
3264   if (network_playing)
3265     SendToServer_MovePlayer(MV_NONE);
3266 #endif
3267
3268   ZX = ZY = -1;
3269   ExitX = ExitY = -1;
3270
3271   FrameCounter = 0;
3272   TimeFrames = 0;
3273   TimePlayed = 0;
3274   TimeLeft = level.time;
3275   TapeTime = 0;
3276
3277   ScreenMovDir = MV_NONE;
3278   ScreenMovPos = 0;
3279   ScreenGfxPos = 0;
3280
3281   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3282
3283   AllPlayersGone = FALSE;
3284
3285   game.no_time_limit = (level.time == 0);
3286
3287   game.yamyam_content_nr = 0;
3288   game.robot_wheel_active = FALSE;
3289   game.magic_wall_active = FALSE;
3290   game.magic_wall_time_left = 0;
3291   game.light_time_left = 0;
3292   game.timegate_time_left = 0;
3293   game.switchgate_pos = 0;
3294   game.wind_direction = level.wind_direction_initial;
3295
3296   game.lenses_time_left = 0;
3297   game.magnify_time_left = 0;
3298
3299   game.ball_state = level.ball_state_initial;
3300   game.ball_content_nr = 0;
3301
3302   game.envelope_active = FALSE;
3303
3304   /* set focus to local player for network games, else to all players */
3305   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3306   game.centered_player_nr_next = game.centered_player_nr;
3307   game.set_centered_player = FALSE;
3308
3309   if (network_playing && tape.recording)
3310   {
3311     /* store client dependent player focus when recording network games */
3312     tape.centered_player_nr_next = game.centered_player_nr_next;
3313     tape.set_centered_player = TRUE;
3314   }
3315
3316   for (i = 0; i < NUM_BELTS; i++)
3317   {
3318     game.belt_dir[i] = MV_NONE;
3319     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3320   }
3321
3322   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3323     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3324
3325 #if DEBUG_INIT_PLAYER
3326   if (options.debug)
3327   {
3328     printf("Player status at level initialization:\n");
3329   }
3330 #endif
3331
3332   SCAN_PLAYFIELD(x, y)
3333   {
3334     Feld[x][y] = level.field[x][y];
3335     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3336     ChangeDelay[x][y] = 0;
3337     ChangePage[x][y] = -1;
3338     CustomValue[x][y] = 0;              /* initialized in InitField() */
3339     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3340     AmoebaNr[x][y] = 0;
3341     WasJustMoving[x][y] = 0;
3342     WasJustFalling[x][y] = 0;
3343     CheckCollision[x][y] = 0;
3344     CheckImpact[x][y] = 0;
3345     Stop[x][y] = FALSE;
3346     Pushed[x][y] = FALSE;
3347
3348     ChangeCount[x][y] = 0;
3349     ChangeEvent[x][y] = -1;
3350
3351     ExplodePhase[x][y] = 0;
3352     ExplodeDelay[x][y] = 0;
3353     ExplodeField[x][y] = EX_TYPE_NONE;
3354
3355     RunnerVisit[x][y] = 0;
3356     PlayerVisit[x][y] = 0;
3357
3358     GfxFrame[x][y] = 0;
3359     GfxRandom[x][y] = INIT_GFX_RANDOM();
3360     GfxElement[x][y] = EL_UNDEFINED;
3361     GfxAction[x][y] = ACTION_DEFAULT;
3362     GfxDir[x][y] = MV_NONE;
3363     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3364   }
3365
3366   SCAN_PLAYFIELD(x, y)
3367   {
3368     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3369       emulate_bd = FALSE;
3370     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3371       emulate_sb = FALSE;
3372     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3373       emulate_sp = FALSE;
3374
3375     InitField(x, y, TRUE);
3376
3377     ResetGfxAnimation(x, y);
3378   }
3379
3380   InitBeltMovement();
3381
3382   for (i = 0; i < MAX_PLAYERS; i++)
3383   {
3384     struct PlayerInfo *player = &stored_player[i];
3385
3386     /* set number of special actions for bored and sleeping animation */
3387     player->num_special_action_bored =
3388       get_num_special_action(player->artwork_element,
3389                              ACTION_BORING_1, ACTION_BORING_LAST);
3390     player->num_special_action_sleeping =
3391       get_num_special_action(player->artwork_element,
3392                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3393   }
3394
3395   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3396                     emulate_sb ? EMU_SOKOBAN :
3397                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3398
3399   /* initialize type of slippery elements */
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401   {
3402     if (!IS_CUSTOM_ELEMENT(i))
3403     {
3404       /* default: elements slip down either to the left or right randomly */
3405       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3406
3407       /* SP style elements prefer to slip down on the left side */
3408       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3409         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3410
3411       /* BD style elements prefer to slip down on the left side */
3412       if (game.emulation == EMU_BOULDERDASH)
3413         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3414     }
3415   }
3416
3417   /* initialize explosion and ignition delay */
3418   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3419   {
3420     if (!IS_CUSTOM_ELEMENT(i))
3421     {
3422       int num_phase = 8;
3423       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3424                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3425                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3426       int last_phase = (num_phase + 1) * delay;
3427       int half_phase = (num_phase / 2) * delay;
3428
3429       element_info[i].explosion_delay = last_phase - 1;
3430       element_info[i].ignition_delay = half_phase;
3431
3432       if (i == EL_BLACK_ORB)
3433         element_info[i].ignition_delay = 1;
3434     }
3435   }
3436
3437   /* correct non-moving belts to start moving left */
3438   for (i = 0; i < NUM_BELTS; i++)
3439     if (game.belt_dir[i] == MV_NONE)
3440       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3441
3442 #if USE_NEW_PLAYER_ASSIGNMENTS
3443   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3444   /* choose default local player */
3445   local_player = &stored_player[0];
3446
3447   for (i = 0; i < MAX_PLAYERS; i++)
3448     stored_player[i].connected = FALSE;
3449
3450   local_player->connected = TRUE;
3451   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3452
3453   if (tape.playing)
3454   {
3455     for (i = 0; i < MAX_PLAYERS; i++)
3456       stored_player[i].connected = tape.player_participates[i];
3457   }
3458   else if (game.team_mode && !options.network)
3459   {
3460     /* try to guess locally connected team mode players (needed for correct
3461        assignment of player figures from level to locally playing players) */
3462
3463     for (i = 0; i < MAX_PLAYERS; i++)
3464       if (setup.input[i].use_joystick ||
3465           setup.input[i].key.left != KSYM_UNDEFINED)
3466         stored_player[i].connected = TRUE;
3467   }
3468
3469 #if DEBUG_INIT_PLAYER
3470   if (options.debug)
3471   {
3472     printf("Player status after level initialization:\n");
3473
3474     for (i = 0; i < MAX_PLAYERS; i++)
3475     {
3476       struct PlayerInfo *player = &stored_player[i];
3477
3478       printf("- player %d: present == %d, connected == %d, active == %d",
3479              i + 1,
3480              player->present,
3481              player->connected,
3482              player->active);
3483
3484       if (local_player == player)
3485         printf(" (local player)");
3486
3487       printf("\n");
3488     }
3489   }
3490 #endif
3491
3492 #if DEBUG_INIT_PLAYER
3493   if (options.debug)
3494     printf("Reassigning players ...\n");
3495 #endif
3496
3497   /* check if any connected player was not found in playfield */
3498   for (i = 0; i < MAX_PLAYERS; i++)
3499   {
3500     struct PlayerInfo *player = &stored_player[i];
3501
3502     if (player->connected && !player->present)
3503     {
3504       struct PlayerInfo *field_player = NULL;
3505
3506 #if DEBUG_INIT_PLAYER
3507       if (options.debug)
3508         printf("- looking for field player for player %d ...\n", i + 1);
3509 #endif
3510
3511       /* assign first free player found that is present in the playfield */
3512
3513       /* first try: look for unmapped playfield player that is not connected */
3514       for (j = 0; j < MAX_PLAYERS; j++)
3515         if (field_player == NULL &&
3516             stored_player[j].present &&
3517             !stored_player[j].mapped &&
3518             !stored_player[j].connected)
3519           field_player = &stored_player[j];
3520
3521       /* second try: look for *any* unmapped playfield player */
3522       for (j = 0; j < MAX_PLAYERS; j++)
3523         if (field_player == NULL &&
3524             stored_player[j].present &&
3525             !stored_player[j].mapped)
3526           field_player = &stored_player[j];
3527
3528       if (field_player != NULL)
3529       {
3530         int jx = field_player->jx, jy = field_player->jy;
3531
3532 #if DEBUG_INIT_PLAYER
3533         if (options.debug)
3534           printf("- found player %d\n", field_player->index_nr + 1);
3535 #endif
3536
3537         player->present = FALSE;
3538         player->active = FALSE;
3539
3540         field_player->present = TRUE;
3541         field_player->active = TRUE;
3542
3543         /*
3544         player->initial_element = field_player->initial_element;
3545         player->artwork_element = field_player->artwork_element;
3546
3547         player->block_last_field       = field_player->block_last_field;
3548         player->block_delay_adjustment = field_player->block_delay_adjustment;
3549         */
3550
3551         StorePlayer[jx][jy] = field_player->element_nr;
3552
3553         field_player->jx = field_player->last_jx = jx;
3554         field_player->jy = field_player->last_jy = jy;
3555
3556         if (local_player == player)
3557           local_player = field_player;
3558
3559         map_player_action[field_player->index_nr] = i;
3560
3561         field_player->mapped = TRUE;
3562
3563 #if DEBUG_INIT_PLAYER
3564         if (options.debug)
3565           printf("- map_player_action[%d] == %d\n",
3566                  field_player->index_nr + 1, i + 1);
3567 #endif
3568       }
3569     }
3570
3571     if (player->connected && player->present)
3572       player->mapped = TRUE;
3573   }
3574
3575 #if DEBUG_INIT_PLAYER
3576   if (options.debug)
3577   {
3578     printf("Player status after player assignment (first stage):\n");
3579
3580     for (i = 0; i < MAX_PLAYERS; i++)
3581     {
3582       struct PlayerInfo *player = &stored_player[i];
3583
3584       printf("- player %d: present == %d, connected == %d, active == %d",
3585              i + 1,
3586              player->present,
3587              player->connected,
3588              player->active);
3589
3590       if (local_player == player)
3591         printf(" (local player)");
3592
3593       printf("\n");
3594     }
3595   }
3596 #endif
3597
3598 #else
3599
3600   /* check if any connected player was not found in playfield */
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     if (player->connected && !player->present)
3606     {
3607       for (j = 0; j < MAX_PLAYERS; j++)
3608       {
3609         struct PlayerInfo *field_player = &stored_player[j];
3610         int jx = field_player->jx, jy = field_player->jy;
3611
3612         /* assign first free player found that is present in the playfield */
3613         if (field_player->present && !field_player->connected)
3614         {
3615           player->present = TRUE;
3616           player->active = TRUE;
3617
3618           field_player->present = FALSE;
3619           field_player->active = FALSE;
3620
3621           player->initial_element = field_player->initial_element;
3622           player->artwork_element = field_player->artwork_element;
3623
3624           player->block_last_field       = field_player->block_last_field;
3625           player->block_delay_adjustment = field_player->block_delay_adjustment;
3626
3627           StorePlayer[jx][jy] = player->element_nr;
3628
3629           player->jx = player->last_jx = jx;
3630           player->jy = player->last_jy = jy;
3631
3632           break;
3633         }
3634       }
3635     }
3636   }
3637 #endif
3638
3639 #if 0
3640   printf("::: local_player->present == %d\n", local_player->present);
3641 #endif
3642
3643   if (tape.playing)
3644   {
3645     /* when playing a tape, eliminate all players who do not participate */
3646
3647 #if USE_NEW_PLAYER_ASSIGNMENTS
3648
3649     if (!game.team_mode)
3650     {
3651       for (i = 0; i < MAX_PLAYERS; i++)
3652       {
3653         if (stored_player[i].active &&
3654             !tape.player_participates[map_player_action[i]])
3655         {
3656           struct PlayerInfo *player = &stored_player[i];
3657           int jx = player->jx, jy = player->jy;
3658
3659 #if DEBUG_INIT_PLAYER
3660           if (options.debug)
3661             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3662 #endif
3663
3664           player->active = FALSE;
3665           StorePlayer[jx][jy] = 0;
3666           Feld[jx][jy] = EL_EMPTY;
3667         }
3668       }
3669     }
3670
3671 #else
3672
3673     for (i = 0; i < MAX_PLAYERS; i++)
3674     {
3675       if (stored_player[i].active &&
3676           !tape.player_participates[i])
3677       {
3678         struct PlayerInfo *player = &stored_player[i];
3679         int jx = player->jx, jy = player->jy;
3680
3681         player->active = FALSE;
3682         StorePlayer[jx][jy] = 0;
3683         Feld[jx][jy] = EL_EMPTY;
3684       }
3685     }
3686 #endif
3687   }
3688   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3689   {
3690     /* when in single player mode, eliminate all but the first active player */
3691
3692     for (i = 0; i < MAX_PLAYERS; i++)
3693     {
3694       if (stored_player[i].active)
3695       {
3696         for (j = i + 1; j < MAX_PLAYERS; j++)
3697         {
3698           if (stored_player[j].active)
3699           {
3700             struct PlayerInfo *player = &stored_player[j];
3701             int jx = player->jx, jy = player->jy;
3702
3703             player->active = FALSE;
3704             player->present = FALSE;
3705
3706             StorePlayer[jx][jy] = 0;
3707             Feld[jx][jy] = EL_EMPTY;
3708           }
3709         }
3710       }
3711     }
3712   }
3713
3714   /* when recording the game, store which players take part in the game */
3715   if (tape.recording)
3716   {
3717 #if USE_NEW_PLAYER_ASSIGNMENTS
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719       if (stored_player[i].connected)
3720         tape.player_participates[i] = TRUE;
3721 #else
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       if (stored_player[i].active)
3724         tape.player_participates[i] = TRUE;
3725 #endif
3726   }
3727
3728 #if DEBUG_INIT_PLAYER
3729   if (options.debug)
3730   {
3731     printf("Player status after player assignment (final stage):\n");
3732
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734     {
3735       struct PlayerInfo *player = &stored_player[i];
3736
3737       printf("- player %d: present == %d, connected == %d, active == %d",
3738              i + 1,
3739              player->present,
3740              player->connected,
3741              player->active);
3742
3743       if (local_player == player)
3744         printf(" (local player)");
3745
3746       printf("\n");
3747     }
3748   }
3749 #endif
3750
3751   if (BorderElement == EL_EMPTY)
3752   {
3753     SBX_Left = 0;
3754     SBX_Right = lev_fieldx - SCR_FIELDX;
3755     SBY_Upper = 0;
3756     SBY_Lower = lev_fieldy - SCR_FIELDY;
3757   }
3758   else
3759   {
3760     SBX_Left = -1;
3761     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3762     SBY_Upper = -1;
3763     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3764   }
3765
3766   if (full_lev_fieldx <= SCR_FIELDX)
3767     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3768   if (full_lev_fieldy <= SCR_FIELDY)
3769     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3770
3771   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3772     SBX_Left--;
3773   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3774     SBY_Upper--;
3775
3776   /* if local player not found, look for custom element that might create
3777      the player (make some assumptions about the right custom element) */
3778   if (!local_player->present)
3779   {
3780     int start_x = 0, start_y = 0;
3781     int found_rating = 0;
3782     int found_element = EL_UNDEFINED;
3783     int player_nr = local_player->index_nr;
3784
3785     SCAN_PLAYFIELD(x, y)
3786     {
3787       int element = Feld[x][y];
3788       int content;
3789       int xx, yy;
3790       boolean is_player;
3791
3792       if (level.use_start_element[player_nr] &&
3793           level.start_element[player_nr] == element &&
3794           found_rating < 4)
3795       {
3796         start_x = x;
3797         start_y = y;
3798
3799         found_rating = 4;
3800         found_element = element;
3801       }
3802
3803       if (!IS_CUSTOM_ELEMENT(element))
3804         continue;
3805
3806       if (CAN_CHANGE(element))
3807       {
3808         for (i = 0; i < element_info[element].num_change_pages; i++)
3809         {
3810           /* check for player created from custom element as single target */
3811           content = element_info[element].change_page[i].target_element;
3812           is_player = ELEM_IS_PLAYER(content);
3813
3814           if (is_player && (found_rating < 3 ||
3815                             (found_rating == 3 && element < found_element)))
3816           {
3817             start_x = x;
3818             start_y = y;
3819
3820             found_rating = 3;
3821             found_element = element;
3822           }
3823         }
3824       }
3825
3826       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3827       {
3828         /* check for player created from custom element as explosion content */
3829         content = element_info[element].content.e[xx][yy];
3830         is_player = ELEM_IS_PLAYER(content);
3831
3832         if (is_player && (found_rating < 2 ||
3833                           (found_rating == 2 && element < found_element)))
3834         {
3835           start_x = x + xx - 1;
3836           start_y = y + yy - 1;
3837
3838           found_rating = 2;
3839           found_element = element;
3840         }
3841
3842         if (!CAN_CHANGE(element))
3843           continue;
3844
3845         for (i = 0; i < element_info[element].num_change_pages; i++)
3846         {
3847           /* check for player created from custom element as extended target */
3848           content =
3849             element_info[element].change_page[i].target_content.e[xx][yy];
3850
3851           is_player = ELEM_IS_PLAYER(content);
3852
3853           if (is_player && (found_rating < 1 ||
3854                             (found_rating == 1 && element < found_element)))
3855           {
3856             start_x = x + xx - 1;
3857             start_y = y + yy - 1;
3858
3859             found_rating = 1;
3860             found_element = element;
3861           }
3862         }
3863       }
3864     }
3865
3866     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3867                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3868                 start_x - MIDPOSX);
3869
3870     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3871                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3872                 start_y - MIDPOSY);
3873   }
3874   else
3875   {
3876     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3877                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3878                 local_player->jx - MIDPOSX);
3879
3880     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3881                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3882                 local_player->jy - MIDPOSY);
3883   }
3884
3885   /* !!! FIX THIS (START) !!! */
3886   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3887   {
3888     InitGameEngine_EM();
3889   }
3890   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3891   {
3892     InitGameEngine_SP();
3893   }
3894   else
3895   {
3896     DrawLevel(REDRAW_FIELD);
3897     DrawAllPlayers();
3898
3899     /* after drawing the level, correct some elements */
3900     if (game.timegate_time_left == 0)
3901       CloseAllOpenTimegates();
3902   }
3903
3904   /* blit playfield from scroll buffer to normal back buffer for fading in */
3905   BlitScreenToBitmap(backbuffer);
3906
3907   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3908   /* !!! FIX THIS (END) !!! */
3909
3910   FadeIn(REDRAW_FIELD);
3911
3912 #if 1
3913   // full screen redraw is required at this point in the following cases:
3914   // - special editor door undrawn when game was started from level editor
3915   // - drawing area (playfield) was changed and has to be removed completely
3916   redraw_mask = REDRAW_ALL;
3917   BackToFront();
3918 #endif
3919
3920   if (!game.restart_level)
3921   {
3922     /* copy default game door content to main double buffer */
3923
3924     /* !!! CHECK AGAIN !!! */
3925     SetPanelBackground();
3926     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3927     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3928   }
3929
3930   SetPanelBackground();
3931   SetDrawBackgroundMask(REDRAW_DOOR_1);
3932
3933   UpdateAndDisplayGameControlValues();
3934
3935   if (!game.restart_level)
3936   {
3937     UnmapGameButtons();
3938     UnmapTapeButtons();
3939     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3940     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3941     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3942     MapGameButtons();
3943     MapTapeButtons();
3944
3945     /* copy actual game door content to door double buffer for OpenDoor() */
3946     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3947
3948     OpenDoor(DOOR_OPEN_ALL);
3949
3950     PlaySound(SND_GAME_STARTING);
3951
3952     if (setup.sound_music)
3953       PlayLevelMusic();
3954
3955     KeyboardAutoRepeatOffUnlessAutoplay();
3956
3957 #if DEBUG_INIT_PLAYER
3958     if (options.debug)
3959     {
3960       printf("Player status (final):\n");
3961
3962       for (i = 0; i < MAX_PLAYERS; i++)
3963       {
3964         struct PlayerInfo *player = &stored_player[i];
3965
3966         printf("- player %d: present == %d, connected == %d, active == %d",
3967                i + 1,
3968                player->present,
3969                player->connected,
3970                player->active);
3971
3972         if (local_player == player)
3973           printf(" (local player)");
3974
3975         printf("\n");
3976       }
3977     }
3978 #endif
3979   }
3980
3981   UnmapAllGadgets();
3982
3983   MapGameButtons();
3984   MapTapeButtons();
3985
3986   if (!game.restart_level && !tape.playing)
3987   {
3988     LevelStats_incPlayed(level_nr);
3989
3990     SaveLevelSetup_SeriesInfo();
3991   }
3992
3993   game.restart_level = FALSE;
3994 }
3995
3996 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3997 {
3998   /* this is used for non-R'n'D game engines to update certain engine values */
3999
4000   /* needed to determine if sounds are played within the visible screen area */
4001   scroll_x = actual_scroll_x;
4002   scroll_y = actual_scroll_y;
4003 }
4004
4005 void InitMovDir(int x, int y)
4006 {
4007   int i, element = Feld[x][y];
4008   static int xy[4][2] =
4009   {
4010     {  0, +1 },
4011     { +1,  0 },
4012     {  0, -1 },
4013     { -1,  0 }
4014   };
4015   static int direction[3][4] =
4016   {
4017     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4018     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4019     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4020   };
4021
4022   switch (element)
4023   {
4024     case EL_BUG_RIGHT:
4025     case EL_BUG_UP:
4026     case EL_BUG_LEFT:
4027     case EL_BUG_DOWN:
4028       Feld[x][y] = EL_BUG;
4029       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4030       break;
4031
4032     case EL_SPACESHIP_RIGHT:
4033     case EL_SPACESHIP_UP:
4034     case EL_SPACESHIP_LEFT:
4035     case EL_SPACESHIP_DOWN:
4036       Feld[x][y] = EL_SPACESHIP;
4037       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4038       break;
4039
4040     case EL_BD_BUTTERFLY_RIGHT:
4041     case EL_BD_BUTTERFLY_UP:
4042     case EL_BD_BUTTERFLY_LEFT:
4043     case EL_BD_BUTTERFLY_DOWN:
4044       Feld[x][y] = EL_BD_BUTTERFLY;
4045       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4046       break;
4047
4048     case EL_BD_FIREFLY_RIGHT:
4049     case EL_BD_FIREFLY_UP:
4050     case EL_BD_FIREFLY_LEFT:
4051     case EL_BD_FIREFLY_DOWN:
4052       Feld[x][y] = EL_BD_FIREFLY;
4053       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4054       break;
4055
4056     case EL_PACMAN_RIGHT:
4057     case EL_PACMAN_UP:
4058     case EL_PACMAN_LEFT:
4059     case EL_PACMAN_DOWN:
4060       Feld[x][y] = EL_PACMAN;
4061       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4062       break;
4063
4064     case EL_YAMYAM_LEFT:
4065     case EL_YAMYAM_RIGHT:
4066     case EL_YAMYAM_UP:
4067     case EL_YAMYAM_DOWN:
4068       Feld[x][y] = EL_YAMYAM;
4069       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4070       break;
4071
4072     case EL_SP_SNIKSNAK:
4073       MovDir[x][y] = MV_UP;
4074       break;
4075
4076     case EL_SP_ELECTRON:
4077       MovDir[x][y] = MV_LEFT;
4078       break;
4079
4080     case EL_MOLE_LEFT:
4081     case EL_MOLE_RIGHT:
4082     case EL_MOLE_UP:
4083     case EL_MOLE_DOWN:
4084       Feld[x][y] = EL_MOLE;
4085       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4086       break;
4087
4088     default:
4089       if (IS_CUSTOM_ELEMENT(element))
4090       {
4091         struct ElementInfo *ei = &element_info[element];
4092         int move_direction_initial = ei->move_direction_initial;
4093         int move_pattern = ei->move_pattern;
4094
4095         if (move_direction_initial == MV_START_PREVIOUS)
4096         {
4097           if (MovDir[x][y] != MV_NONE)
4098             return;
4099
4100           move_direction_initial = MV_START_AUTOMATIC;
4101         }
4102
4103         if (move_direction_initial == MV_START_RANDOM)
4104           MovDir[x][y] = 1 << RND(4);
4105         else if (move_direction_initial & MV_ANY_DIRECTION)
4106           MovDir[x][y] = move_direction_initial;
4107         else if (move_pattern == MV_ALL_DIRECTIONS ||
4108                  move_pattern == MV_TURNING_LEFT ||
4109                  move_pattern == MV_TURNING_RIGHT ||
4110                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4111                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4112                  move_pattern == MV_TURNING_RANDOM)
4113           MovDir[x][y] = 1 << RND(4);
4114         else if (move_pattern == MV_HORIZONTAL)
4115           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4116         else if (move_pattern == MV_VERTICAL)
4117           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4118         else if (move_pattern & MV_ANY_DIRECTION)
4119           MovDir[x][y] = element_info[element].move_pattern;
4120         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4121                  move_pattern == MV_ALONG_RIGHT_SIDE)
4122         {
4123           /* use random direction as default start direction */
4124           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4125             MovDir[x][y] = 1 << RND(4);
4126
4127           for (i = 0; i < NUM_DIRECTIONS; i++)
4128           {
4129             int x1 = x + xy[i][0];
4130             int y1 = y + xy[i][1];
4131
4132             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4133             {
4134               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4135                 MovDir[x][y] = direction[0][i];
4136               else
4137                 MovDir[x][y] = direction[1][i];
4138
4139               break;
4140             }
4141           }
4142         }                
4143       }
4144       else
4145       {
4146         MovDir[x][y] = 1 << RND(4);
4147
4148         if (element != EL_BUG &&
4149             element != EL_SPACESHIP &&
4150             element != EL_BD_BUTTERFLY &&
4151             element != EL_BD_FIREFLY)
4152           break;
4153
4154         for (i = 0; i < NUM_DIRECTIONS; i++)
4155         {
4156           int x1 = x + xy[i][0];
4157           int y1 = y + xy[i][1];
4158
4159           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4160           {
4161             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4162             {
4163               MovDir[x][y] = direction[0][i];
4164               break;
4165             }
4166             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4167                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4168             {
4169               MovDir[x][y] = direction[1][i];
4170               break;
4171             }
4172           }
4173         }
4174       }
4175       break;
4176   }
4177
4178   GfxDir[x][y] = MovDir[x][y];
4179 }
4180
4181 void InitAmoebaNr(int x, int y)
4182 {
4183   int i;
4184   int group_nr = AmoebeNachbarNr(x, y);
4185
4186   if (group_nr == 0)
4187   {
4188     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4189     {
4190       if (AmoebaCnt[i] == 0)
4191       {
4192         group_nr = i;
4193         break;
4194       }
4195     }
4196   }
4197
4198   AmoebaNr[x][y] = group_nr;
4199   AmoebaCnt[group_nr]++;
4200   AmoebaCnt2[group_nr]++;
4201 }
4202
4203 static void PlayerWins(struct PlayerInfo *player)
4204 {
4205   player->LevelSolved = TRUE;
4206   player->GameOver = TRUE;
4207
4208   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4209                          level.native_em_level->lev->score : player->score);
4210
4211   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4212                                       TimeLeft);
4213   player->LevelSolved_CountingScore = player->score_final;
4214 }
4215
4216 void GameWon()
4217 {
4218   static int time, time_final;
4219   static int score, score_final;
4220   static int game_over_delay_1 = 0;
4221   static int game_over_delay_2 = 0;
4222   int game_over_delay_value_1 = 50;
4223   int game_over_delay_value_2 = 50;
4224
4225   if (!local_player->LevelSolved_GameWon)
4226   {
4227     int i;
4228
4229     /* do not start end game actions before the player stops moving (to exit) */
4230     if (local_player->MovPos)
4231       return;
4232
4233     local_player->LevelSolved_GameWon = TRUE;
4234     local_player->LevelSolved_SaveTape = tape.recording;
4235     local_player->LevelSolved_SaveScore = !tape.playing;
4236
4237     if (!tape.playing)
4238     {
4239       LevelStats_incSolved(level_nr);
4240
4241       SaveLevelSetup_SeriesInfo();
4242     }
4243
4244     if (tape.auto_play)         /* tape might already be stopped here */
4245       tape.auto_play_level_solved = TRUE;
4246
4247     TapeStop();
4248
4249     game_over_delay_1 = game_over_delay_value_1;
4250     game_over_delay_2 = game_over_delay_value_2;
4251
4252     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4253     score = score_final = local_player->score_final;
4254
4255     if (TimeLeft > 0)
4256     {
4257       time_final = 0;
4258       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4259     }
4260     else if (game.no_time_limit && TimePlayed < 999)
4261     {
4262       time_final = 999;
4263       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4264     }
4265
4266     local_player->score_final = score_final;
4267
4268     if (level_editor_test_game)
4269     {
4270       time = time_final;
4271       score = score_final;
4272
4273       local_player->LevelSolved_CountingTime = time;
4274       local_player->LevelSolved_CountingScore = score;
4275
4276       game_panel_controls[GAME_PANEL_TIME].value = time;
4277       game_panel_controls[GAME_PANEL_SCORE].value = score;
4278
4279       DisplayGameControlValues();
4280     }
4281
4282     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4283     {
4284       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4285       {
4286         /* close exit door after last player */
4287         if ((AllPlayersGone &&
4288              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4289               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4290               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4291             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4292             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4293         {
4294           int element = Feld[ExitX][ExitY];
4295
4296           Feld[ExitX][ExitY] =
4297             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4298              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4299              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4300              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4301              EL_EM_STEEL_EXIT_CLOSING);
4302
4303           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4304         }
4305
4306         /* player disappears */
4307         DrawLevelField(ExitX, ExitY);
4308       }
4309
4310       for (i = 0; i < MAX_PLAYERS; i++)
4311       {
4312         struct PlayerInfo *player = &stored_player[i];
4313
4314         if (player->present)
4315         {
4316           RemovePlayer(player);
4317
4318           /* player disappears */
4319           DrawLevelField(player->jx, player->jy);
4320         }
4321       }
4322     }
4323
4324     PlaySound(SND_GAME_WINNING);
4325   }
4326
4327   if (game_over_delay_1 > 0)
4328   {
4329     game_over_delay_1--;
4330
4331     return;
4332   }
4333
4334   if (time != time_final)
4335   {
4336     int time_to_go = ABS(time_final - time);
4337     int time_count_dir = (time < time_final ? +1 : -1);
4338     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4339
4340     time  += time_count_steps * time_count_dir;
4341     score += time_count_steps * level.score[SC_TIME_BONUS];
4342
4343     local_player->LevelSolved_CountingTime = time;
4344     local_player->LevelSolved_CountingScore = score;
4345
4346     game_panel_controls[GAME_PANEL_TIME].value = time;
4347     game_panel_controls[GAME_PANEL_SCORE].value = score;
4348
4349     DisplayGameControlValues();
4350
4351     if (time == time_final)
4352       StopSound(SND_GAME_LEVELTIME_BONUS);
4353     else if (setup.sound_loops)
4354       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4355     else
4356       PlaySound(SND_GAME_LEVELTIME_BONUS);
4357
4358     return;
4359   }
4360
4361   local_player->LevelSolved_PanelOff = TRUE;
4362
4363   if (game_over_delay_2 > 0)
4364   {
4365     game_over_delay_2--;
4366
4367     return;
4368   }
4369
4370   GameEnd();
4371 }
4372
4373 void GameEnd()
4374 {
4375   int hi_pos;
4376   boolean raise_level = FALSE;
4377
4378   local_player->LevelSolved_GameEnd = TRUE;
4379
4380   CloseDoor(DOOR_CLOSE_1);
4381
4382   if (local_player->LevelSolved_SaveTape)
4383   {
4384     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4385   }
4386
4387   if (level_editor_test_game)
4388   {
4389     game_status = GAME_MODE_MAIN;
4390
4391     DrawAndFadeInMainMenu(REDRAW_FIELD);
4392
4393     return;
4394   }
4395
4396   if (!local_player->LevelSolved_SaveScore)
4397   {
4398     FadeOut(REDRAW_FIELD);
4399
4400     game_status = GAME_MODE_MAIN;
4401
4402     DrawAndFadeInMainMenu(REDRAW_FIELD);
4403
4404     return;
4405   }
4406
4407   if (level_nr == leveldir_current->handicap_level)
4408   {
4409     leveldir_current->handicap_level++;
4410
4411     SaveLevelSetup_SeriesInfo();
4412   }
4413
4414   if (level_nr < leveldir_current->last_level)
4415     raise_level = TRUE;                 /* advance to next level */
4416
4417   if ((hi_pos = NewHiScore()) >= 0) 
4418   {
4419     game_status = GAME_MODE_SCORES;
4420
4421     DrawHallOfFame(hi_pos);
4422
4423     if (raise_level)
4424     {
4425       level_nr++;
4426       TapeErase();
4427     }
4428   }
4429   else
4430   {
4431     FadeOut(REDRAW_FIELD);
4432
4433     game_status = GAME_MODE_MAIN;
4434
4435     if (raise_level)
4436     {
4437       level_nr++;
4438       TapeErase();
4439     }
4440
4441     DrawAndFadeInMainMenu(REDRAW_FIELD);
4442   }
4443 }
4444
4445 int NewHiScore()
4446 {
4447   int k, l;
4448   int position = -1;
4449
4450   LoadScore(level_nr);
4451
4452   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4453       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4454     return -1;
4455
4456   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4457   {
4458     if (local_player->score_final > highscore[k].Score)
4459     {
4460       /* player has made it to the hall of fame */
4461
4462       if (k < MAX_SCORE_ENTRIES - 1)
4463       {
4464         int m = MAX_SCORE_ENTRIES - 1;
4465
4466 #ifdef ONE_PER_NAME
4467         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4468           if (strEqual(setup.player_name, highscore[l].Name))
4469             m = l;
4470         if (m == k)     /* player's new highscore overwrites his old one */
4471           goto put_into_list;
4472 #endif
4473
4474         for (l = m; l > k; l--)
4475         {
4476           strcpy(highscore[l].Name, highscore[l - 1].Name);
4477           highscore[l].Score = highscore[l - 1].Score;
4478         }
4479       }
4480
4481 #ifdef ONE_PER_NAME
4482       put_into_list:
4483 #endif
4484       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4485       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4486       highscore[k].Score = local_player->score_final; 
4487       position = k;
4488       break;
4489     }
4490
4491 #ifdef ONE_PER_NAME
4492     else if (!strncmp(setup.player_name, highscore[k].Name,
4493                       MAX_PLAYER_NAME_LEN))
4494       break;    /* player already there with a higher score */
4495 #endif
4496
4497   }
4498
4499   if (position >= 0) 
4500     SaveScore(level_nr);
4501
4502   return position;
4503 }
4504
4505 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4506 {
4507   int element = Feld[x][y];
4508   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4509   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4510   int horiz_move = (dx != 0);
4511   int sign = (horiz_move ? dx : dy);
4512   int step = sign * element_info[element].move_stepsize;
4513
4514   /* special values for move stepsize for spring and things on conveyor belt */
4515   if (horiz_move)
4516   {
4517     if (CAN_FALL(element) &&
4518         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4519       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4520     else if (element == EL_SPRING)
4521       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4522   }
4523
4524   return step;
4525 }
4526
4527 inline static int getElementMoveStepsize(int x, int y)
4528 {
4529   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4530 }
4531
4532 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4533 {
4534   if (player->GfxAction != action || player->GfxDir != dir)
4535   {
4536     player->GfxAction = action;
4537     player->GfxDir = dir;
4538     player->Frame = 0;
4539     player->StepFrame = 0;
4540   }
4541 }
4542
4543 static void ResetGfxFrame(int x, int y, boolean redraw)
4544 {
4545   int element = Feld[x][y];
4546   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4547   int last_gfx_frame = GfxFrame[x][y];
4548
4549   if (graphic_info[graphic].anim_global_sync)
4550     GfxFrame[x][y] = FrameCounter;
4551   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4552     GfxFrame[x][y] = CustomValue[x][y];
4553   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4554     GfxFrame[x][y] = element_info[element].collect_score;
4555   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4556     GfxFrame[x][y] = ChangeDelay[x][y];
4557
4558   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4559     DrawLevelGraphicAnimation(x, y, graphic);
4560 }
4561
4562 static void ResetGfxAnimation(int x, int y)
4563 {
4564   GfxAction[x][y] = ACTION_DEFAULT;
4565   GfxDir[x][y] = MovDir[x][y];
4566   GfxFrame[x][y] = 0;
4567
4568   ResetGfxFrame(x, y, FALSE);
4569 }
4570
4571 static void ResetRandomAnimationValue(int x, int y)
4572 {
4573   GfxRandom[x][y] = INIT_GFX_RANDOM();
4574 }
4575
4576 void InitMovingField(int x, int y, int direction)
4577 {
4578   int element = Feld[x][y];
4579   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4580   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4581   int newx = x + dx;
4582   int newy = y + dy;
4583   boolean is_moving_before, is_moving_after;
4584
4585   /* check if element was/is moving or being moved before/after mode change */
4586   is_moving_before = (WasJustMoving[x][y] != 0);
4587   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4588
4589   /* reset animation only for moving elements which change direction of moving
4590      or which just started or stopped moving
4591      (else CEs with property "can move" / "not moving" are reset each frame) */
4592   if (is_moving_before != is_moving_after ||
4593       direction != MovDir[x][y])
4594     ResetGfxAnimation(x, y);
4595
4596   MovDir[x][y] = direction;
4597   GfxDir[x][y] = direction;
4598
4599   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4600                      direction == MV_DOWN && CAN_FALL(element) ?
4601                      ACTION_FALLING : ACTION_MOVING);
4602
4603   /* this is needed for CEs with property "can move" / "not moving" */
4604
4605   if (is_moving_after)
4606   {
4607     if (Feld[newx][newy] == EL_EMPTY)
4608       Feld[newx][newy] = EL_BLOCKED;
4609
4610     MovDir[newx][newy] = MovDir[x][y];
4611
4612     CustomValue[newx][newy] = CustomValue[x][y];
4613
4614     GfxFrame[newx][newy] = GfxFrame[x][y];
4615     GfxRandom[newx][newy] = GfxRandom[x][y];
4616     GfxAction[newx][newy] = GfxAction[x][y];
4617     GfxDir[newx][newy] = GfxDir[x][y];
4618   }
4619 }
4620
4621 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4622 {
4623   int direction = MovDir[x][y];
4624   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4625   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4626
4627   *goes_to_x = newx;
4628   *goes_to_y = newy;
4629 }
4630
4631 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4632 {
4633   int oldx = x, oldy = y;
4634   int direction = MovDir[x][y];
4635
4636   if (direction == MV_LEFT)
4637     oldx++;
4638   else if (direction == MV_RIGHT)
4639     oldx--;
4640   else if (direction == MV_UP)
4641     oldy++;
4642   else if (direction == MV_DOWN)
4643     oldy--;
4644
4645   *comes_from_x = oldx;
4646   *comes_from_y = oldy;
4647 }
4648
4649 int MovingOrBlocked2Element(int x, int y)
4650 {
4651   int element = Feld[x][y];
4652
4653   if (element == EL_BLOCKED)
4654   {
4655     int oldx, oldy;
4656
4657     Blocked2Moving(x, y, &oldx, &oldy);
4658     return Feld[oldx][oldy];
4659   }
4660   else
4661     return element;
4662 }
4663
4664 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4665 {
4666   /* like MovingOrBlocked2Element(), but if element is moving
4667      and (x,y) is the field the moving element is just leaving,
4668      return EL_BLOCKED instead of the element value */
4669   int element = Feld[x][y];
4670
4671   if (IS_MOVING(x, y))
4672   {
4673     if (element == EL_BLOCKED)
4674     {
4675       int oldx, oldy;
4676
4677       Blocked2Moving(x, y, &oldx, &oldy);
4678       return Feld[oldx][oldy];
4679     }
4680     else
4681       return EL_BLOCKED;
4682   }
4683   else
4684     return element;
4685 }
4686
4687 static void RemoveField(int x, int y)
4688 {
4689   Feld[x][y] = EL_EMPTY;
4690
4691   MovPos[x][y] = 0;
4692   MovDir[x][y] = 0;
4693   MovDelay[x][y] = 0;
4694
4695   CustomValue[x][y] = 0;
4696
4697   AmoebaNr[x][y] = 0;
4698   ChangeDelay[x][y] = 0;
4699   ChangePage[x][y] = -1;
4700   Pushed[x][y] = FALSE;
4701
4702   GfxElement[x][y] = EL_UNDEFINED;
4703   GfxAction[x][y] = ACTION_DEFAULT;
4704   GfxDir[x][y] = MV_NONE;
4705 }
4706
4707 void RemoveMovingField(int x, int y)
4708 {
4709   int oldx = x, oldy = y, newx = x, newy = y;
4710   int element = Feld[x][y];
4711   int next_element = EL_UNDEFINED;
4712
4713   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4714     return;
4715
4716   if (IS_MOVING(x, y))
4717   {
4718     Moving2Blocked(x, y, &newx, &newy);
4719
4720     if (Feld[newx][newy] != EL_BLOCKED)
4721     {
4722       /* element is moving, but target field is not free (blocked), but
4723          already occupied by something different (example: acid pool);
4724          in this case, only remove the moving field, but not the target */
4725
4726       RemoveField(oldx, oldy);
4727
4728       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4729
4730       TEST_DrawLevelField(oldx, oldy);
4731
4732       return;
4733     }
4734   }
4735   else if (element == EL_BLOCKED)
4736   {
4737     Blocked2Moving(x, y, &oldx, &oldy);
4738     if (!IS_MOVING(oldx, oldy))
4739       return;
4740   }
4741
4742   if (element == EL_BLOCKED &&
4743       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4744        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4745        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4746        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4747        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4748        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4749     next_element = get_next_element(Feld[oldx][oldy]);
4750
4751   RemoveField(oldx, oldy);
4752   RemoveField(newx, newy);
4753
4754   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4755
4756   if (next_element != EL_UNDEFINED)
4757     Feld[oldx][oldy] = next_element;
4758
4759   TEST_DrawLevelField(oldx, oldy);
4760   TEST_DrawLevelField(newx, newy);
4761 }
4762
4763 void DrawDynamite(int x, int y)
4764 {
4765   int sx = SCREENX(x), sy = SCREENY(y);
4766   int graphic = el2img(Feld[x][y]);
4767   int frame;
4768
4769   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4770     return;
4771
4772   if (IS_WALKABLE_INSIDE(Back[x][y]))
4773     return;
4774
4775   if (Back[x][y])
4776     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4777   else if (Store[x][y])
4778     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4779
4780   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4781
4782   if (Back[x][y] || Store[x][y])
4783     DrawGraphicThruMask(sx, sy, graphic, frame);
4784   else
4785     DrawGraphic(sx, sy, graphic, frame);
4786 }
4787
4788 void CheckDynamite(int x, int y)
4789 {
4790   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4791   {
4792     MovDelay[x][y]--;
4793
4794     if (MovDelay[x][y] != 0)
4795     {
4796       DrawDynamite(x, y);
4797       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4798
4799       return;
4800     }
4801   }
4802
4803   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4804
4805   Bang(x, y);
4806 }
4807
4808 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4809 {
4810   boolean num_checked_players = 0;
4811   int i;
4812
4813   for (i = 0; i < MAX_PLAYERS; i++)
4814   {
4815     if (stored_player[i].active)
4816     {
4817       int sx = stored_player[i].jx;
4818       int sy = stored_player[i].jy;
4819
4820       if (num_checked_players == 0)
4821       {
4822         *sx1 = *sx2 = sx;
4823         *sy1 = *sy2 = sy;
4824       }
4825       else
4826       {
4827         *sx1 = MIN(*sx1, sx);
4828         *sy1 = MIN(*sy1, sy);
4829         *sx2 = MAX(*sx2, sx);
4830         *sy2 = MAX(*sy2, sy);
4831       }
4832
4833       num_checked_players++;
4834     }
4835   }
4836 }
4837
4838 static boolean checkIfAllPlayersFitToScreen_RND()
4839 {
4840   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4841
4842   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4843
4844   return (sx2 - sx1 < SCR_FIELDX &&
4845           sy2 - sy1 < SCR_FIELDY);
4846 }
4847
4848 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4849 {
4850   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4851
4852   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4853
4854   *sx = (sx1 + sx2) / 2;
4855   *sy = (sy1 + sy2) / 2;
4856 }
4857
4858 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4859                         boolean center_screen, boolean quick_relocation)
4860 {
4861   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4862   boolean no_delay = (tape.warp_forward);
4863   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4864   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4865
4866   if (quick_relocation)
4867   {
4868     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4869     {
4870       if (!level.shifted_relocation || center_screen)
4871       {
4872         /* quick relocation (without scrolling), with centering of screen */
4873
4874         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4875                     x > SBX_Right + MIDPOSX ? SBX_Right :
4876                     x - MIDPOSX);
4877
4878         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4879                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4880                     y - MIDPOSY);
4881       }
4882       else
4883       {
4884         /* quick relocation (without scrolling), but do not center screen */
4885
4886         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4887                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4888                                old_x - MIDPOSX);
4889
4890         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4891                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4892                                old_y - MIDPOSY);
4893
4894         int offset_x = x + (scroll_x - center_scroll_x);
4895         int offset_y = y + (scroll_y - center_scroll_y);
4896
4897         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4898                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4899                     offset_x - MIDPOSX);
4900
4901         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4902                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4903                     offset_y - MIDPOSY);
4904       }
4905     }
4906     else
4907     {
4908       if (!level.shifted_relocation || center_screen)
4909       {
4910         /* quick relocation (without scrolling), with centering of screen */
4911
4912         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4913                     x > SBX_Right + MIDPOSX ? SBX_Right :
4914                     x - MIDPOSX);
4915
4916         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4917                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4918                     y - MIDPOSY);
4919       }
4920       else
4921       {
4922         /* quick relocation (without scrolling), but do not center screen */
4923
4924         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4925                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4926                                old_x - MIDPOSX);
4927
4928         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4929                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4930                                old_y - MIDPOSY);
4931
4932         int offset_x = x + (scroll_x - center_scroll_x);
4933         int offset_y = y + (scroll_y - center_scroll_y);
4934
4935         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4936                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4937                     offset_x - MIDPOSX);
4938
4939         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4940                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4941                     offset_y - MIDPOSY);
4942       }
4943     }
4944
4945     RedrawPlayfield(TRUE, 0,0,0,0);
4946   }
4947   else
4948   {
4949     int scroll_xx, scroll_yy;
4950
4951     if (!level.shifted_relocation || center_screen)
4952     {
4953       /* visible relocation (with scrolling), with centering of screen */
4954
4955       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4956                    x > SBX_Right + MIDPOSX ? SBX_Right :
4957                    x - MIDPOSX);
4958
4959       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4960                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4961                    y - MIDPOSY);
4962     }
4963     else
4964     {
4965       /* visible relocation (with scrolling), but do not center screen */
4966
4967       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4968                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4969                              old_x - MIDPOSX);
4970
4971       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4972                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4973                              old_y - MIDPOSY);
4974
4975       int offset_x = x + (scroll_x - center_scroll_x);
4976       int offset_y = y + (scroll_y - center_scroll_y);
4977
4978       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4979                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4980                    offset_x - MIDPOSX);
4981
4982       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4983                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4984                    offset_y - MIDPOSY);
4985     }
4986
4987
4988     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4989
4990     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4991     {
4992       int dx = 0, dy = 0;
4993       int fx = FX, fy = FY;
4994
4995       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4996       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4997
4998       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4999         break;
5000
5001       scroll_x -= dx;
5002       scroll_y -= dy;
5003
5004       fx += dx * TILEX / 2;
5005       fy += dy * TILEY / 2;
5006
5007       ScrollLevel(dx, dy);
5008       DrawAllPlayers();
5009
5010       /* scroll in two steps of half tile size to make things smoother */
5011       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5012       Delay(wait_delay_value);
5013
5014       /* scroll second step to align at full tile size */
5015       BackToFront();
5016       Delay(wait_delay_value);
5017     }
5018
5019     DrawAllPlayers();
5020     BackToFront();
5021     Delay(wait_delay_value);
5022   }
5023 }
5024
5025 void RelocatePlayer(int jx, int jy, int el_player_raw)
5026 {
5027   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5028   int player_nr = GET_PLAYER_NR(el_player);
5029   struct PlayerInfo *player = &stored_player[player_nr];
5030   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5031   boolean no_delay = (tape.warp_forward);
5032   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5033   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5034   int old_jx = player->jx;
5035   int old_jy = player->jy;
5036   int old_element = Feld[old_jx][old_jy];
5037   int element = Feld[jx][jy];
5038   boolean player_relocated = (old_jx != jx || old_jy != jy);
5039
5040   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5041   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5042   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5043   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5044   int leave_side_horiz = move_dir_horiz;
5045   int leave_side_vert  = move_dir_vert;
5046   int enter_side = enter_side_horiz | enter_side_vert;
5047   int leave_side = leave_side_horiz | leave_side_vert;
5048
5049   if (player->GameOver)         /* do not reanimate dead player */
5050     return;
5051
5052   if (!player_relocated)        /* no need to relocate the player */
5053     return;
5054
5055   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5056   {
5057     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5058     DrawLevelField(jx, jy);
5059   }
5060
5061   if (player->present)
5062   {
5063     while (player->MovPos)
5064     {
5065       ScrollPlayer(player, SCROLL_GO_ON);
5066       ScrollScreen(NULL, SCROLL_GO_ON);
5067
5068       AdvanceFrameAndPlayerCounters(player->index_nr);
5069
5070       DrawPlayer(player);
5071
5072       BackToFront();
5073       Delay(wait_delay_value);
5074     }
5075
5076     DrawPlayer(player);         /* needed here only to cleanup last field */
5077     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5078
5079     player->is_moving = FALSE;
5080   }
5081
5082   if (IS_CUSTOM_ELEMENT(old_element))
5083     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5084                                CE_LEFT_BY_PLAYER,
5085                                player->index_bit, leave_side);
5086
5087   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5088                                       CE_PLAYER_LEAVES_X,
5089                                       player->index_bit, leave_side);
5090
5091   Feld[jx][jy] = el_player;
5092   InitPlayerField(jx, jy, el_player, TRUE);
5093
5094   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5095      possible that the relocation target field did not contain a player element,
5096      but a walkable element, to which the new player was relocated -- in this
5097      case, restore that (already initialized!) element on the player field */
5098   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5099   {
5100     Feld[jx][jy] = element;     /* restore previously existing element */
5101   }
5102
5103   /* only visually relocate centered player */
5104   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5105                      FALSE, level.instant_relocation);
5106
5107   TestIfPlayerTouchesBadThing(jx, jy);
5108   TestIfPlayerTouchesCustomElement(jx, jy);
5109
5110   if (IS_CUSTOM_ELEMENT(element))
5111     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5112                                player->index_bit, enter_side);
5113
5114   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5115                                       player->index_bit, enter_side);
5116
5117   if (player->is_switching)
5118   {
5119     /* ensure that relocation while still switching an element does not cause
5120        a new element to be treated as also switched directly after relocation
5121        (this is important for teleporter switches that teleport the player to
5122        a place where another teleporter switch is in the same direction, which
5123        would then incorrectly be treated as immediately switched before the
5124        direction key that caused the switch was released) */
5125
5126     player->switch_x += jx - old_jx;
5127     player->switch_y += jy - old_jy;
5128   }
5129 }
5130
5131 void Explode(int ex, int ey, int phase, int mode)
5132 {
5133   int x, y;
5134   int last_phase;
5135   int border_element;
5136
5137   /* !!! eliminate this variable !!! */
5138   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5139
5140   if (game.explosions_delayed)
5141   {
5142     ExplodeField[ex][ey] = mode;
5143     return;
5144   }
5145
5146   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5147   {
5148     int center_element = Feld[ex][ey];
5149     int artwork_element, explosion_element;     /* set these values later */
5150
5151     /* remove things displayed in background while burning dynamite */
5152     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5153       Back[ex][ey] = 0;
5154
5155     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5156     {
5157       /* put moving element to center field (and let it explode there) */
5158       center_element = MovingOrBlocked2Element(ex, ey);
5159       RemoveMovingField(ex, ey);
5160       Feld[ex][ey] = center_element;
5161     }
5162
5163     /* now "center_element" is finally determined -- set related values now */
5164     artwork_element = center_element;           /* for custom player artwork */
5165     explosion_element = center_element;         /* for custom player artwork */
5166
5167     if (IS_PLAYER(ex, ey))
5168     {
5169       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5170
5171       artwork_element = stored_player[player_nr].artwork_element;
5172
5173       if (level.use_explosion_element[player_nr])
5174       {
5175         explosion_element = level.explosion_element[player_nr];
5176         artwork_element = explosion_element;
5177       }
5178     }
5179
5180     if (mode == EX_TYPE_NORMAL ||
5181         mode == EX_TYPE_CENTER ||
5182         mode == EX_TYPE_CROSS)
5183       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5184
5185     last_phase = element_info[explosion_element].explosion_delay + 1;
5186
5187     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5188     {
5189       int xx = x - ex + 1;
5190       int yy = y - ey + 1;
5191       int element;
5192
5193       if (!IN_LEV_FIELD(x, y) ||
5194           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5195           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5196         continue;
5197
5198       element = Feld[x][y];
5199
5200       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5201       {
5202         element = MovingOrBlocked2Element(x, y);
5203
5204         if (!IS_EXPLOSION_PROOF(element))
5205           RemoveMovingField(x, y);
5206       }
5207
5208       /* indestructible elements can only explode in center (but not flames) */
5209       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5210                                            mode == EX_TYPE_BORDER)) ||
5211           element == EL_FLAMES)
5212         continue;
5213
5214       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5215          behaviour, for example when touching a yamyam that explodes to rocks
5216          with active deadly shield, a rock is created under the player !!! */
5217       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5218 #if 0
5219       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5220           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5221            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5222 #else
5223       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5224 #endif
5225       {
5226         if (IS_ACTIVE_BOMB(element))
5227         {
5228           /* re-activate things under the bomb like gate or penguin */
5229           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5230           Back[x][y] = 0;
5231         }
5232
5233         continue;
5234       }
5235
5236       /* save walkable background elements while explosion on same tile */
5237       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5238           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5239         Back[x][y] = element;
5240
5241       /* ignite explodable elements reached by other explosion */
5242       if (element == EL_EXPLOSION)
5243         element = Store2[x][y];
5244
5245       if (AmoebaNr[x][y] &&
5246           (element == EL_AMOEBA_FULL ||
5247            element == EL_BD_AMOEBA ||
5248            element == EL_AMOEBA_GROWING))
5249       {
5250         AmoebaCnt[AmoebaNr[x][y]]--;
5251         AmoebaCnt2[AmoebaNr[x][y]]--;
5252       }
5253
5254       RemoveField(x, y);
5255
5256       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5257       {
5258         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5259
5260         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5261
5262         if (PLAYERINFO(ex, ey)->use_murphy)
5263           Store[x][y] = EL_EMPTY;
5264       }
5265
5266       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5267          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5268       else if (ELEM_IS_PLAYER(center_element))
5269         Store[x][y] = EL_EMPTY;
5270       else if (center_element == EL_YAMYAM)
5271         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5272       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5273         Store[x][y] = element_info[center_element].content.e[xx][yy];
5274 #if 1
5275       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5276          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5277          otherwise) -- FIX THIS !!! */
5278       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5279         Store[x][y] = element_info[element].content.e[1][1];
5280 #else
5281       else if (!CAN_EXPLODE(element))
5282         Store[x][y] = element_info[element].content.e[1][1];
5283 #endif
5284       else
5285         Store[x][y] = EL_EMPTY;
5286
5287       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5288           center_element == EL_AMOEBA_TO_DIAMOND)
5289         Store2[x][y] = element;
5290
5291       Feld[x][y] = EL_EXPLOSION;
5292       GfxElement[x][y] = artwork_element;
5293
5294       ExplodePhase[x][y] = 1;
5295       ExplodeDelay[x][y] = last_phase;
5296
5297       Stop[x][y] = TRUE;
5298     }
5299
5300     if (center_element == EL_YAMYAM)
5301       game.yamyam_content_nr =
5302         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5303
5304     return;
5305   }
5306
5307   if (Stop[ex][ey])
5308     return;
5309
5310   x = ex;
5311   y = ey;
5312
5313   if (phase == 1)
5314     GfxFrame[x][y] = 0;         /* restart explosion animation */
5315
5316   last_phase = ExplodeDelay[x][y];
5317
5318   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5319
5320   /* this can happen if the player leaves an explosion just in time */
5321   if (GfxElement[x][y] == EL_UNDEFINED)
5322     GfxElement[x][y] = EL_EMPTY;
5323
5324   border_element = Store2[x][y];
5325   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5326     border_element = StorePlayer[x][y];
5327
5328   if (phase == element_info[border_element].ignition_delay ||
5329       phase == last_phase)
5330   {
5331     boolean border_explosion = FALSE;
5332
5333     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5334         !PLAYER_EXPLOSION_PROTECTED(x, y))
5335     {
5336       KillPlayerUnlessExplosionProtected(x, y);
5337       border_explosion = TRUE;
5338     }
5339     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5340     {
5341       Feld[x][y] = Store2[x][y];
5342       Store2[x][y] = 0;
5343       Bang(x, y);
5344       border_explosion = TRUE;
5345     }
5346     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5347     {
5348       AmoebeUmwandeln(x, y);
5349       Store2[x][y] = 0;
5350       border_explosion = TRUE;
5351     }
5352
5353     /* if an element just explodes due to another explosion (chain-reaction),
5354        do not immediately end the new explosion when it was the last frame of
5355        the explosion (as it would be done in the following "if"-statement!) */
5356     if (border_explosion && phase == last_phase)
5357       return;
5358   }
5359
5360   if (phase == last_phase)
5361   {
5362     int element;
5363
5364     element = Feld[x][y] = Store[x][y];
5365     Store[x][y] = Store2[x][y] = 0;
5366     GfxElement[x][y] = EL_UNDEFINED;
5367
5368     /* player can escape from explosions and might therefore be still alive */
5369     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5370         element <= EL_PLAYER_IS_EXPLODING_4)
5371     {
5372       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5373       int explosion_element = EL_PLAYER_1 + player_nr;
5374       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5375       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5376
5377       if (level.use_explosion_element[player_nr])
5378         explosion_element = level.explosion_element[player_nr];
5379
5380       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5381                     element_info[explosion_element].content.e[xx][yy]);
5382     }
5383
5384     /* restore probably existing indestructible background element */
5385     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5386       element = Feld[x][y] = Back[x][y];
5387     Back[x][y] = 0;
5388
5389     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5390     GfxDir[x][y] = MV_NONE;
5391     ChangeDelay[x][y] = 0;
5392     ChangePage[x][y] = -1;
5393
5394     CustomValue[x][y] = 0;
5395
5396     InitField_WithBug2(x, y, FALSE);
5397
5398     TEST_DrawLevelField(x, y);
5399
5400     TestIfElementTouchesCustomElement(x, y);
5401
5402     if (GFX_CRUMBLED(element))
5403       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5404
5405     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5406       StorePlayer[x][y] = 0;
5407
5408     if (ELEM_IS_PLAYER(element))
5409       RelocatePlayer(x, y, element);
5410   }
5411   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5412   {
5413     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5414     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5415
5416     if (phase == delay)
5417       TEST_DrawLevelFieldCrumbled(x, y);
5418
5419     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5420     {
5421       DrawLevelElement(x, y, Back[x][y]);
5422       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5423     }
5424     else if (IS_WALKABLE_UNDER(Back[x][y]))
5425     {
5426       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5427       DrawLevelElementThruMask(x, y, Back[x][y]);
5428     }
5429     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5430       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5431   }
5432 }
5433
5434 void DynaExplode(int ex, int ey)
5435 {
5436   int i, j;
5437   int dynabomb_element = Feld[ex][ey];
5438   int dynabomb_size = 1;
5439   boolean dynabomb_xl = FALSE;
5440   struct PlayerInfo *player;
5441   static int xy[4][2] =
5442   {
5443     { 0, -1 },
5444     { -1, 0 },
5445     { +1, 0 },
5446     { 0, +1 }
5447   };
5448
5449   if (IS_ACTIVE_BOMB(dynabomb_element))
5450   {
5451     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5452     dynabomb_size = player->dynabomb_size;
5453     dynabomb_xl = player->dynabomb_xl;
5454     player->dynabombs_left++;
5455   }
5456
5457   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5458
5459   for (i = 0; i < NUM_DIRECTIONS; i++)
5460   {
5461     for (j = 1; j <= dynabomb_size; j++)
5462     {
5463       int x = ex + j * xy[i][0];
5464       int y = ey + j * xy[i][1];
5465       int element;
5466
5467       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5468         break;
5469
5470       element = Feld[x][y];
5471
5472       /* do not restart explosions of fields with active bombs */
5473       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5474         continue;
5475
5476       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5477
5478       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5479           !IS_DIGGABLE(element) && !dynabomb_xl)
5480         break;
5481     }
5482   }
5483 }
5484
5485 void Bang(int x, int y)
5486 {
5487   int element = MovingOrBlocked2Element(x, y);
5488   int explosion_type = EX_TYPE_NORMAL;
5489
5490   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5491   {
5492     struct PlayerInfo *player = PLAYERINFO(x, y);
5493
5494     element = Feld[x][y] = player->initial_element;
5495
5496     if (level.use_explosion_element[player->index_nr])
5497     {
5498       int explosion_element = level.explosion_element[player->index_nr];
5499
5500       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5501         explosion_type = EX_TYPE_CROSS;
5502       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5503         explosion_type = EX_TYPE_CENTER;
5504     }
5505   }
5506
5507   switch (element)
5508   {
5509     case EL_BUG:
5510     case EL_SPACESHIP:
5511     case EL_BD_BUTTERFLY:
5512     case EL_BD_FIREFLY:
5513     case EL_YAMYAM:
5514     case EL_DARK_YAMYAM:
5515     case EL_ROBOT:
5516     case EL_PACMAN:
5517     case EL_MOLE:
5518       RaiseScoreElement(element);
5519       break;
5520
5521     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5522     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5523     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5524     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5525     case EL_DYNABOMB_INCREASE_NUMBER:
5526     case EL_DYNABOMB_INCREASE_SIZE:
5527     case EL_DYNABOMB_INCREASE_POWER:
5528       explosion_type = EX_TYPE_DYNA;
5529       break;
5530
5531     case EL_DC_LANDMINE:
5532       explosion_type = EX_TYPE_CENTER;
5533       break;
5534
5535     case EL_PENGUIN:
5536     case EL_LAMP:
5537     case EL_LAMP_ACTIVE:
5538     case EL_AMOEBA_TO_DIAMOND:
5539       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5540         explosion_type = EX_TYPE_CENTER;
5541       break;
5542
5543     default:
5544       if (element_info[element].explosion_type == EXPLODES_CROSS)
5545         explosion_type = EX_TYPE_CROSS;
5546       else if (element_info[element].explosion_type == EXPLODES_1X1)
5547         explosion_type = EX_TYPE_CENTER;
5548       break;
5549   }
5550
5551   if (explosion_type == EX_TYPE_DYNA)
5552     DynaExplode(x, y);
5553   else
5554     Explode(x, y, EX_PHASE_START, explosion_type);
5555
5556   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5557 }
5558
5559 void SplashAcid(int x, int y)
5560 {
5561   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5562       (!IN_LEV_FIELD(x - 1, y - 2) ||
5563        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5564     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5565
5566   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5567       (!IN_LEV_FIELD(x + 1, y - 2) ||
5568        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5569     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5570
5571   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5572 }
5573
5574 static void InitBeltMovement()
5575 {
5576   static int belt_base_element[4] =
5577   {
5578     EL_CONVEYOR_BELT_1_LEFT,
5579     EL_CONVEYOR_BELT_2_LEFT,
5580     EL_CONVEYOR_BELT_3_LEFT,
5581     EL_CONVEYOR_BELT_4_LEFT
5582   };
5583   static int belt_base_active_element[4] =
5584   {
5585     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5586     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5587     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5588     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5589   };
5590
5591   int x, y, i, j;
5592
5593   /* set frame order for belt animation graphic according to belt direction */
5594   for (i = 0; i < NUM_BELTS; i++)
5595   {
5596     int belt_nr = i;
5597
5598     for (j = 0; j < NUM_BELT_PARTS; j++)
5599     {
5600       int element = belt_base_active_element[belt_nr] + j;
5601       int graphic_1 = el2img(element);
5602       int graphic_2 = el2panelimg(element);
5603
5604       if (game.belt_dir[i] == MV_LEFT)
5605       {
5606         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5607         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5608       }
5609       else
5610       {
5611         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5612         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5613       }
5614     }
5615   }
5616
5617   SCAN_PLAYFIELD(x, y)
5618   {
5619     int element = Feld[x][y];
5620
5621     for (i = 0; i < NUM_BELTS; i++)
5622     {
5623       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5624       {
5625         int e_belt_nr = getBeltNrFromBeltElement(element);
5626         int belt_nr = i;
5627
5628         if (e_belt_nr == belt_nr)
5629         {
5630           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5631
5632           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5633         }
5634       }
5635     }
5636   }
5637 }
5638
5639 static void ToggleBeltSwitch(int x, int y)
5640 {
5641   static int belt_base_element[4] =
5642   {
5643     EL_CONVEYOR_BELT_1_LEFT,
5644     EL_CONVEYOR_BELT_2_LEFT,
5645     EL_CONVEYOR_BELT_3_LEFT,
5646     EL_CONVEYOR_BELT_4_LEFT
5647   };
5648   static int belt_base_active_element[4] =
5649   {
5650     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5651     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5652     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5653     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5654   };
5655   static int belt_base_switch_element[4] =
5656   {
5657     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5658     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5659     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5660     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5661   };
5662   static int belt_move_dir[4] =
5663   {
5664     MV_LEFT,
5665     MV_NONE,
5666     MV_RIGHT,
5667     MV_NONE,
5668   };
5669
5670   int element = Feld[x][y];
5671   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5672   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5673   int belt_dir = belt_move_dir[belt_dir_nr];
5674   int xx, yy, i;
5675
5676   if (!IS_BELT_SWITCH(element))
5677     return;
5678
5679   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5680   game.belt_dir[belt_nr] = belt_dir;
5681
5682   if (belt_dir_nr == 3)
5683     belt_dir_nr = 1;
5684
5685   /* set frame order for belt animation graphic according to belt direction */
5686   for (i = 0; i < NUM_BELT_PARTS; i++)
5687   {
5688     int element = belt_base_active_element[belt_nr] + i;
5689     int graphic_1 = el2img(element);
5690     int graphic_2 = el2panelimg(element);
5691
5692     if (belt_dir == MV_LEFT)
5693     {
5694       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5695       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5696     }
5697     else
5698     {
5699       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5700       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5701     }
5702   }
5703
5704   SCAN_PLAYFIELD(xx, yy)
5705   {
5706     int element = Feld[xx][yy];
5707
5708     if (IS_BELT_SWITCH(element))
5709     {
5710       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5711
5712       if (e_belt_nr == belt_nr)
5713       {
5714         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5715         TEST_DrawLevelField(xx, yy);
5716       }
5717     }
5718     else if (IS_BELT(element) && belt_dir != MV_NONE)
5719     {
5720       int e_belt_nr = getBeltNrFromBeltElement(element);
5721
5722       if (e_belt_nr == belt_nr)
5723       {
5724         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5725
5726         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5727         TEST_DrawLevelField(xx, yy);
5728       }
5729     }
5730     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5731     {
5732       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5733
5734       if (e_belt_nr == belt_nr)
5735       {
5736         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5737
5738         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5739         TEST_DrawLevelField(xx, yy);
5740       }
5741     }
5742   }
5743 }
5744
5745 static void ToggleSwitchgateSwitch(int x, int y)
5746 {
5747   int xx, yy;
5748
5749   game.switchgate_pos = !game.switchgate_pos;
5750
5751   SCAN_PLAYFIELD(xx, yy)
5752   {
5753     int element = Feld[xx][yy];
5754
5755     if (element == EL_SWITCHGATE_SWITCH_UP)
5756     {
5757       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5758       TEST_DrawLevelField(xx, yy);
5759     }
5760     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5761     {
5762       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5763       TEST_DrawLevelField(xx, yy);
5764     }
5765     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5766     {
5767       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5768       TEST_DrawLevelField(xx, yy);
5769     }
5770     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5771     {
5772       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5773       TEST_DrawLevelField(xx, yy);
5774     }
5775     else if (element == EL_SWITCHGATE_OPEN ||
5776              element == EL_SWITCHGATE_OPENING)
5777     {
5778       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5779
5780       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5781     }
5782     else if (element == EL_SWITCHGATE_CLOSED ||
5783              element == EL_SWITCHGATE_CLOSING)
5784     {
5785       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5786
5787       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5788     }
5789   }
5790 }
5791
5792 static int getInvisibleActiveFromInvisibleElement(int element)
5793 {
5794   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5795           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5796           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5797           element);
5798 }
5799
5800 static int getInvisibleFromInvisibleActiveElement(int element)
5801 {
5802   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5803           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5804           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5805           element);
5806 }
5807
5808 static void RedrawAllLightSwitchesAndInvisibleElements()
5809 {
5810   int x, y;
5811
5812   SCAN_PLAYFIELD(x, y)
5813   {
5814     int element = Feld[x][y];
5815
5816     if (element == EL_LIGHT_SWITCH &&
5817         game.light_time_left > 0)
5818     {
5819       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5820       TEST_DrawLevelField(x, y);
5821     }
5822     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5823              game.light_time_left == 0)
5824     {
5825       Feld[x][y] = EL_LIGHT_SWITCH;
5826       TEST_DrawLevelField(x, y);
5827     }
5828     else if (element == EL_EMC_DRIPPER &&
5829              game.light_time_left > 0)
5830     {
5831       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5832       TEST_DrawLevelField(x, y);
5833     }
5834     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5835              game.light_time_left == 0)
5836     {
5837       Feld[x][y] = EL_EMC_DRIPPER;
5838       TEST_DrawLevelField(x, y);
5839     }
5840     else if (element == EL_INVISIBLE_STEELWALL ||
5841              element == EL_INVISIBLE_WALL ||
5842              element == EL_INVISIBLE_SAND)
5843     {
5844       if (game.light_time_left > 0)
5845         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5846
5847       TEST_DrawLevelField(x, y);
5848
5849       /* uncrumble neighbour fields, if needed */
5850       if (element == EL_INVISIBLE_SAND)
5851         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5852     }
5853     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5854              element == EL_INVISIBLE_WALL_ACTIVE ||
5855              element == EL_INVISIBLE_SAND_ACTIVE)
5856     {
5857       if (game.light_time_left == 0)
5858         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5859
5860       TEST_DrawLevelField(x, y);
5861
5862       /* re-crumble neighbour fields, if needed */
5863       if (element == EL_INVISIBLE_SAND)
5864         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5865     }
5866   }
5867 }
5868
5869 static void RedrawAllInvisibleElementsForLenses()
5870 {
5871   int x, y;
5872
5873   SCAN_PLAYFIELD(x, y)
5874   {
5875     int element = Feld[x][y];
5876
5877     if (element == EL_EMC_DRIPPER &&
5878         game.lenses_time_left > 0)
5879     {
5880       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5881       TEST_DrawLevelField(x, y);
5882     }
5883     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5884              game.lenses_time_left == 0)
5885     {
5886       Feld[x][y] = EL_EMC_DRIPPER;
5887       TEST_DrawLevelField(x, y);
5888     }
5889     else if (element == EL_INVISIBLE_STEELWALL ||
5890              element == EL_INVISIBLE_WALL ||
5891              element == EL_INVISIBLE_SAND)
5892     {
5893       if (game.lenses_time_left > 0)
5894         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5895
5896       TEST_DrawLevelField(x, y);
5897
5898       /* uncrumble neighbour fields, if needed */
5899       if (element == EL_INVISIBLE_SAND)
5900         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5901     }
5902     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5903              element == EL_INVISIBLE_WALL_ACTIVE ||
5904              element == EL_INVISIBLE_SAND_ACTIVE)
5905     {
5906       if (game.lenses_time_left == 0)
5907         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5908
5909       TEST_DrawLevelField(x, y);
5910
5911       /* re-crumble neighbour fields, if needed */
5912       if (element == EL_INVISIBLE_SAND)
5913         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5914     }
5915   }
5916 }
5917
5918 static void RedrawAllInvisibleElementsForMagnifier()
5919 {
5920   int x, y;
5921
5922   SCAN_PLAYFIELD(x, y)
5923   {
5924     int element = Feld[x][y];
5925
5926     if (element == EL_EMC_FAKE_GRASS &&
5927         game.magnify_time_left > 0)
5928     {
5929       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5930       TEST_DrawLevelField(x, y);
5931     }
5932     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5933              game.magnify_time_left == 0)
5934     {
5935       Feld[x][y] = EL_EMC_FAKE_GRASS;
5936       TEST_DrawLevelField(x, y);
5937     }
5938     else if (IS_GATE_GRAY(element) &&
5939              game.magnify_time_left > 0)
5940     {
5941       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5942                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5943                     IS_EM_GATE_GRAY(element) ?
5944                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5945                     IS_EMC_GATE_GRAY(element) ?
5946                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5947                     IS_DC_GATE_GRAY(element) ?
5948                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5949                     element);
5950       TEST_DrawLevelField(x, y);
5951     }
5952     else if (IS_GATE_GRAY_ACTIVE(element) &&
5953              game.magnify_time_left == 0)
5954     {
5955       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5956                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5957                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5958                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5959                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5960                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5961                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5962                     EL_DC_GATE_WHITE_GRAY :
5963                     element);
5964       TEST_DrawLevelField(x, y);
5965     }
5966   }
5967 }
5968
5969 static void ToggleLightSwitch(int x, int y)
5970 {
5971   int element = Feld[x][y];
5972
5973   game.light_time_left =
5974     (element == EL_LIGHT_SWITCH ?
5975      level.time_light * FRAMES_PER_SECOND : 0);
5976
5977   RedrawAllLightSwitchesAndInvisibleElements();
5978 }
5979
5980 static void ActivateTimegateSwitch(int x, int y)
5981 {
5982   int xx, yy;
5983
5984   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5985
5986   SCAN_PLAYFIELD(xx, yy)
5987   {
5988     int element = Feld[xx][yy];
5989
5990     if (element == EL_TIMEGATE_CLOSED ||
5991         element == EL_TIMEGATE_CLOSING)
5992     {
5993       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5994       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5995     }
5996
5997     /*
5998     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5999     {
6000       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6001       TEST_DrawLevelField(xx, yy);
6002     }
6003     */
6004
6005   }
6006
6007   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6008                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6009 }
6010
6011 void Impact(int x, int y)
6012 {
6013   boolean last_line = (y == lev_fieldy - 1);
6014   boolean object_hit = FALSE;
6015   boolean impact = (last_line || object_hit);
6016   int element = Feld[x][y];
6017   int smashed = EL_STEELWALL;
6018
6019   if (!last_line)       /* check if element below was hit */
6020   {
6021     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6022       return;
6023
6024     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6025                                          MovDir[x][y + 1] != MV_DOWN ||
6026                                          MovPos[x][y + 1] <= TILEY / 2));
6027
6028     /* do not smash moving elements that left the smashed field in time */
6029     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6030         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6031       object_hit = FALSE;
6032
6033 #if USE_QUICKSAND_IMPACT_BUGFIX
6034     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6035     {
6036       RemoveMovingField(x, y + 1);
6037       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6038       Feld[x][y + 2] = EL_ROCK;
6039       TEST_DrawLevelField(x, y + 2);
6040
6041       object_hit = TRUE;
6042     }
6043
6044     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6045     {
6046       RemoveMovingField(x, y + 1);
6047       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6048       Feld[x][y + 2] = EL_ROCK;
6049       TEST_DrawLevelField(x, y + 2);
6050
6051       object_hit = TRUE;
6052     }
6053 #endif
6054
6055     if (object_hit)
6056       smashed = MovingOrBlocked2Element(x, y + 1);
6057
6058     impact = (last_line || object_hit);
6059   }
6060
6061   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6062   {
6063     SplashAcid(x, y + 1);
6064     return;
6065   }
6066
6067   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6068   /* only reset graphic animation if graphic really changes after impact */
6069   if (impact &&
6070       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6071   {
6072     ResetGfxAnimation(x, y);
6073     TEST_DrawLevelField(x, y);
6074   }
6075
6076   if (impact && CAN_EXPLODE_IMPACT(element))
6077   {
6078     Bang(x, y);
6079     return;
6080   }
6081   else if (impact && element == EL_PEARL &&
6082            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6083   {
6084     ResetGfxAnimation(x, y);
6085
6086     Feld[x][y] = EL_PEARL_BREAKING;
6087     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6088     return;
6089   }
6090   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6091   {
6092     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6093
6094     return;
6095   }
6096
6097   if (impact && element == EL_AMOEBA_DROP)
6098   {
6099     if (object_hit && IS_PLAYER(x, y + 1))
6100       KillPlayerUnlessEnemyProtected(x, y + 1);
6101     else if (object_hit && smashed == EL_PENGUIN)
6102       Bang(x, y + 1);
6103     else
6104     {
6105       Feld[x][y] = EL_AMOEBA_GROWING;
6106       Store[x][y] = EL_AMOEBA_WET;
6107
6108       ResetRandomAnimationValue(x, y);
6109     }
6110     return;
6111   }
6112
6113   if (object_hit)               /* check which object was hit */
6114   {
6115     if ((CAN_PASS_MAGIC_WALL(element) && 
6116          (smashed == EL_MAGIC_WALL ||
6117           smashed == EL_BD_MAGIC_WALL)) ||
6118         (CAN_PASS_DC_MAGIC_WALL(element) &&
6119          smashed == EL_DC_MAGIC_WALL))
6120     {
6121       int xx, yy;
6122       int activated_magic_wall =
6123         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6124          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6125          EL_DC_MAGIC_WALL_ACTIVE);
6126
6127       /* activate magic wall / mill */
6128       SCAN_PLAYFIELD(xx, yy)
6129       {
6130         if (Feld[xx][yy] == smashed)
6131           Feld[xx][yy] = activated_magic_wall;
6132       }
6133
6134       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6135       game.magic_wall_active = TRUE;
6136
6137       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6138                             SND_MAGIC_WALL_ACTIVATING :
6139                             smashed == EL_BD_MAGIC_WALL ?
6140                             SND_BD_MAGIC_WALL_ACTIVATING :
6141                             SND_DC_MAGIC_WALL_ACTIVATING));
6142     }
6143
6144     if (IS_PLAYER(x, y + 1))
6145     {
6146       if (CAN_SMASH_PLAYER(element))
6147       {
6148         KillPlayerUnlessEnemyProtected(x, y + 1);
6149         return;
6150       }
6151     }
6152     else if (smashed == EL_PENGUIN)
6153     {
6154       if (CAN_SMASH_PLAYER(element))
6155       {
6156         Bang(x, y + 1);
6157         return;
6158       }
6159     }
6160     else if (element == EL_BD_DIAMOND)
6161     {
6162       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6163       {
6164         Bang(x, y + 1);
6165         return;
6166       }
6167     }
6168     else if (((element == EL_SP_INFOTRON ||
6169                element == EL_SP_ZONK) &&
6170               (smashed == EL_SP_SNIKSNAK ||
6171                smashed == EL_SP_ELECTRON ||
6172                smashed == EL_SP_DISK_ORANGE)) ||
6173              (element == EL_SP_INFOTRON &&
6174               smashed == EL_SP_DISK_YELLOW))
6175     {
6176       Bang(x, y + 1);
6177       return;
6178     }
6179     else if (CAN_SMASH_EVERYTHING(element))
6180     {
6181       if (IS_CLASSIC_ENEMY(smashed) ||
6182           CAN_EXPLODE_SMASHED(smashed))
6183       {
6184         Bang(x, y + 1);
6185         return;
6186       }
6187       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6188       {
6189         if (smashed == EL_LAMP ||
6190             smashed == EL_LAMP_ACTIVE)
6191         {
6192           Bang(x, y + 1);
6193           return;
6194         }
6195         else if (smashed == EL_NUT)
6196         {
6197           Feld[x][y + 1] = EL_NUT_BREAKING;
6198           PlayLevelSound(x, y, SND_NUT_BREAKING);
6199           RaiseScoreElement(EL_NUT);
6200           return;
6201         }
6202         else if (smashed == EL_PEARL)
6203         {
6204           ResetGfxAnimation(x, y);
6205
6206           Feld[x][y + 1] = EL_PEARL_BREAKING;
6207           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6208           return;
6209         }
6210         else if (smashed == EL_DIAMOND)
6211         {
6212           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6213           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6214           return;
6215         }
6216         else if (IS_BELT_SWITCH(smashed))
6217         {
6218           ToggleBeltSwitch(x, y + 1);
6219         }
6220         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6221                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6222                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6223                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6224         {
6225           ToggleSwitchgateSwitch(x, y + 1);
6226         }
6227         else if (smashed == EL_LIGHT_SWITCH ||
6228                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6229         {
6230           ToggleLightSwitch(x, y + 1);
6231         }
6232         else
6233         {
6234           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6235
6236           CheckElementChangeBySide(x, y + 1, smashed, element,
6237                                    CE_SWITCHED, CH_SIDE_TOP);
6238           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6239                                             CH_SIDE_TOP);
6240         }
6241       }
6242       else
6243       {
6244         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6245       }
6246     }
6247   }
6248
6249   /* play sound of magic wall / mill */
6250   if (!last_line &&
6251       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6252        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6253        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6254   {
6255     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6256       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6257     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6258       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6259     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6260       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6261
6262     return;
6263   }
6264
6265   /* play sound of object that hits the ground */
6266   if (last_line || object_hit)
6267     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6268 }
6269
6270 inline static void TurnRoundExt(int x, int y)
6271 {
6272   static struct
6273   {
6274     int dx, dy;
6275   } move_xy[] =
6276   {
6277     {  0,  0 },
6278     { -1,  0 },
6279     { +1,  0 },
6280     {  0,  0 },
6281     {  0, -1 },
6282     {  0,  0 }, { 0, 0 }, { 0, 0 },
6283     {  0, +1 }
6284   };
6285   static struct
6286   {
6287     int left, right, back;
6288   } turn[] =
6289   {
6290     { 0,        0,              0        },
6291     { MV_DOWN,  MV_UP,          MV_RIGHT },
6292     { MV_UP,    MV_DOWN,        MV_LEFT  },
6293     { 0,        0,              0        },
6294     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6295     { 0,        0,              0        },
6296     { 0,        0,              0        },
6297     { 0,        0,              0        },
6298     { MV_RIGHT, MV_LEFT,        MV_UP    }
6299   };
6300
6301   int element = Feld[x][y];
6302   int move_pattern = element_info[element].move_pattern;
6303
6304   int old_move_dir = MovDir[x][y];
6305   int left_dir  = turn[old_move_dir].left;
6306   int right_dir = turn[old_move_dir].right;
6307   int back_dir  = turn[old_move_dir].back;
6308
6309   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6310   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6311   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6312   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6313
6314   int left_x  = x + left_dx,  left_y  = y + left_dy;
6315   int right_x = x + right_dx, right_y = y + right_dy;
6316   int move_x  = x + move_dx,  move_y  = y + move_dy;
6317
6318   int xx, yy;
6319
6320   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6321   {
6322     TestIfBadThingTouchesOtherBadThing(x, y);
6323
6324     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6325       MovDir[x][y] = right_dir;
6326     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6327       MovDir[x][y] = left_dir;
6328
6329     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6330       MovDelay[x][y] = 9;
6331     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6332       MovDelay[x][y] = 1;
6333   }
6334   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6335   {
6336     TestIfBadThingTouchesOtherBadThing(x, y);
6337
6338     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6339       MovDir[x][y] = left_dir;
6340     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6341       MovDir[x][y] = right_dir;
6342
6343     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6344       MovDelay[x][y] = 9;
6345     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6346       MovDelay[x][y] = 1;
6347   }
6348   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6349   {
6350     TestIfBadThingTouchesOtherBadThing(x, y);
6351
6352     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6353       MovDir[x][y] = left_dir;
6354     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6355       MovDir[x][y] = right_dir;
6356
6357     if (MovDir[x][y] != old_move_dir)
6358       MovDelay[x][y] = 9;
6359   }
6360   else if (element == EL_YAMYAM)
6361   {
6362     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6363     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6364
6365     if (can_turn_left && can_turn_right)
6366       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6367     else if (can_turn_left)
6368       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6369     else if (can_turn_right)
6370       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6371     else
6372       MovDir[x][y] = back_dir;
6373
6374     MovDelay[x][y] = 16 + 16 * RND(3);
6375   }
6376   else if (element == EL_DARK_YAMYAM)
6377   {
6378     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6379                                                          left_x, left_y);
6380     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6381                                                          right_x, right_y);
6382
6383     if (can_turn_left && can_turn_right)
6384       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6385     else if (can_turn_left)
6386       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6387     else if (can_turn_right)
6388       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6389     else
6390       MovDir[x][y] = back_dir;
6391
6392     MovDelay[x][y] = 16 + 16 * RND(3);
6393   }
6394   else if (element == EL_PACMAN)
6395   {
6396     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6397     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6398
6399     if (can_turn_left && can_turn_right)
6400       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6401     else if (can_turn_left)
6402       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6403     else if (can_turn_right)
6404       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6405     else
6406       MovDir[x][y] = back_dir;
6407
6408     MovDelay[x][y] = 6 + RND(40);
6409   }
6410   else if (element == EL_PIG)
6411   {
6412     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6413     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6414     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6415     boolean should_turn_left, should_turn_right, should_move_on;
6416     int rnd_value = 24;
6417     int rnd = RND(rnd_value);
6418
6419     should_turn_left = (can_turn_left &&
6420                         (!can_move_on ||
6421                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6422                                                    y + back_dy + left_dy)));
6423     should_turn_right = (can_turn_right &&
6424                          (!can_move_on ||
6425                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6426                                                     y + back_dy + right_dy)));
6427     should_move_on = (can_move_on &&
6428                       (!can_turn_left ||
6429                        !can_turn_right ||
6430                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6431                                                  y + move_dy + left_dy) ||
6432                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6433                                                  y + move_dy + right_dy)));
6434
6435     if (should_turn_left || should_turn_right || should_move_on)
6436     {
6437       if (should_turn_left && should_turn_right && should_move_on)
6438         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6439                         rnd < 2 * rnd_value / 3 ? right_dir :
6440                         old_move_dir);
6441       else if (should_turn_left && should_turn_right)
6442         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6443       else if (should_turn_left && should_move_on)
6444         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6445       else if (should_turn_right && should_move_on)
6446         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6447       else if (should_turn_left)
6448         MovDir[x][y] = left_dir;
6449       else if (should_turn_right)
6450         MovDir[x][y] = right_dir;
6451       else if (should_move_on)
6452         MovDir[x][y] = old_move_dir;
6453     }
6454     else if (can_move_on && rnd > rnd_value / 8)
6455       MovDir[x][y] = old_move_dir;
6456     else if (can_turn_left && can_turn_right)
6457       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6458     else if (can_turn_left && rnd > rnd_value / 8)
6459       MovDir[x][y] = left_dir;
6460     else if (can_turn_right && rnd > rnd_value/8)
6461       MovDir[x][y] = right_dir;
6462     else
6463       MovDir[x][y] = back_dir;
6464
6465     xx = x + move_xy[MovDir[x][y]].dx;
6466     yy = y + move_xy[MovDir[x][y]].dy;
6467
6468     if (!IN_LEV_FIELD(xx, yy) ||
6469         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6470       MovDir[x][y] = old_move_dir;
6471
6472     MovDelay[x][y] = 0;
6473   }
6474   else if (element == EL_DRAGON)
6475   {
6476     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6477     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6478     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6479     int rnd_value = 24;
6480     int rnd = RND(rnd_value);
6481
6482     if (can_move_on && rnd > rnd_value / 8)
6483       MovDir[x][y] = old_move_dir;
6484     else if (can_turn_left && can_turn_right)
6485       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6486     else if (can_turn_left && rnd > rnd_value / 8)
6487       MovDir[x][y] = left_dir;
6488     else if (can_turn_right && rnd > rnd_value / 8)
6489       MovDir[x][y] = right_dir;
6490     else
6491       MovDir[x][y] = back_dir;
6492
6493     xx = x + move_xy[MovDir[x][y]].dx;
6494     yy = y + move_xy[MovDir[x][y]].dy;
6495
6496     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6497       MovDir[x][y] = old_move_dir;
6498
6499     MovDelay[x][y] = 0;
6500   }
6501   else if (element == EL_MOLE)
6502   {
6503     boolean can_move_on =
6504       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6505                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6506                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6507     if (!can_move_on)
6508     {
6509       boolean can_turn_left =
6510         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6511                               IS_AMOEBOID(Feld[left_x][left_y])));
6512
6513       boolean can_turn_right =
6514         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6515                               IS_AMOEBOID(Feld[right_x][right_y])));
6516
6517       if (can_turn_left && can_turn_right)
6518         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6519       else if (can_turn_left)
6520         MovDir[x][y] = left_dir;
6521       else
6522         MovDir[x][y] = right_dir;
6523     }
6524
6525     if (MovDir[x][y] != old_move_dir)
6526       MovDelay[x][y] = 9;
6527   }
6528   else if (element == EL_BALLOON)
6529   {
6530     MovDir[x][y] = game.wind_direction;
6531     MovDelay[x][y] = 0;
6532   }
6533   else if (element == EL_SPRING)
6534   {
6535     if (MovDir[x][y] & MV_HORIZONTAL)
6536     {
6537       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6538           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6539       {
6540         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6541         ResetGfxAnimation(move_x, move_y);
6542         TEST_DrawLevelField(move_x, move_y);
6543
6544         MovDir[x][y] = back_dir;
6545       }
6546       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6547                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6548         MovDir[x][y] = MV_NONE;
6549     }
6550
6551     MovDelay[x][y] = 0;
6552   }
6553   else if (element == EL_ROBOT ||
6554            element == EL_SATELLITE ||
6555            element == EL_PENGUIN ||
6556            element == EL_EMC_ANDROID)
6557   {
6558     int attr_x = -1, attr_y = -1;
6559
6560     if (AllPlayersGone)
6561     {
6562       attr_x = ExitX;
6563       attr_y = ExitY;
6564     }
6565     else
6566     {
6567       int i;
6568
6569       for (i = 0; i < MAX_PLAYERS; i++)
6570       {
6571         struct PlayerInfo *player = &stored_player[i];
6572         int jx = player->jx, jy = player->jy;
6573
6574         if (!player->active)
6575           continue;
6576
6577         if (attr_x == -1 ||
6578             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6579         {
6580           attr_x = jx;
6581           attr_y = jy;
6582         }
6583       }
6584     }
6585
6586     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6587         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6588          game.engine_version < VERSION_IDENT(3,1,0,0)))
6589     {
6590       attr_x = ZX;
6591       attr_y = ZY;
6592     }
6593
6594     if (element == EL_PENGUIN)
6595     {
6596       int i;
6597       static int xy[4][2] =
6598       {
6599         { 0, -1 },
6600         { -1, 0 },
6601         { +1, 0 },
6602         { 0, +1 }
6603       };
6604
6605       for (i = 0; i < NUM_DIRECTIONS; i++)
6606       {
6607         int ex = x + xy[i][0];
6608         int ey = y + xy[i][1];
6609
6610         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6611                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6612                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6613                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6614         {
6615           attr_x = ex;
6616           attr_y = ey;
6617           break;
6618         }
6619       }
6620     }
6621
6622     MovDir[x][y] = MV_NONE;
6623     if (attr_x < x)
6624       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6625     else if (attr_x > x)
6626       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6627     if (attr_y < y)
6628       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6629     else if (attr_y > y)
6630       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6631
6632     if (element == EL_ROBOT)
6633     {
6634       int newx, newy;
6635
6636       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6637         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6638       Moving2Blocked(x, y, &newx, &newy);
6639
6640       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6641         MovDelay[x][y] = 8 + 8 * !RND(3);
6642       else
6643         MovDelay[x][y] = 16;
6644     }
6645     else if (element == EL_PENGUIN)
6646     {
6647       int newx, newy;
6648
6649       MovDelay[x][y] = 1;
6650
6651       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6652       {
6653         boolean first_horiz = RND(2);
6654         int new_move_dir = MovDir[x][y];
6655
6656         MovDir[x][y] =
6657           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6658         Moving2Blocked(x, y, &newx, &newy);
6659
6660         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6661           return;
6662
6663         MovDir[x][y] =
6664           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6665         Moving2Blocked(x, y, &newx, &newy);
6666
6667         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6668           return;
6669
6670         MovDir[x][y] = old_move_dir;
6671         return;
6672       }
6673     }
6674     else if (element == EL_SATELLITE)
6675     {
6676       int newx, newy;
6677
6678       MovDelay[x][y] = 1;
6679
6680       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6681       {
6682         boolean first_horiz = RND(2);
6683         int new_move_dir = MovDir[x][y];
6684
6685         MovDir[x][y] =
6686           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6687         Moving2Blocked(x, y, &newx, &newy);
6688
6689         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6690           return;
6691
6692         MovDir[x][y] =
6693           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6694         Moving2Blocked(x, y, &newx, &newy);
6695
6696         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6697           return;
6698
6699         MovDir[x][y] = old_move_dir;
6700         return;
6701       }
6702     }
6703     else if (element == EL_EMC_ANDROID)
6704     {
6705       static int check_pos[16] =
6706       {
6707         -1,             /*  0 => (invalid)          */
6708         7,              /*  1 => MV_LEFT            */
6709         3,              /*  2 => MV_RIGHT           */
6710         -1,             /*  3 => (invalid)          */
6711         1,              /*  4 =>            MV_UP   */
6712         0,              /*  5 => MV_LEFT  | MV_UP   */
6713         2,              /*  6 => MV_RIGHT | MV_UP   */
6714         -1,             /*  7 => (invalid)          */
6715         5,              /*  8 =>            MV_DOWN */
6716         6,              /*  9 => MV_LEFT  | MV_DOWN */
6717         4,              /* 10 => MV_RIGHT | MV_DOWN */
6718         -1,             /* 11 => (invalid)          */
6719         -1,             /* 12 => (invalid)          */
6720         -1,             /* 13 => (invalid)          */
6721         -1,             /* 14 => (invalid)          */
6722         -1,             /* 15 => (invalid)          */
6723       };
6724       static struct
6725       {
6726         int dx, dy;
6727         int dir;
6728       } check_xy[8] =
6729       {
6730         { -1, -1,       MV_LEFT  | MV_UP   },
6731         {  0, -1,                  MV_UP   },
6732         { +1, -1,       MV_RIGHT | MV_UP   },
6733         { +1,  0,       MV_RIGHT           },
6734         { +1, +1,       MV_RIGHT | MV_DOWN },
6735         {  0, +1,                  MV_DOWN },
6736         { -1, +1,       MV_LEFT  | MV_DOWN },
6737         { -1,  0,       MV_LEFT            },
6738       };
6739       int start_pos, check_order;
6740       boolean can_clone = FALSE;
6741       int i;
6742
6743       /* check if there is any free field around current position */
6744       for (i = 0; i < 8; i++)
6745       {
6746         int newx = x + check_xy[i].dx;
6747         int newy = y + check_xy[i].dy;
6748
6749         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6750         {
6751           can_clone = TRUE;
6752
6753           break;
6754         }
6755       }
6756
6757       if (can_clone)            /* randomly find an element to clone */
6758       {
6759         can_clone = FALSE;
6760
6761         start_pos = check_pos[RND(8)];
6762         check_order = (RND(2) ? -1 : +1);
6763
6764         for (i = 0; i < 8; i++)
6765         {
6766           int pos_raw = start_pos + i * check_order;
6767           int pos = (pos_raw + 8) % 8;
6768           int newx = x + check_xy[pos].dx;
6769           int newy = y + check_xy[pos].dy;
6770
6771           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6772           {
6773             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6774             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6775
6776             Store[x][y] = Feld[newx][newy];
6777
6778             can_clone = TRUE;
6779
6780             break;
6781           }
6782         }
6783       }
6784
6785       if (can_clone)            /* randomly find a direction to move */
6786       {
6787         can_clone = FALSE;
6788
6789         start_pos = check_pos[RND(8)];
6790         check_order = (RND(2) ? -1 : +1);
6791
6792         for (i = 0; i < 8; i++)
6793         {
6794           int pos_raw = start_pos + i * check_order;
6795           int pos = (pos_raw + 8) % 8;
6796           int newx = x + check_xy[pos].dx;
6797           int newy = y + check_xy[pos].dy;
6798           int new_move_dir = check_xy[pos].dir;
6799
6800           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6801           {
6802             MovDir[x][y] = new_move_dir;
6803             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6804
6805             can_clone = TRUE;
6806
6807             break;
6808           }
6809         }
6810       }
6811
6812       if (can_clone)            /* cloning and moving successful */
6813         return;
6814
6815       /* cannot clone -- try to move towards player */
6816
6817       start_pos = check_pos[MovDir[x][y] & 0x0f];
6818       check_order = (RND(2) ? -1 : +1);
6819
6820       for (i = 0; i < 3; i++)
6821       {
6822         /* first check start_pos, then previous/next or (next/previous) pos */
6823         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6824         int pos = (pos_raw + 8) % 8;
6825         int newx = x + check_xy[pos].dx;
6826         int newy = y + check_xy[pos].dy;
6827         int new_move_dir = check_xy[pos].dir;
6828
6829         if (IS_PLAYER(newx, newy))
6830           break;
6831
6832         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6833         {
6834           MovDir[x][y] = new_move_dir;
6835           MovDelay[x][y] = level.android_move_time * 8 + 1;
6836
6837           break;
6838         }
6839       }
6840     }
6841   }
6842   else if (move_pattern == MV_TURNING_LEFT ||
6843            move_pattern == MV_TURNING_RIGHT ||
6844            move_pattern == MV_TURNING_LEFT_RIGHT ||
6845            move_pattern == MV_TURNING_RIGHT_LEFT ||
6846            move_pattern == MV_TURNING_RANDOM ||
6847            move_pattern == MV_ALL_DIRECTIONS)
6848   {
6849     boolean can_turn_left =
6850       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6851     boolean can_turn_right =
6852       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6853
6854     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6855       return;
6856
6857     if (move_pattern == MV_TURNING_LEFT)
6858       MovDir[x][y] = left_dir;
6859     else if (move_pattern == MV_TURNING_RIGHT)
6860       MovDir[x][y] = right_dir;
6861     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6862       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6863     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6864       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6865     else if (move_pattern == MV_TURNING_RANDOM)
6866       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6867                       can_turn_right && !can_turn_left ? right_dir :
6868                       RND(2) ? left_dir : right_dir);
6869     else if (can_turn_left && can_turn_right)
6870       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6871     else if (can_turn_left)
6872       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6873     else if (can_turn_right)
6874       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6875     else
6876       MovDir[x][y] = back_dir;
6877
6878     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6879   }
6880   else if (move_pattern == MV_HORIZONTAL ||
6881            move_pattern == MV_VERTICAL)
6882   {
6883     if (move_pattern & old_move_dir)
6884       MovDir[x][y] = back_dir;
6885     else if (move_pattern == MV_HORIZONTAL)
6886       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6887     else if (move_pattern == MV_VERTICAL)
6888       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6889
6890     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6891   }
6892   else if (move_pattern & MV_ANY_DIRECTION)
6893   {
6894     MovDir[x][y] = move_pattern;
6895     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6896   }
6897   else if (move_pattern & MV_WIND_DIRECTION)
6898   {
6899     MovDir[x][y] = game.wind_direction;
6900     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6901   }
6902   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6903   {
6904     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6905       MovDir[x][y] = left_dir;
6906     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6907       MovDir[x][y] = right_dir;
6908
6909     if (MovDir[x][y] != old_move_dir)
6910       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6911   }
6912   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6913   {
6914     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6915       MovDir[x][y] = right_dir;
6916     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6917       MovDir[x][y] = left_dir;
6918
6919     if (MovDir[x][y] != old_move_dir)
6920       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6921   }
6922   else if (move_pattern == MV_TOWARDS_PLAYER ||
6923            move_pattern == MV_AWAY_FROM_PLAYER)
6924   {
6925     int attr_x = -1, attr_y = -1;
6926     int newx, newy;
6927     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6928
6929     if (AllPlayersGone)
6930     {
6931       attr_x = ExitX;
6932       attr_y = ExitY;
6933     }
6934     else
6935     {
6936       int i;
6937
6938       for (i = 0; i < MAX_PLAYERS; i++)
6939       {
6940         struct PlayerInfo *player = &stored_player[i];
6941         int jx = player->jx, jy = player->jy;
6942
6943         if (!player->active)
6944           continue;
6945
6946         if (attr_x == -1 ||
6947             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6948         {
6949           attr_x = jx;
6950           attr_y = jy;
6951         }
6952       }
6953     }
6954
6955     MovDir[x][y] = MV_NONE;
6956     if (attr_x < x)
6957       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6958     else if (attr_x > x)
6959       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6960     if (attr_y < y)
6961       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6962     else if (attr_y > y)
6963       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6964
6965     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6966
6967     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6968     {
6969       boolean first_horiz = RND(2);
6970       int new_move_dir = MovDir[x][y];
6971
6972       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6973       {
6974         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6975         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6976
6977         return;
6978       }
6979
6980       MovDir[x][y] =
6981         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6982       Moving2Blocked(x, y, &newx, &newy);
6983
6984       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6985         return;
6986
6987       MovDir[x][y] =
6988         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6989       Moving2Blocked(x, y, &newx, &newy);
6990
6991       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6992         return;
6993
6994       MovDir[x][y] = old_move_dir;
6995     }
6996   }
6997   else if (move_pattern == MV_WHEN_PUSHED ||
6998            move_pattern == MV_WHEN_DROPPED)
6999   {
7000     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7001       MovDir[x][y] = MV_NONE;
7002
7003     MovDelay[x][y] = 0;
7004   }
7005   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7006   {
7007     static int test_xy[7][2] =
7008     {
7009       { 0, -1 },
7010       { -1, 0 },
7011       { +1, 0 },
7012       { 0, +1 },
7013       { 0, -1 },
7014       { -1, 0 },
7015       { +1, 0 },
7016     };
7017     static int test_dir[7] =
7018     {
7019       MV_UP,
7020       MV_LEFT,
7021       MV_RIGHT,
7022       MV_DOWN,
7023       MV_UP,
7024       MV_LEFT,
7025       MV_RIGHT,
7026     };
7027     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7028     int move_preference = -1000000;     /* start with very low preference */
7029     int new_move_dir = MV_NONE;
7030     int start_test = RND(4);
7031     int i;
7032
7033     for (i = 0; i < NUM_DIRECTIONS; i++)
7034     {
7035       int move_dir = test_dir[start_test + i];
7036       int move_dir_preference;
7037
7038       xx = x + test_xy[start_test + i][0];
7039       yy = y + test_xy[start_test + i][1];
7040
7041       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7042           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7043       {
7044         new_move_dir = move_dir;
7045
7046         break;
7047       }
7048
7049       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7050         continue;
7051
7052       move_dir_preference = -1 * RunnerVisit[xx][yy];
7053       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7054         move_dir_preference = PlayerVisit[xx][yy];
7055
7056       if (move_dir_preference > move_preference)
7057       {
7058         /* prefer field that has not been visited for the longest time */
7059         move_preference = move_dir_preference;
7060         new_move_dir = move_dir;
7061       }
7062       else if (move_dir_preference == move_preference &&
7063                move_dir == old_move_dir)
7064       {
7065         /* prefer last direction when all directions are preferred equally */
7066         move_preference = move_dir_preference;
7067         new_move_dir = move_dir;
7068       }
7069     }
7070
7071     MovDir[x][y] = new_move_dir;
7072     if (old_move_dir != new_move_dir)
7073       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7074   }
7075 }
7076
7077 static void TurnRound(int x, int y)
7078 {
7079   int direction = MovDir[x][y];
7080
7081   TurnRoundExt(x, y);
7082
7083   GfxDir[x][y] = MovDir[x][y];
7084
7085   if (direction != MovDir[x][y])
7086     GfxFrame[x][y] = 0;
7087
7088   if (MovDelay[x][y])
7089     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7090
7091   ResetGfxFrame(x, y, FALSE);
7092 }
7093
7094 static boolean JustBeingPushed(int x, int y)
7095 {
7096   int i;
7097
7098   for (i = 0; i < MAX_PLAYERS; i++)
7099   {
7100     struct PlayerInfo *player = &stored_player[i];
7101
7102     if (player->active && player->is_pushing && player->MovPos)
7103     {
7104       int next_jx = player->jx + (player->jx - player->last_jx);
7105       int next_jy = player->jy + (player->jy - player->last_jy);
7106
7107       if (x == next_jx && y == next_jy)
7108         return TRUE;
7109     }
7110   }
7111
7112   return FALSE;
7113 }
7114
7115 void StartMoving(int x, int y)
7116 {
7117   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7118   int element = Feld[x][y];
7119
7120   if (Stop[x][y])
7121     return;
7122
7123   if (MovDelay[x][y] == 0)
7124     GfxAction[x][y] = ACTION_DEFAULT;
7125
7126   if (CAN_FALL(element) && y < lev_fieldy - 1)
7127   {
7128     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7129         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7130       if (JustBeingPushed(x, y))
7131         return;
7132
7133     if (element == EL_QUICKSAND_FULL)
7134     {
7135       if (IS_FREE(x, y + 1))
7136       {
7137         InitMovingField(x, y, MV_DOWN);
7138         started_moving = TRUE;
7139
7140         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7141 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7142         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7143           Store[x][y] = EL_ROCK;
7144 #else
7145         Store[x][y] = EL_ROCK;
7146 #endif
7147
7148         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7149       }
7150       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7151       {
7152         if (!MovDelay[x][y])
7153         {
7154           MovDelay[x][y] = TILEY + 1;
7155
7156           ResetGfxAnimation(x, y);
7157           ResetGfxAnimation(x, y + 1);
7158         }
7159
7160         if (MovDelay[x][y])
7161         {
7162           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7163           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7164
7165           MovDelay[x][y]--;
7166           if (MovDelay[x][y])
7167             return;
7168         }
7169
7170         Feld[x][y] = EL_QUICKSAND_EMPTY;
7171         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7172         Store[x][y + 1] = Store[x][y];
7173         Store[x][y] = 0;
7174
7175         PlayLevelSoundAction(x, y, ACTION_FILLING);
7176       }
7177       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7178       {
7179         if (!MovDelay[x][y])
7180         {
7181           MovDelay[x][y] = TILEY + 1;
7182
7183           ResetGfxAnimation(x, y);
7184           ResetGfxAnimation(x, y + 1);
7185         }
7186
7187         if (MovDelay[x][y])
7188         {
7189           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7190           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7191
7192           MovDelay[x][y]--;
7193           if (MovDelay[x][y])
7194             return;
7195         }
7196
7197         Feld[x][y] = EL_QUICKSAND_EMPTY;
7198         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7199         Store[x][y + 1] = Store[x][y];
7200         Store[x][y] = 0;
7201
7202         PlayLevelSoundAction(x, y, ACTION_FILLING);
7203       }
7204     }
7205     else if (element == EL_QUICKSAND_FAST_FULL)
7206     {
7207       if (IS_FREE(x, y + 1))
7208       {
7209         InitMovingField(x, y, MV_DOWN);
7210         started_moving = TRUE;
7211
7212         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7213 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7214         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7215           Store[x][y] = EL_ROCK;
7216 #else
7217         Store[x][y] = EL_ROCK;
7218 #endif
7219
7220         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7221       }
7222       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7223       {
7224         if (!MovDelay[x][y])
7225         {
7226           MovDelay[x][y] = TILEY + 1;
7227
7228           ResetGfxAnimation(x, y);
7229           ResetGfxAnimation(x, y + 1);
7230         }
7231
7232         if (MovDelay[x][y])
7233         {
7234           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7235           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7236
7237           MovDelay[x][y]--;
7238           if (MovDelay[x][y])
7239             return;
7240         }
7241
7242         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7243         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7244         Store[x][y + 1] = Store[x][y];
7245         Store[x][y] = 0;
7246
7247         PlayLevelSoundAction(x, y, ACTION_FILLING);
7248       }
7249       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7250       {
7251         if (!MovDelay[x][y])
7252         {
7253           MovDelay[x][y] = TILEY + 1;
7254
7255           ResetGfxAnimation(x, y);
7256           ResetGfxAnimation(x, y + 1);
7257         }
7258
7259         if (MovDelay[x][y])
7260         {
7261           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7262           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7263
7264           MovDelay[x][y]--;
7265           if (MovDelay[x][y])
7266             return;
7267         }
7268
7269         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7270         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7271         Store[x][y + 1] = Store[x][y];
7272         Store[x][y] = 0;
7273
7274         PlayLevelSoundAction(x, y, ACTION_FILLING);
7275       }
7276     }
7277     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7278              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7279     {
7280       InitMovingField(x, y, MV_DOWN);
7281       started_moving = TRUE;
7282
7283       Feld[x][y] = EL_QUICKSAND_FILLING;
7284       Store[x][y] = element;
7285
7286       PlayLevelSoundAction(x, y, ACTION_FILLING);
7287     }
7288     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7289              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7290     {
7291       InitMovingField(x, y, MV_DOWN);
7292       started_moving = TRUE;
7293
7294       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7295       Store[x][y] = element;
7296
7297       PlayLevelSoundAction(x, y, ACTION_FILLING);
7298     }
7299     else if (element == EL_MAGIC_WALL_FULL)
7300     {
7301       if (IS_FREE(x, y + 1))
7302       {
7303         InitMovingField(x, y, MV_DOWN);
7304         started_moving = TRUE;
7305
7306         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7307         Store[x][y] = EL_CHANGED(Store[x][y]);
7308       }
7309       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7310       {
7311         if (!MovDelay[x][y])
7312           MovDelay[x][y] = TILEY / 4 + 1;
7313
7314         if (MovDelay[x][y])
7315         {
7316           MovDelay[x][y]--;
7317           if (MovDelay[x][y])
7318             return;
7319         }
7320
7321         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7322         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7323         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7324         Store[x][y] = 0;
7325       }
7326     }
7327     else if (element == EL_BD_MAGIC_WALL_FULL)
7328     {
7329       if (IS_FREE(x, y + 1))
7330       {
7331         InitMovingField(x, y, MV_DOWN);
7332         started_moving = TRUE;
7333
7334         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7335         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7336       }
7337       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7338       {
7339         if (!MovDelay[x][y])
7340           MovDelay[x][y] = TILEY / 4 + 1;
7341
7342         if (MovDelay[x][y])
7343         {
7344           MovDelay[x][y]--;
7345           if (MovDelay[x][y])
7346             return;
7347         }
7348
7349         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7350         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7351         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7352         Store[x][y] = 0;
7353       }
7354     }
7355     else if (element == EL_DC_MAGIC_WALL_FULL)
7356     {
7357       if (IS_FREE(x, y + 1))
7358       {
7359         InitMovingField(x, y, MV_DOWN);
7360         started_moving = TRUE;
7361
7362         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7363         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7364       }
7365       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7366       {
7367         if (!MovDelay[x][y])
7368           MovDelay[x][y] = TILEY / 4 + 1;
7369
7370         if (MovDelay[x][y])
7371         {
7372           MovDelay[x][y]--;
7373           if (MovDelay[x][y])
7374             return;
7375         }
7376
7377         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7378         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7379         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7380         Store[x][y] = 0;
7381       }
7382     }
7383     else if ((CAN_PASS_MAGIC_WALL(element) &&
7384               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7385                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7386              (CAN_PASS_DC_MAGIC_WALL(element) &&
7387               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7388
7389     {
7390       InitMovingField(x, y, MV_DOWN);
7391       started_moving = TRUE;
7392
7393       Feld[x][y] =
7394         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7395          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7396          EL_DC_MAGIC_WALL_FILLING);
7397       Store[x][y] = element;
7398     }
7399     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7400     {
7401       SplashAcid(x, y + 1);
7402
7403       InitMovingField(x, y, MV_DOWN);
7404       started_moving = TRUE;
7405
7406       Store[x][y] = EL_ACID;
7407     }
7408     else if (
7409              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7410               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7411              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7412               CAN_FALL(element) && WasJustFalling[x][y] &&
7413               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7414
7415              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7416               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7417               (Feld[x][y + 1] == EL_BLOCKED)))
7418     {
7419       /* this is needed for a special case not covered by calling "Impact()"
7420          from "ContinueMoving()": if an element moves to a tile directly below
7421          another element which was just falling on that tile (which was empty
7422          in the previous frame), the falling element above would just stop
7423          instead of smashing the element below (in previous version, the above
7424          element was just checked for "moving" instead of "falling", resulting
7425          in incorrect smashes caused by horizontal movement of the above
7426          element; also, the case of the player being the element to smash was
7427          simply not covered here... :-/ ) */
7428
7429       CheckCollision[x][y] = 0;
7430       CheckImpact[x][y] = 0;
7431
7432       Impact(x, y);
7433     }
7434     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7435     {
7436       if (MovDir[x][y] == MV_NONE)
7437       {
7438         InitMovingField(x, y, MV_DOWN);
7439         started_moving = TRUE;
7440       }
7441     }
7442     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7443     {
7444       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7445         MovDir[x][y] = MV_DOWN;
7446
7447       InitMovingField(x, y, MV_DOWN);
7448       started_moving = TRUE;
7449     }
7450     else if (element == EL_AMOEBA_DROP)
7451     {
7452       Feld[x][y] = EL_AMOEBA_GROWING;
7453       Store[x][y] = EL_AMOEBA_WET;
7454     }
7455     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7456               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7457              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7458              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7459     {
7460       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7461                                 (IS_FREE(x - 1, y + 1) ||
7462                                  Feld[x - 1][y + 1] == EL_ACID));
7463       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7464                                 (IS_FREE(x + 1, y + 1) ||
7465                                  Feld[x + 1][y + 1] == EL_ACID));
7466       boolean can_fall_any  = (can_fall_left || can_fall_right);
7467       boolean can_fall_both = (can_fall_left && can_fall_right);
7468       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7469
7470       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7471       {
7472         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7473           can_fall_right = FALSE;
7474         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7475           can_fall_left = FALSE;
7476         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7477           can_fall_right = FALSE;
7478         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7479           can_fall_left = FALSE;
7480
7481         can_fall_any  = (can_fall_left || can_fall_right);
7482         can_fall_both = FALSE;
7483       }
7484
7485       if (can_fall_both)
7486       {
7487         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7488           can_fall_right = FALSE;       /* slip down on left side */
7489         else
7490           can_fall_left = !(can_fall_right = RND(2));
7491
7492         can_fall_both = FALSE;
7493       }
7494
7495       if (can_fall_any)
7496       {
7497         /* if not determined otherwise, prefer left side for slipping down */
7498         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7499         started_moving = TRUE;
7500       }
7501     }
7502     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7503     {
7504       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7505       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7506       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7507       int belt_dir = game.belt_dir[belt_nr];
7508
7509       if ((belt_dir == MV_LEFT  && left_is_free) ||
7510           (belt_dir == MV_RIGHT && right_is_free))
7511       {
7512         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7513
7514         InitMovingField(x, y, belt_dir);
7515         started_moving = TRUE;
7516
7517         Pushed[x][y] = TRUE;
7518         Pushed[nextx][y] = TRUE;
7519
7520         GfxAction[x][y] = ACTION_DEFAULT;
7521       }
7522       else
7523       {
7524         MovDir[x][y] = 0;       /* if element was moving, stop it */
7525       }
7526     }
7527   }
7528
7529   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7530   if (CAN_MOVE(element) && !started_moving)
7531   {
7532     int move_pattern = element_info[element].move_pattern;
7533     int newx, newy;
7534
7535     Moving2Blocked(x, y, &newx, &newy);
7536
7537     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7538       return;
7539
7540     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7541         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7542     {
7543       WasJustMoving[x][y] = 0;
7544       CheckCollision[x][y] = 0;
7545
7546       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7547
7548       if (Feld[x][y] != element)        /* element has changed */
7549         return;
7550     }
7551
7552     if (!MovDelay[x][y])        /* start new movement phase */
7553     {
7554       /* all objects that can change their move direction after each step
7555          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7556
7557       if (element != EL_YAMYAM &&
7558           element != EL_DARK_YAMYAM &&
7559           element != EL_PACMAN &&
7560           !(move_pattern & MV_ANY_DIRECTION) &&
7561           move_pattern != MV_TURNING_LEFT &&
7562           move_pattern != MV_TURNING_RIGHT &&
7563           move_pattern != MV_TURNING_LEFT_RIGHT &&
7564           move_pattern != MV_TURNING_RIGHT_LEFT &&
7565           move_pattern != MV_TURNING_RANDOM)
7566       {
7567         TurnRound(x, y);
7568
7569         if (MovDelay[x][y] && (element == EL_BUG ||
7570                                element == EL_SPACESHIP ||
7571                                element == EL_SP_SNIKSNAK ||
7572                                element == EL_SP_ELECTRON ||
7573                                element == EL_MOLE))
7574           TEST_DrawLevelField(x, y);
7575       }
7576     }
7577
7578     if (MovDelay[x][y])         /* wait some time before next movement */
7579     {
7580       MovDelay[x][y]--;
7581
7582       if (element == EL_ROBOT ||
7583           element == EL_YAMYAM ||
7584           element == EL_DARK_YAMYAM)
7585       {
7586         DrawLevelElementAnimationIfNeeded(x, y, element);
7587         PlayLevelSoundAction(x, y, ACTION_WAITING);
7588       }
7589       else if (element == EL_SP_ELECTRON)
7590         DrawLevelElementAnimationIfNeeded(x, y, element);
7591       else if (element == EL_DRAGON)
7592       {
7593         int i;
7594         int dir = MovDir[x][y];
7595         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7596         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7597         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7598                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7599                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7600                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7601         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7602
7603         GfxAction[x][y] = ACTION_ATTACKING;
7604
7605         if (IS_PLAYER(x, y))
7606           DrawPlayerField(x, y);
7607         else
7608           TEST_DrawLevelField(x, y);
7609
7610         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7611
7612         for (i = 1; i <= 3; i++)
7613         {
7614           int xx = x + i * dx;
7615           int yy = y + i * dy;
7616           int sx = SCREENX(xx);
7617           int sy = SCREENY(yy);
7618           int flame_graphic = graphic + (i - 1);
7619
7620           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7621             break;
7622
7623           if (MovDelay[x][y])
7624           {
7625             int flamed = MovingOrBlocked2Element(xx, yy);
7626
7627             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7628               Bang(xx, yy);
7629             else
7630               RemoveMovingField(xx, yy);
7631
7632             ChangeDelay[xx][yy] = 0;
7633
7634             Feld[xx][yy] = EL_FLAMES;
7635
7636             if (IN_SCR_FIELD(sx, sy))
7637             {
7638               TEST_DrawLevelFieldCrumbled(xx, yy);
7639               DrawGraphic(sx, sy, flame_graphic, frame);
7640             }
7641           }
7642           else
7643           {
7644             if (Feld[xx][yy] == EL_FLAMES)
7645               Feld[xx][yy] = EL_EMPTY;
7646             TEST_DrawLevelField(xx, yy);
7647           }
7648         }
7649       }
7650
7651       if (MovDelay[x][y])       /* element still has to wait some time */
7652       {
7653         PlayLevelSoundAction(x, y, ACTION_WAITING);
7654
7655         return;
7656       }
7657     }
7658
7659     /* now make next step */
7660
7661     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7662
7663     if (DONT_COLLIDE_WITH(element) &&
7664         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7665         !PLAYER_ENEMY_PROTECTED(newx, newy))
7666     {
7667       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7668
7669       return;
7670     }
7671
7672     else if (CAN_MOVE_INTO_ACID(element) &&
7673              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7674              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7675              (MovDir[x][y] == MV_DOWN ||
7676               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7677     {
7678       SplashAcid(newx, newy);
7679       Store[x][y] = EL_ACID;
7680     }
7681     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7682     {
7683       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7684           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7685           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7686           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7687       {
7688         RemoveField(x, y);
7689         TEST_DrawLevelField(x, y);
7690
7691         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7692         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7693           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7694
7695         local_player->friends_still_needed--;
7696         if (!local_player->friends_still_needed &&
7697             !local_player->GameOver && AllPlayersGone)
7698           PlayerWins(local_player);
7699
7700         return;
7701       }
7702       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7703       {
7704         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7705           TEST_DrawLevelField(newx, newy);
7706         else
7707           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7708       }
7709       else if (!IS_FREE(newx, newy))
7710       {
7711         GfxAction[x][y] = ACTION_WAITING;
7712
7713         if (IS_PLAYER(x, y))
7714           DrawPlayerField(x, y);
7715         else
7716           TEST_DrawLevelField(x, y);
7717
7718         return;
7719       }
7720     }
7721     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7722     {
7723       if (IS_FOOD_PIG(Feld[newx][newy]))
7724       {
7725         if (IS_MOVING(newx, newy))
7726           RemoveMovingField(newx, newy);
7727         else
7728         {
7729           Feld[newx][newy] = EL_EMPTY;
7730           TEST_DrawLevelField(newx, newy);
7731         }
7732
7733         PlayLevelSound(x, y, SND_PIG_DIGGING);
7734       }
7735       else if (!IS_FREE(newx, newy))
7736       {
7737         if (IS_PLAYER(x, y))
7738           DrawPlayerField(x, y);
7739         else
7740           TEST_DrawLevelField(x, y);
7741
7742         return;
7743       }
7744     }
7745     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7746     {
7747       if (Store[x][y] != EL_EMPTY)
7748       {
7749         boolean can_clone = FALSE;
7750         int xx, yy;
7751
7752         /* check if element to clone is still there */
7753         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7754         {
7755           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7756           {
7757             can_clone = TRUE;
7758
7759             break;
7760           }
7761         }
7762
7763         /* cannot clone or target field not free anymore -- do not clone */
7764         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7765           Store[x][y] = EL_EMPTY;
7766       }
7767
7768       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7769       {
7770         if (IS_MV_DIAGONAL(MovDir[x][y]))
7771         {
7772           int diagonal_move_dir = MovDir[x][y];
7773           int stored = Store[x][y];
7774           int change_delay = 8;
7775           int graphic;
7776
7777           /* android is moving diagonally */
7778
7779           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7780
7781           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7782           GfxElement[x][y] = EL_EMC_ANDROID;
7783           GfxAction[x][y] = ACTION_SHRINKING;
7784           GfxDir[x][y] = diagonal_move_dir;
7785           ChangeDelay[x][y] = change_delay;
7786
7787           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7788                                    GfxDir[x][y]);
7789
7790           DrawLevelGraphicAnimation(x, y, graphic);
7791           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7792
7793           if (Feld[newx][newy] == EL_ACID)
7794           {
7795             SplashAcid(newx, newy);
7796
7797             return;
7798           }
7799
7800           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7801
7802           Store[newx][newy] = EL_EMC_ANDROID;
7803           GfxElement[newx][newy] = EL_EMC_ANDROID;
7804           GfxAction[newx][newy] = ACTION_GROWING;
7805           GfxDir[newx][newy] = diagonal_move_dir;
7806           ChangeDelay[newx][newy] = change_delay;
7807
7808           graphic = el_act_dir2img(GfxElement[newx][newy],
7809                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7810
7811           DrawLevelGraphicAnimation(newx, newy, graphic);
7812           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7813
7814           return;
7815         }
7816         else
7817         {
7818           Feld[newx][newy] = EL_EMPTY;
7819           TEST_DrawLevelField(newx, newy);
7820
7821           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7822         }
7823       }
7824       else if (!IS_FREE(newx, newy))
7825       {
7826         return;
7827       }
7828     }
7829     else if (IS_CUSTOM_ELEMENT(element) &&
7830              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7831     {
7832       if (!DigFieldByCE(newx, newy, element))
7833         return;
7834
7835       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7836       {
7837         RunnerVisit[x][y] = FrameCounter;
7838         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7839       }
7840     }
7841     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7842     {
7843       if (!IS_FREE(newx, newy))
7844       {
7845         if (IS_PLAYER(x, y))
7846           DrawPlayerField(x, y);
7847         else
7848           TEST_DrawLevelField(x, y);
7849
7850         return;
7851       }
7852       else
7853       {
7854         boolean wanna_flame = !RND(10);
7855         int dx = newx - x, dy = newy - y;
7856         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7857         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7858         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7859                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7860         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7861                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7862
7863         if ((wanna_flame ||
7864              IS_CLASSIC_ENEMY(element1) ||
7865              IS_CLASSIC_ENEMY(element2)) &&
7866             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7867             element1 != EL_FLAMES && element2 != EL_FLAMES)
7868         {
7869           ResetGfxAnimation(x, y);
7870           GfxAction[x][y] = ACTION_ATTACKING;
7871
7872           if (IS_PLAYER(x, y))
7873             DrawPlayerField(x, y);
7874           else
7875             TEST_DrawLevelField(x, y);
7876
7877           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7878
7879           MovDelay[x][y] = 50;
7880
7881           Feld[newx][newy] = EL_FLAMES;
7882           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7883             Feld[newx1][newy1] = EL_FLAMES;
7884           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7885             Feld[newx2][newy2] = EL_FLAMES;
7886
7887           return;
7888         }
7889       }
7890     }
7891     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7892              Feld[newx][newy] == EL_DIAMOND)
7893     {
7894       if (IS_MOVING(newx, newy))
7895         RemoveMovingField(newx, newy);
7896       else
7897       {
7898         Feld[newx][newy] = EL_EMPTY;
7899         TEST_DrawLevelField(newx, newy);
7900       }
7901
7902       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7903     }
7904     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7905              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7906     {
7907       if (AmoebaNr[newx][newy])
7908       {
7909         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7910         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7911             Feld[newx][newy] == EL_BD_AMOEBA)
7912           AmoebaCnt[AmoebaNr[newx][newy]]--;
7913       }
7914
7915       if (IS_MOVING(newx, newy))
7916       {
7917         RemoveMovingField(newx, newy);
7918       }
7919       else
7920       {
7921         Feld[newx][newy] = EL_EMPTY;
7922         TEST_DrawLevelField(newx, newy);
7923       }
7924
7925       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7926     }
7927     else if ((element == EL_PACMAN || element == EL_MOLE)
7928              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7929     {
7930       if (AmoebaNr[newx][newy])
7931       {
7932         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7933         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7934             Feld[newx][newy] == EL_BD_AMOEBA)
7935           AmoebaCnt[AmoebaNr[newx][newy]]--;
7936       }
7937
7938       if (element == EL_MOLE)
7939       {
7940         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7941         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7942
7943         ResetGfxAnimation(x, y);
7944         GfxAction[x][y] = ACTION_DIGGING;
7945         TEST_DrawLevelField(x, y);
7946
7947         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7948
7949         return;                         /* wait for shrinking amoeba */
7950       }
7951       else      /* element == EL_PACMAN */
7952       {
7953         Feld[newx][newy] = EL_EMPTY;
7954         TEST_DrawLevelField(newx, newy);
7955         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7956       }
7957     }
7958     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7959              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7960               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7961     {
7962       /* wait for shrinking amoeba to completely disappear */
7963       return;
7964     }
7965     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7966     {
7967       /* object was running against a wall */
7968
7969       TurnRound(x, y);
7970
7971       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7972         DrawLevelElementAnimation(x, y, element);
7973
7974       if (DONT_TOUCH(element))
7975         TestIfBadThingTouchesPlayer(x, y);
7976
7977       return;
7978     }
7979
7980     InitMovingField(x, y, MovDir[x][y]);
7981
7982     PlayLevelSoundAction(x, y, ACTION_MOVING);
7983   }
7984
7985   if (MovDir[x][y])
7986     ContinueMoving(x, y);
7987 }
7988
7989 void ContinueMoving(int x, int y)
7990 {
7991   int element = Feld[x][y];
7992   struct ElementInfo *ei = &element_info[element];
7993   int direction = MovDir[x][y];
7994   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7995   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7996   int newx = x + dx, newy = y + dy;
7997   int stored = Store[x][y];
7998   int stored_new = Store[newx][newy];
7999   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8000   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8001   boolean last_line = (newy == lev_fieldy - 1);
8002
8003   MovPos[x][y] += getElementMoveStepsize(x, y);
8004
8005   if (pushed_by_player) /* special case: moving object pushed by player */
8006     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8007
8008   if (ABS(MovPos[x][y]) < TILEX)
8009   {
8010     TEST_DrawLevelField(x, y);
8011
8012     return;     /* element is still moving */
8013   }
8014
8015   /* element reached destination field */
8016
8017   Feld[x][y] = EL_EMPTY;
8018   Feld[newx][newy] = element;
8019   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8020
8021   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8022   {
8023     element = Feld[newx][newy] = EL_ACID;
8024   }
8025   else if (element == EL_MOLE)
8026   {
8027     Feld[x][y] = EL_SAND;
8028
8029     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8030   }
8031   else if (element == EL_QUICKSAND_FILLING)
8032   {
8033     element = Feld[newx][newy] = get_next_element(element);
8034     Store[newx][newy] = Store[x][y];
8035   }
8036   else if (element == EL_QUICKSAND_EMPTYING)
8037   {
8038     Feld[x][y] = get_next_element(element);
8039     element = Feld[newx][newy] = Store[x][y];
8040   }
8041   else if (element == EL_QUICKSAND_FAST_FILLING)
8042   {
8043     element = Feld[newx][newy] = get_next_element(element);
8044     Store[newx][newy] = Store[x][y];
8045   }
8046   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8047   {
8048     Feld[x][y] = get_next_element(element);
8049     element = Feld[newx][newy] = Store[x][y];
8050   }
8051   else if (element == EL_MAGIC_WALL_FILLING)
8052   {
8053     element = Feld[newx][newy] = get_next_element(element);
8054     if (!game.magic_wall_active)
8055       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8056     Store[newx][newy] = Store[x][y];
8057   }
8058   else if (element == EL_MAGIC_WALL_EMPTYING)
8059   {
8060     Feld[x][y] = get_next_element(element);
8061     if (!game.magic_wall_active)
8062       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8063     element = Feld[newx][newy] = Store[x][y];
8064
8065     InitField(newx, newy, FALSE);
8066   }
8067   else if (element == EL_BD_MAGIC_WALL_FILLING)
8068   {
8069     element = Feld[newx][newy] = get_next_element(element);
8070     if (!game.magic_wall_active)
8071       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8072     Store[newx][newy] = Store[x][y];
8073   }
8074   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8075   {
8076     Feld[x][y] = get_next_element(element);
8077     if (!game.magic_wall_active)
8078       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8079     element = Feld[newx][newy] = Store[x][y];
8080
8081     InitField(newx, newy, FALSE);
8082   }
8083   else if (element == EL_DC_MAGIC_WALL_FILLING)
8084   {
8085     element = Feld[newx][newy] = get_next_element(element);
8086     if (!game.magic_wall_active)
8087       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8088     Store[newx][newy] = Store[x][y];
8089   }
8090   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8091   {
8092     Feld[x][y] = get_next_element(element);
8093     if (!game.magic_wall_active)
8094       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8095     element = Feld[newx][newy] = Store[x][y];
8096
8097     InitField(newx, newy, FALSE);
8098   }
8099   else if (element == EL_AMOEBA_DROPPING)
8100   {
8101     Feld[x][y] = get_next_element(element);
8102     element = Feld[newx][newy] = Store[x][y];
8103   }
8104   else if (element == EL_SOKOBAN_OBJECT)
8105   {
8106     if (Back[x][y])
8107       Feld[x][y] = Back[x][y];
8108
8109     if (Back[newx][newy])
8110       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8111
8112     Back[x][y] = Back[newx][newy] = 0;
8113   }
8114
8115   Store[x][y] = EL_EMPTY;
8116   MovPos[x][y] = 0;
8117   MovDir[x][y] = 0;
8118   MovDelay[x][y] = 0;
8119
8120   MovDelay[newx][newy] = 0;
8121
8122   if (CAN_CHANGE_OR_HAS_ACTION(element))
8123   {
8124     /* copy element change control values to new field */
8125     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8126     ChangePage[newx][newy]  = ChangePage[x][y];
8127     ChangeCount[newx][newy] = ChangeCount[x][y];
8128     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8129   }
8130
8131   CustomValue[newx][newy] = CustomValue[x][y];
8132
8133   ChangeDelay[x][y] = 0;
8134   ChangePage[x][y] = -1;
8135   ChangeCount[x][y] = 0;
8136   ChangeEvent[x][y] = -1;
8137
8138   CustomValue[x][y] = 0;
8139
8140   /* copy animation control values to new field */
8141   GfxFrame[newx][newy]  = GfxFrame[x][y];
8142   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8143   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8144   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8145
8146   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8147
8148   /* some elements can leave other elements behind after moving */
8149   if (ei->move_leave_element != EL_EMPTY &&
8150       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8151       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8152   {
8153     int move_leave_element = ei->move_leave_element;
8154
8155     /* this makes it possible to leave the removed element again */
8156     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8157       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8158
8159     Feld[x][y] = move_leave_element;
8160
8161     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8162       MovDir[x][y] = direction;
8163
8164     InitField(x, y, FALSE);
8165
8166     if (GFX_CRUMBLED(Feld[x][y]))
8167       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8168
8169     if (ELEM_IS_PLAYER(move_leave_element))
8170       RelocatePlayer(x, y, move_leave_element);
8171   }
8172
8173   /* do this after checking for left-behind element */
8174   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8175
8176   if (!CAN_MOVE(element) ||
8177       (CAN_FALL(element) && direction == MV_DOWN &&
8178        (element == EL_SPRING ||
8179         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8180         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8181     GfxDir[x][y] = MovDir[newx][newy] = 0;
8182
8183   TEST_DrawLevelField(x, y);
8184   TEST_DrawLevelField(newx, newy);
8185
8186   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8187
8188   /* prevent pushed element from moving on in pushed direction */
8189   if (pushed_by_player && CAN_MOVE(element) &&
8190       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8191       !(element_info[element].move_pattern & direction))
8192     TurnRound(newx, newy);
8193
8194   /* prevent elements on conveyor belt from moving on in last direction */
8195   if (pushed_by_conveyor && CAN_FALL(element) &&
8196       direction & MV_HORIZONTAL)
8197     MovDir[newx][newy] = 0;
8198
8199   if (!pushed_by_player)
8200   {
8201     int nextx = newx + dx, nexty = newy + dy;
8202     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8203
8204     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8205
8206     if (CAN_FALL(element) && direction == MV_DOWN)
8207       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8208
8209     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8210       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8211
8212     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8213       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8214   }
8215
8216   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8217   {
8218     TestIfBadThingTouchesPlayer(newx, newy);
8219     TestIfBadThingTouchesFriend(newx, newy);
8220
8221     if (!IS_CUSTOM_ELEMENT(element))
8222       TestIfBadThingTouchesOtherBadThing(newx, newy);
8223   }
8224   else if (element == EL_PENGUIN)
8225     TestIfFriendTouchesBadThing(newx, newy);
8226
8227   if (DONT_GET_HIT_BY(element))
8228   {
8229     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8230   }
8231
8232   /* give the player one last chance (one more frame) to move away */
8233   if (CAN_FALL(element) && direction == MV_DOWN &&
8234       (last_line || (!IS_FREE(x, newy + 1) &&
8235                      (!IS_PLAYER(x, newy + 1) ||
8236                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8237     Impact(x, newy);
8238
8239   if (pushed_by_player && !game.use_change_when_pushing_bug)
8240   {
8241     int push_side = MV_DIR_OPPOSITE(direction);
8242     struct PlayerInfo *player = PLAYERINFO(x, y);
8243
8244     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8245                                player->index_bit, push_side);
8246     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8247                                         player->index_bit, push_side);
8248   }
8249
8250   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8251     MovDelay[newx][newy] = 1;
8252
8253   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8254
8255   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8256   TestIfElementHitsCustomElement(newx, newy, direction);
8257   TestIfPlayerTouchesCustomElement(newx, newy);
8258   TestIfElementTouchesCustomElement(newx, newy);
8259
8260   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8261       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8262     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8263                              MV_DIR_OPPOSITE(direction));
8264 }
8265
8266 int AmoebeNachbarNr(int ax, int ay)
8267 {
8268   int i;
8269   int element = Feld[ax][ay];
8270   int group_nr = 0;
8271   static int xy[4][2] =
8272   {
8273     { 0, -1 },
8274     { -1, 0 },
8275     { +1, 0 },
8276     { 0, +1 }
8277   };
8278
8279   for (i = 0; i < NUM_DIRECTIONS; i++)
8280   {
8281     int x = ax + xy[i][0];
8282     int y = ay + xy[i][1];
8283
8284     if (!IN_LEV_FIELD(x, y))
8285       continue;
8286
8287     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8288       group_nr = AmoebaNr[x][y];
8289   }
8290
8291   return group_nr;
8292 }
8293
8294 void AmoebenVereinigen(int ax, int ay)
8295 {
8296   int i, x, y, xx, yy;
8297   int new_group_nr = AmoebaNr[ax][ay];
8298   static int xy[4][2] =
8299   {
8300     { 0, -1 },
8301     { -1, 0 },
8302     { +1, 0 },
8303     { 0, +1 }
8304   };
8305
8306   if (new_group_nr == 0)
8307     return;
8308
8309   for (i = 0; i < NUM_DIRECTIONS; i++)
8310   {
8311     x = ax + xy[i][0];
8312     y = ay + xy[i][1];
8313
8314     if (!IN_LEV_FIELD(x, y))
8315       continue;
8316
8317     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8318          Feld[x][y] == EL_BD_AMOEBA ||
8319          Feld[x][y] == EL_AMOEBA_DEAD) &&
8320         AmoebaNr[x][y] != new_group_nr)
8321     {
8322       int old_group_nr = AmoebaNr[x][y];
8323
8324       if (old_group_nr == 0)
8325         return;
8326
8327       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8328       AmoebaCnt[old_group_nr] = 0;
8329       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8330       AmoebaCnt2[old_group_nr] = 0;
8331
8332       SCAN_PLAYFIELD(xx, yy)
8333       {
8334         if (AmoebaNr[xx][yy] == old_group_nr)
8335           AmoebaNr[xx][yy] = new_group_nr;
8336       }
8337     }
8338   }
8339 }
8340
8341 void AmoebeUmwandeln(int ax, int ay)
8342 {
8343   int i, x, y;
8344
8345   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8346   {
8347     int group_nr = AmoebaNr[ax][ay];
8348
8349 #ifdef DEBUG
8350     if (group_nr == 0)
8351     {
8352       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8353       printf("AmoebeUmwandeln(): This should never happen!\n");
8354       return;
8355     }
8356 #endif
8357
8358     SCAN_PLAYFIELD(x, y)
8359     {
8360       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8361       {
8362         AmoebaNr[x][y] = 0;
8363         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8364       }
8365     }
8366
8367     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8368                             SND_AMOEBA_TURNING_TO_GEM :
8369                             SND_AMOEBA_TURNING_TO_ROCK));
8370     Bang(ax, ay);
8371   }
8372   else
8373   {
8374     static int xy[4][2] =
8375     {
8376       { 0, -1 },
8377       { -1, 0 },
8378       { +1, 0 },
8379       { 0, +1 }
8380     };
8381
8382     for (i = 0; i < NUM_DIRECTIONS; i++)
8383     {
8384       x = ax + xy[i][0];
8385       y = ay + xy[i][1];
8386
8387       if (!IN_LEV_FIELD(x, y))
8388         continue;
8389
8390       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8391       {
8392         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8393                               SND_AMOEBA_TURNING_TO_GEM :
8394                               SND_AMOEBA_TURNING_TO_ROCK));
8395         Bang(x, y);
8396       }
8397     }
8398   }
8399 }
8400
8401 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8402 {
8403   int x, y;
8404   int group_nr = AmoebaNr[ax][ay];
8405   boolean done = FALSE;
8406
8407 #ifdef DEBUG
8408   if (group_nr == 0)
8409   {
8410     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8411     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8412     return;
8413   }
8414 #endif
8415
8416   SCAN_PLAYFIELD(x, y)
8417   {
8418     if (AmoebaNr[x][y] == group_nr &&
8419         (Feld[x][y] == EL_AMOEBA_DEAD ||
8420          Feld[x][y] == EL_BD_AMOEBA ||
8421          Feld[x][y] == EL_AMOEBA_GROWING))
8422     {
8423       AmoebaNr[x][y] = 0;
8424       Feld[x][y] = new_element;
8425       InitField(x, y, FALSE);
8426       TEST_DrawLevelField(x, y);
8427       done = TRUE;
8428     }
8429   }
8430
8431   if (done)
8432     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8433                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8434                             SND_BD_AMOEBA_TURNING_TO_GEM));
8435 }
8436
8437 void AmoebeWaechst(int x, int y)
8438 {
8439   static unsigned int sound_delay = 0;
8440   static unsigned int sound_delay_value = 0;
8441
8442   if (!MovDelay[x][y])          /* start new growing cycle */
8443   {
8444     MovDelay[x][y] = 7;
8445
8446     if (DelayReached(&sound_delay, sound_delay_value))
8447     {
8448       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8449       sound_delay_value = 30;
8450     }
8451   }
8452
8453   if (MovDelay[x][y])           /* wait some time before growing bigger */
8454   {
8455     MovDelay[x][y]--;
8456     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8457     {
8458       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8459                                            6 - MovDelay[x][y]);
8460
8461       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8462     }
8463
8464     if (!MovDelay[x][y])
8465     {
8466       Feld[x][y] = Store[x][y];
8467       Store[x][y] = 0;
8468       TEST_DrawLevelField(x, y);
8469     }
8470   }
8471 }
8472
8473 void AmoebaDisappearing(int x, int y)
8474 {
8475   static unsigned int sound_delay = 0;
8476   static unsigned int sound_delay_value = 0;
8477
8478   if (!MovDelay[x][y])          /* start new shrinking cycle */
8479   {
8480     MovDelay[x][y] = 7;
8481
8482     if (DelayReached(&sound_delay, sound_delay_value))
8483       sound_delay_value = 30;
8484   }
8485
8486   if (MovDelay[x][y])           /* wait some time before shrinking */
8487   {
8488     MovDelay[x][y]--;
8489     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8490     {
8491       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8492                                            6 - MovDelay[x][y]);
8493
8494       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8495     }
8496
8497     if (!MovDelay[x][y])
8498     {
8499       Feld[x][y] = EL_EMPTY;
8500       TEST_DrawLevelField(x, y);
8501
8502       /* don't let mole enter this field in this cycle;
8503          (give priority to objects falling to this field from above) */
8504       Stop[x][y] = TRUE;
8505     }
8506   }
8507 }
8508
8509 void AmoebeAbleger(int ax, int ay)
8510 {
8511   int i;
8512   int element = Feld[ax][ay];
8513   int graphic = el2img(element);
8514   int newax = ax, neway = ay;
8515   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8516   static int xy[4][2] =
8517   {
8518     { 0, -1 },
8519     { -1, 0 },
8520     { +1, 0 },
8521     { 0, +1 }
8522   };
8523
8524   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8525   {
8526     Feld[ax][ay] = EL_AMOEBA_DEAD;
8527     TEST_DrawLevelField(ax, ay);
8528     return;
8529   }
8530
8531   if (IS_ANIMATED(graphic))
8532     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8533
8534   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8535     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8536
8537   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8538   {
8539     MovDelay[ax][ay]--;
8540     if (MovDelay[ax][ay])
8541       return;
8542   }
8543
8544   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8545   {
8546     int start = RND(4);
8547     int x = ax + xy[start][0];
8548     int y = ay + xy[start][1];
8549
8550     if (!IN_LEV_FIELD(x, y))
8551       return;
8552
8553     if (IS_FREE(x, y) ||
8554         CAN_GROW_INTO(Feld[x][y]) ||
8555         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8556         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8557     {
8558       newax = x;
8559       neway = y;
8560     }
8561
8562     if (newax == ax && neway == ay)
8563       return;
8564   }
8565   else                          /* normal or "filled" (BD style) amoeba */
8566   {
8567     int start = RND(4);
8568     boolean waiting_for_player = FALSE;
8569
8570     for (i = 0; i < NUM_DIRECTIONS; i++)
8571     {
8572       int j = (start + i) % 4;
8573       int x = ax + xy[j][0];
8574       int y = ay + xy[j][1];
8575
8576       if (!IN_LEV_FIELD(x, y))
8577         continue;
8578
8579       if (IS_FREE(x, y) ||
8580           CAN_GROW_INTO(Feld[x][y]) ||
8581           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8582           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8583       {
8584         newax = x;
8585         neway = y;
8586         break;
8587       }
8588       else if (IS_PLAYER(x, y))
8589         waiting_for_player = TRUE;
8590     }
8591
8592     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8593     {
8594       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8595       {
8596         Feld[ax][ay] = EL_AMOEBA_DEAD;
8597         TEST_DrawLevelField(ax, ay);
8598         AmoebaCnt[AmoebaNr[ax][ay]]--;
8599
8600         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8601         {
8602           if (element == EL_AMOEBA_FULL)
8603             AmoebeUmwandeln(ax, ay);
8604           else if (element == EL_BD_AMOEBA)
8605             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8606         }
8607       }
8608       return;
8609     }
8610     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8611     {
8612       /* amoeba gets larger by growing in some direction */
8613
8614       int new_group_nr = AmoebaNr[ax][ay];
8615
8616 #ifdef DEBUG
8617   if (new_group_nr == 0)
8618   {
8619     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8620     printf("AmoebeAbleger(): This should never happen!\n");
8621     return;
8622   }
8623 #endif
8624
8625       AmoebaNr[newax][neway] = new_group_nr;
8626       AmoebaCnt[new_group_nr]++;
8627       AmoebaCnt2[new_group_nr]++;
8628
8629       /* if amoeba touches other amoeba(s) after growing, unify them */
8630       AmoebenVereinigen(newax, neway);
8631
8632       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8633       {
8634         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8635         return;
8636       }
8637     }
8638   }
8639
8640   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8641       (neway == lev_fieldy - 1 && newax != ax))
8642   {
8643     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8644     Store[newax][neway] = element;
8645   }
8646   else if (neway == ay || element == EL_EMC_DRIPPER)
8647   {
8648     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8649
8650     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8651   }
8652   else
8653   {
8654     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8655     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8656     Store[ax][ay] = EL_AMOEBA_DROP;
8657     ContinueMoving(ax, ay);
8658     return;
8659   }
8660
8661   TEST_DrawLevelField(newax, neway);
8662 }
8663
8664 void Life(int ax, int ay)
8665 {
8666   int x1, y1, x2, y2;
8667   int life_time = 40;
8668   int element = Feld[ax][ay];
8669   int graphic = el2img(element);
8670   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8671                          level.biomaze);
8672   boolean changed = FALSE;
8673
8674   if (IS_ANIMATED(graphic))
8675     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8676
8677   if (Stop[ax][ay])
8678     return;
8679
8680   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8681     MovDelay[ax][ay] = life_time;
8682
8683   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8684   {
8685     MovDelay[ax][ay]--;
8686     if (MovDelay[ax][ay])
8687       return;
8688   }
8689
8690   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8691   {
8692     int xx = ax+x1, yy = ay+y1;
8693     int nachbarn = 0;
8694
8695     if (!IN_LEV_FIELD(xx, yy))
8696       continue;
8697
8698     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8699     {
8700       int x = xx+x2, y = yy+y2;
8701
8702       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8703         continue;
8704
8705       if (((Feld[x][y] == element ||
8706             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8707            !Stop[x][y]) ||
8708           (IS_FREE(x, y) && Stop[x][y]))
8709         nachbarn++;
8710     }
8711
8712     if (xx == ax && yy == ay)           /* field in the middle */
8713     {
8714       if (nachbarn < life_parameter[0] ||
8715           nachbarn > life_parameter[1])
8716       {
8717         Feld[xx][yy] = EL_EMPTY;
8718         if (!Stop[xx][yy])
8719           TEST_DrawLevelField(xx, yy);
8720         Stop[xx][yy] = TRUE;
8721         changed = TRUE;
8722       }
8723     }
8724     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8725     {                                   /* free border field */
8726       if (nachbarn >= life_parameter[2] &&
8727           nachbarn <= life_parameter[3])
8728       {
8729         Feld[xx][yy] = element;
8730         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8731         if (!Stop[xx][yy])
8732           TEST_DrawLevelField(xx, yy);
8733         Stop[xx][yy] = TRUE;
8734         changed = TRUE;
8735       }
8736     }
8737   }
8738
8739   if (changed)
8740     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8741                    SND_GAME_OF_LIFE_GROWING);
8742 }
8743
8744 static void InitRobotWheel(int x, int y)
8745 {
8746   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8747 }
8748
8749 static void RunRobotWheel(int x, int y)
8750 {
8751   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8752 }
8753
8754 static void StopRobotWheel(int x, int y)
8755 {
8756   if (ZX == x && ZY == y)
8757   {
8758     ZX = ZY = -1;
8759
8760     game.robot_wheel_active = FALSE;
8761   }
8762 }
8763
8764 static void InitTimegateWheel(int x, int y)
8765 {
8766   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8767 }
8768
8769 static void RunTimegateWheel(int x, int y)
8770 {
8771   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8772 }
8773
8774 static void InitMagicBallDelay(int x, int y)
8775 {
8776   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8777 }
8778
8779 static void ActivateMagicBall(int bx, int by)
8780 {
8781   int x, y;
8782
8783   if (level.ball_random)
8784   {
8785     int pos_border = RND(8);    /* select one of the eight border elements */
8786     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8787     int xx = pos_content % 3;
8788     int yy = pos_content / 3;
8789
8790     x = bx - 1 + xx;
8791     y = by - 1 + yy;
8792
8793     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8794       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8795   }
8796   else
8797   {
8798     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8799     {
8800       int xx = x - bx + 1;
8801       int yy = y - by + 1;
8802
8803       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8804         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8805     }
8806   }
8807
8808   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8809 }
8810
8811 void CheckExit(int x, int y)
8812 {
8813   if (local_player->gems_still_needed > 0 ||
8814       local_player->sokobanfields_still_needed > 0 ||
8815       local_player->lights_still_needed > 0)
8816   {
8817     int element = Feld[x][y];
8818     int graphic = el2img(element);
8819
8820     if (IS_ANIMATED(graphic))
8821       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8822
8823     return;
8824   }
8825
8826   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8827     return;
8828
8829   Feld[x][y] = EL_EXIT_OPENING;
8830
8831   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8832 }
8833
8834 void CheckExitEM(int x, int y)
8835 {
8836   if (local_player->gems_still_needed > 0 ||
8837       local_player->sokobanfields_still_needed > 0 ||
8838       local_player->lights_still_needed > 0)
8839   {
8840     int element = Feld[x][y];
8841     int graphic = el2img(element);
8842
8843     if (IS_ANIMATED(graphic))
8844       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8845
8846     return;
8847   }
8848
8849   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8850     return;
8851
8852   Feld[x][y] = EL_EM_EXIT_OPENING;
8853
8854   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8855 }
8856
8857 void CheckExitSteel(int x, int y)
8858 {
8859   if (local_player->gems_still_needed > 0 ||
8860       local_player->sokobanfields_still_needed > 0 ||
8861       local_player->lights_still_needed > 0)
8862   {
8863     int element = Feld[x][y];
8864     int graphic = el2img(element);
8865
8866     if (IS_ANIMATED(graphic))
8867       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8868
8869     return;
8870   }
8871
8872   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8873     return;
8874
8875   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8876
8877   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8878 }
8879
8880 void CheckExitSteelEM(int x, int y)
8881 {
8882   if (local_player->gems_still_needed > 0 ||
8883       local_player->sokobanfields_still_needed > 0 ||
8884       local_player->lights_still_needed > 0)
8885   {
8886     int element = Feld[x][y];
8887     int graphic = el2img(element);
8888
8889     if (IS_ANIMATED(graphic))
8890       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8891
8892     return;
8893   }
8894
8895   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8896     return;
8897
8898   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8899
8900   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8901 }
8902
8903 void CheckExitSP(int x, int y)
8904 {
8905   if (local_player->gems_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_SP_EXIT_OPENING;
8920
8921   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8922 }
8923
8924 static void CloseAllOpenTimegates()
8925 {
8926   int x, y;
8927
8928   SCAN_PLAYFIELD(x, y)
8929   {
8930     int element = Feld[x][y];
8931
8932     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8933     {
8934       Feld[x][y] = EL_TIMEGATE_CLOSING;
8935
8936       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8937     }
8938   }
8939 }
8940
8941 void DrawTwinkleOnField(int x, int y)
8942 {
8943   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8944     return;
8945
8946   if (Feld[x][y] == EL_BD_DIAMOND)
8947     return;
8948
8949   if (MovDelay[x][y] == 0)      /* next animation frame */
8950     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8951
8952   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8953   {
8954     MovDelay[x][y]--;
8955
8956     DrawLevelElementAnimation(x, y, Feld[x][y]);
8957
8958     if (MovDelay[x][y] != 0)
8959     {
8960       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8961                                            10 - MovDelay[x][y]);
8962
8963       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8964     }
8965   }
8966 }
8967
8968 void MauerWaechst(int x, int y)
8969 {
8970   int delay = 6;
8971
8972   if (!MovDelay[x][y])          /* next animation frame */
8973     MovDelay[x][y] = 3 * delay;
8974
8975   if (MovDelay[x][y])           /* wait some time before next frame */
8976   {
8977     MovDelay[x][y]--;
8978
8979     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8980     {
8981       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8982       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8983
8984       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8985     }
8986
8987     if (!MovDelay[x][y])
8988     {
8989       if (MovDir[x][y] == MV_LEFT)
8990       {
8991         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8992           TEST_DrawLevelField(x - 1, y);
8993       }
8994       else if (MovDir[x][y] == MV_RIGHT)
8995       {
8996         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8997           TEST_DrawLevelField(x + 1, y);
8998       }
8999       else if (MovDir[x][y] == MV_UP)
9000       {
9001         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9002           TEST_DrawLevelField(x, y - 1);
9003       }
9004       else
9005       {
9006         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9007           TEST_DrawLevelField(x, y + 1);
9008       }
9009
9010       Feld[x][y] = Store[x][y];
9011       Store[x][y] = 0;
9012       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9013       TEST_DrawLevelField(x, y);
9014     }
9015   }
9016 }
9017
9018 void MauerAbleger(int ax, int ay)
9019 {
9020   int element = Feld[ax][ay];
9021   int graphic = el2img(element);
9022   boolean oben_frei = FALSE, unten_frei = FALSE;
9023   boolean links_frei = FALSE, rechts_frei = FALSE;
9024   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9025   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9026   boolean new_wall = FALSE;
9027
9028   if (IS_ANIMATED(graphic))
9029     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9030
9031   if (!MovDelay[ax][ay])        /* start building new wall */
9032     MovDelay[ax][ay] = 6;
9033
9034   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9035   {
9036     MovDelay[ax][ay]--;
9037     if (MovDelay[ax][ay])
9038       return;
9039   }
9040
9041   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9042     oben_frei = TRUE;
9043   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9044     unten_frei = TRUE;
9045   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9046     links_frei = TRUE;
9047   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9048     rechts_frei = TRUE;
9049
9050   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9051       element == EL_EXPANDABLE_WALL_ANY)
9052   {
9053     if (oben_frei)
9054     {
9055       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9056       Store[ax][ay-1] = element;
9057       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9058       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9059         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9060                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9061       new_wall = TRUE;
9062     }
9063     if (unten_frei)
9064     {
9065       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9066       Store[ax][ay+1] = element;
9067       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9068       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9069         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9070                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9071       new_wall = TRUE;
9072     }
9073   }
9074
9075   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9076       element == EL_EXPANDABLE_WALL_ANY ||
9077       element == EL_EXPANDABLE_WALL ||
9078       element == EL_BD_EXPANDABLE_WALL)
9079   {
9080     if (links_frei)
9081     {
9082       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9083       Store[ax-1][ay] = element;
9084       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9085       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9086         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9087                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9088       new_wall = TRUE;
9089     }
9090
9091     if (rechts_frei)
9092     {
9093       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9094       Store[ax+1][ay] = element;
9095       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9096       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9097         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9098                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9099       new_wall = TRUE;
9100     }
9101   }
9102
9103   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9104     TEST_DrawLevelField(ax, ay);
9105
9106   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9107     oben_massiv = TRUE;
9108   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9109     unten_massiv = TRUE;
9110   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9111     links_massiv = TRUE;
9112   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9113     rechts_massiv = TRUE;
9114
9115   if (((oben_massiv && unten_massiv) ||
9116        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9117        element == EL_EXPANDABLE_WALL) &&
9118       ((links_massiv && rechts_massiv) ||
9119        element == EL_EXPANDABLE_WALL_VERTICAL))
9120     Feld[ax][ay] = EL_WALL;
9121
9122   if (new_wall)
9123     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9124 }
9125
9126 void MauerAblegerStahl(int ax, int ay)
9127 {
9128   int element = Feld[ax][ay];
9129   int graphic = el2img(element);
9130   boolean oben_frei = FALSE, unten_frei = FALSE;
9131   boolean links_frei = FALSE, rechts_frei = FALSE;
9132   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9133   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9134   boolean new_wall = FALSE;
9135
9136   if (IS_ANIMATED(graphic))
9137     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9138
9139   if (!MovDelay[ax][ay])        /* start building new wall */
9140     MovDelay[ax][ay] = 6;
9141
9142   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9143   {
9144     MovDelay[ax][ay]--;
9145     if (MovDelay[ax][ay])
9146       return;
9147   }
9148
9149   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9150     oben_frei = TRUE;
9151   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9152     unten_frei = TRUE;
9153   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9154     links_frei = TRUE;
9155   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9156     rechts_frei = TRUE;
9157
9158   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9159       element == EL_EXPANDABLE_STEELWALL_ANY)
9160   {
9161     if (oben_frei)
9162     {
9163       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9164       Store[ax][ay-1] = element;
9165       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9166       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9167         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9168                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9169       new_wall = TRUE;
9170     }
9171     if (unten_frei)
9172     {
9173       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9174       Store[ax][ay+1] = element;
9175       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9176       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9177         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9178                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9179       new_wall = TRUE;
9180     }
9181   }
9182
9183   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9184       element == EL_EXPANDABLE_STEELWALL_ANY)
9185   {
9186     if (links_frei)
9187     {
9188       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9189       Store[ax-1][ay] = element;
9190       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9191       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9192         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9193                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9194       new_wall = TRUE;
9195     }
9196
9197     if (rechts_frei)
9198     {
9199       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9200       Store[ax+1][ay] = element;
9201       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9202       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9203         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9204                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9205       new_wall = TRUE;
9206     }
9207   }
9208
9209   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9210     oben_massiv = TRUE;
9211   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9212     unten_massiv = TRUE;
9213   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9214     links_massiv = TRUE;
9215   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9216     rechts_massiv = TRUE;
9217
9218   if (((oben_massiv && unten_massiv) ||
9219        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9220       ((links_massiv && rechts_massiv) ||
9221        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9222     Feld[ax][ay] = EL_STEELWALL;
9223
9224   if (new_wall)
9225     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9226 }
9227
9228 void CheckForDragon(int x, int y)
9229 {
9230   int i, j;
9231   boolean dragon_found = FALSE;
9232   static int xy[4][2] =
9233   {
9234     { 0, -1 },
9235     { -1, 0 },
9236     { +1, 0 },
9237     { 0, +1 }
9238   };
9239
9240   for (i = 0; i < NUM_DIRECTIONS; i++)
9241   {
9242     for (j = 0; j < 4; j++)
9243     {
9244       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9245
9246       if (IN_LEV_FIELD(xx, yy) &&
9247           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9248       {
9249         if (Feld[xx][yy] == EL_DRAGON)
9250           dragon_found = TRUE;
9251       }
9252       else
9253         break;
9254     }
9255   }
9256
9257   if (!dragon_found)
9258   {
9259     for (i = 0; i < NUM_DIRECTIONS; i++)
9260     {
9261       for (j = 0; j < 3; j++)
9262       {
9263         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9264   
9265         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9266         {
9267           Feld[xx][yy] = EL_EMPTY;
9268           TEST_DrawLevelField(xx, yy);
9269         }
9270         else
9271           break;
9272       }
9273     }
9274   }
9275 }
9276
9277 static void InitBuggyBase(int x, int y)
9278 {
9279   int element = Feld[x][y];
9280   int activating_delay = FRAMES_PER_SECOND / 4;
9281
9282   ChangeDelay[x][y] =
9283     (element == EL_SP_BUGGY_BASE ?
9284      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9285      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9286      activating_delay :
9287      element == EL_SP_BUGGY_BASE_ACTIVE ?
9288      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9289 }
9290
9291 static void WarnBuggyBase(int x, int y)
9292 {
9293   int i;
9294   static int xy[4][2] =
9295   {
9296     { 0, -1 },
9297     { -1, 0 },
9298     { +1, 0 },
9299     { 0, +1 }
9300   };
9301
9302   for (i = 0; i < NUM_DIRECTIONS; i++)
9303   {
9304     int xx = x + xy[i][0];
9305     int yy = y + xy[i][1];
9306
9307     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9308     {
9309       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9310
9311       break;
9312     }
9313   }
9314 }
9315
9316 static void InitTrap(int x, int y)
9317 {
9318   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9319 }
9320
9321 static void ActivateTrap(int x, int y)
9322 {
9323   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9324 }
9325
9326 static void ChangeActiveTrap(int x, int y)
9327 {
9328   int graphic = IMG_TRAP_ACTIVE;
9329
9330   /* if new animation frame was drawn, correct crumbled sand border */
9331   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9332     TEST_DrawLevelFieldCrumbled(x, y);
9333 }
9334
9335 static int getSpecialActionElement(int element, int number, int base_element)
9336 {
9337   return (element != EL_EMPTY ? element :
9338           number != -1 ? base_element + number - 1 :
9339           EL_EMPTY);
9340 }
9341
9342 static int getModifiedActionNumber(int value_old, int operator, int operand,
9343                                    int value_min, int value_max)
9344 {
9345   int value_new = (operator == CA_MODE_SET      ? operand :
9346                    operator == CA_MODE_ADD      ? value_old + operand :
9347                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9348                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9349                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9350                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9351                    value_old);
9352
9353   return (value_new < value_min ? value_min :
9354           value_new > value_max ? value_max :
9355           value_new);
9356 }
9357
9358 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9359 {
9360   struct ElementInfo *ei = &element_info[element];
9361   struct ElementChangeInfo *change = &ei->change_page[page];
9362   int target_element = change->target_element;
9363   int action_type = change->action_type;
9364   int action_mode = change->action_mode;
9365   int action_arg = change->action_arg;
9366   int action_element = change->action_element;
9367   int i;
9368
9369   if (!change->has_action)
9370     return;
9371
9372   /* ---------- determine action paramater values -------------------------- */
9373
9374   int level_time_value =
9375     (level.time > 0 ? TimeLeft :
9376      TimePlayed);
9377
9378   int action_arg_element_raw =
9379     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9380      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9381      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9382      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9383      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9384      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9385      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9386      EL_EMPTY);
9387   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9388
9389   int action_arg_direction =
9390     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9391      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9392      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9393      change->actual_trigger_side :
9394      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9395      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9396      MV_NONE);
9397
9398   int action_arg_number_min =
9399     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9400      CA_ARG_MIN);
9401
9402   int action_arg_number_max =
9403     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9404      action_type == CA_SET_LEVEL_GEMS ? 999 :
9405      action_type == CA_SET_LEVEL_TIME ? 9999 :
9406      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9407      action_type == CA_SET_CE_VALUE ? 9999 :
9408      action_type == CA_SET_CE_SCORE ? 9999 :
9409      CA_ARG_MAX);
9410
9411   int action_arg_number_reset =
9412     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9413      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9414      action_type == CA_SET_LEVEL_TIME ? level.time :
9415      action_type == CA_SET_LEVEL_SCORE ? 0 :
9416      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9417      action_type == CA_SET_CE_SCORE ? 0 :
9418      0);
9419
9420   int action_arg_number =
9421     (action_arg <= CA_ARG_MAX ? action_arg :
9422      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9423      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9424      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9425      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9426      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9427      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9428      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9429      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9430      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9431      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9432      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9433      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9434      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9435      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9436      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9437      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9438      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9439      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9440      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9441      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9442      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9443      -1);
9444
9445   int action_arg_number_old =
9446     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9447      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9448      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9449      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9450      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9451      0);
9452
9453   int action_arg_number_new =
9454     getModifiedActionNumber(action_arg_number_old,
9455                             action_mode, action_arg_number,
9456                             action_arg_number_min, action_arg_number_max);
9457
9458   int trigger_player_bits =
9459     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9460      change->actual_trigger_player_bits : change->trigger_player);
9461
9462   int action_arg_player_bits =
9463     (action_arg >= CA_ARG_PLAYER_1 &&
9464      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9465      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9466      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9467      PLAYER_BITS_ANY);
9468
9469   /* ---------- execute action  -------------------------------------------- */
9470
9471   switch (action_type)
9472   {
9473     case CA_NO_ACTION:
9474     {
9475       return;
9476     }
9477
9478     /* ---------- level actions  ------------------------------------------- */
9479
9480     case CA_RESTART_LEVEL:
9481     {
9482       game.restart_level = TRUE;
9483
9484       break;
9485     }
9486
9487     case CA_SHOW_ENVELOPE:
9488     {
9489       int element = getSpecialActionElement(action_arg_element,
9490                                             action_arg_number, EL_ENVELOPE_1);
9491
9492       if (IS_ENVELOPE(element))
9493         local_player->show_envelope = element;
9494
9495       break;
9496     }
9497
9498     case CA_SET_LEVEL_TIME:
9499     {
9500       if (level.time > 0)       /* only modify limited time value */
9501       {
9502         TimeLeft = action_arg_number_new;
9503
9504         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9505
9506         DisplayGameControlValues();
9507
9508         if (!TimeLeft && setup.time_limit)
9509           for (i = 0; i < MAX_PLAYERS; i++)
9510             KillPlayer(&stored_player[i]);
9511       }
9512
9513       break;
9514     }
9515
9516     case CA_SET_LEVEL_SCORE:
9517     {
9518       local_player->score = action_arg_number_new;
9519
9520       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9521
9522       DisplayGameControlValues();
9523
9524       break;
9525     }
9526
9527     case CA_SET_LEVEL_GEMS:
9528     {
9529       local_player->gems_still_needed = action_arg_number_new;
9530
9531       game_panel_controls[GAME_PANEL_GEMS].value =
9532         local_player->gems_still_needed;
9533
9534       DisplayGameControlValues();
9535
9536       break;
9537     }
9538
9539     case CA_SET_LEVEL_WIND:
9540     {
9541       game.wind_direction = action_arg_direction;
9542
9543       break;
9544     }
9545
9546     case CA_SET_LEVEL_RANDOM_SEED:
9547     {
9548       /* ensure that setting a new random seed while playing is predictable */
9549       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9550
9551       break;
9552     }
9553
9554     /* ---------- player actions  ------------------------------------------ */
9555
9556     case CA_MOVE_PLAYER:
9557     {
9558       /* automatically move to the next field in specified direction */
9559       for (i = 0; i < MAX_PLAYERS; i++)
9560         if (trigger_player_bits & (1 << i))
9561           stored_player[i].programmed_action = action_arg_direction;
9562
9563       break;
9564     }
9565
9566     case CA_EXIT_PLAYER:
9567     {
9568       for (i = 0; i < MAX_PLAYERS; i++)
9569         if (action_arg_player_bits & (1 << i))
9570           PlayerWins(&stored_player[i]);
9571
9572       break;
9573     }
9574
9575     case CA_KILL_PLAYER:
9576     {
9577       for (i = 0; i < MAX_PLAYERS; i++)
9578         if (action_arg_player_bits & (1 << i))
9579           KillPlayer(&stored_player[i]);
9580
9581       break;
9582     }
9583
9584     case CA_SET_PLAYER_KEYS:
9585     {
9586       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9587       int element = getSpecialActionElement(action_arg_element,
9588                                             action_arg_number, EL_KEY_1);
9589
9590       if (IS_KEY(element))
9591       {
9592         for (i = 0; i < MAX_PLAYERS; i++)
9593         {
9594           if (trigger_player_bits & (1 << i))
9595           {
9596             stored_player[i].key[KEY_NR(element)] = key_state;
9597
9598             DrawGameDoorValues();
9599           }
9600         }
9601       }
9602
9603       break;
9604     }
9605
9606     case CA_SET_PLAYER_SPEED:
9607     {
9608       for (i = 0; i < MAX_PLAYERS; i++)
9609       {
9610         if (trigger_player_bits & (1 << i))
9611         {
9612           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9613
9614           if (action_arg == CA_ARG_SPEED_FASTER &&
9615               stored_player[i].cannot_move)
9616           {
9617             action_arg_number = STEPSIZE_VERY_SLOW;
9618           }
9619           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9620                    action_arg == CA_ARG_SPEED_FASTER)
9621           {
9622             action_arg_number = 2;
9623             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9624                            CA_MODE_MULTIPLY);
9625           }
9626           else if (action_arg == CA_ARG_NUMBER_RESET)
9627           {
9628             action_arg_number = level.initial_player_stepsize[i];
9629           }
9630
9631           move_stepsize =
9632             getModifiedActionNumber(move_stepsize,
9633                                     action_mode,
9634                                     action_arg_number,
9635                                     action_arg_number_min,
9636                                     action_arg_number_max);
9637
9638           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9639         }
9640       }
9641
9642       break;
9643     }
9644
9645     case CA_SET_PLAYER_SHIELD:
9646     {
9647       for (i = 0; i < MAX_PLAYERS; i++)
9648       {
9649         if (trigger_player_bits & (1 << i))
9650         {
9651           if (action_arg == CA_ARG_SHIELD_OFF)
9652           {
9653             stored_player[i].shield_normal_time_left = 0;
9654             stored_player[i].shield_deadly_time_left = 0;
9655           }
9656           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9657           {
9658             stored_player[i].shield_normal_time_left = 999999;
9659           }
9660           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9661           {
9662             stored_player[i].shield_normal_time_left = 999999;
9663             stored_player[i].shield_deadly_time_left = 999999;
9664           }
9665         }
9666       }
9667
9668       break;
9669     }
9670
9671     case CA_SET_PLAYER_GRAVITY:
9672     {
9673       for (i = 0; i < MAX_PLAYERS; i++)
9674       {
9675         if (trigger_player_bits & (1 << i))
9676         {
9677           stored_player[i].gravity =
9678             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9679              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9680              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9681              stored_player[i].gravity);
9682         }
9683       }
9684
9685       break;
9686     }
9687
9688     case CA_SET_PLAYER_ARTWORK:
9689     {
9690       for (i = 0; i < MAX_PLAYERS; i++)
9691       {
9692         if (trigger_player_bits & (1 << i))
9693         {
9694           int artwork_element = action_arg_element;
9695
9696           if (action_arg == CA_ARG_ELEMENT_RESET)
9697             artwork_element =
9698               (level.use_artwork_element[i] ? level.artwork_element[i] :
9699                stored_player[i].element_nr);
9700
9701           if (stored_player[i].artwork_element != artwork_element)
9702             stored_player[i].Frame = 0;
9703
9704           stored_player[i].artwork_element = artwork_element;
9705
9706           SetPlayerWaiting(&stored_player[i], FALSE);
9707
9708           /* set number of special actions for bored and sleeping animation */
9709           stored_player[i].num_special_action_bored =
9710             get_num_special_action(artwork_element,
9711                                    ACTION_BORING_1, ACTION_BORING_LAST);
9712           stored_player[i].num_special_action_sleeping =
9713             get_num_special_action(artwork_element,
9714                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9715         }
9716       }
9717
9718       break;
9719     }
9720
9721     case CA_SET_PLAYER_INVENTORY:
9722     {
9723       for (i = 0; i < MAX_PLAYERS; i++)
9724       {
9725         struct PlayerInfo *player = &stored_player[i];
9726         int j, k;
9727
9728         if (trigger_player_bits & (1 << i))
9729         {
9730           int inventory_element = action_arg_element;
9731
9732           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9733               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9734               action_arg == CA_ARG_ELEMENT_ACTION)
9735           {
9736             int element = inventory_element;
9737             int collect_count = element_info[element].collect_count_initial;
9738
9739             if (!IS_CUSTOM_ELEMENT(element))
9740               collect_count = 1;
9741
9742             if (collect_count == 0)
9743               player->inventory_infinite_element = element;
9744             else
9745               for (k = 0; k < collect_count; k++)
9746                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9747                   player->inventory_element[player->inventory_size++] =
9748                     element;
9749           }
9750           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9751                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9752                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9753           {
9754             if (player->inventory_infinite_element != EL_UNDEFINED &&
9755                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9756                                      action_arg_element_raw))
9757               player->inventory_infinite_element = EL_UNDEFINED;
9758
9759             for (k = 0, j = 0; j < player->inventory_size; j++)
9760             {
9761               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9762                                         action_arg_element_raw))
9763                 player->inventory_element[k++] = player->inventory_element[j];
9764             }
9765
9766             player->inventory_size = k;
9767           }
9768           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9769           {
9770             if (player->inventory_size > 0)
9771             {
9772               for (j = 0; j < player->inventory_size - 1; j++)
9773                 player->inventory_element[j] = player->inventory_element[j + 1];
9774
9775               player->inventory_size--;
9776             }
9777           }
9778           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9779           {
9780             if (player->inventory_size > 0)
9781               player->inventory_size--;
9782           }
9783           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9784           {
9785             player->inventory_infinite_element = EL_UNDEFINED;
9786             player->inventory_size = 0;
9787           }
9788           else if (action_arg == CA_ARG_INVENTORY_RESET)
9789           {
9790             player->inventory_infinite_element = EL_UNDEFINED;
9791             player->inventory_size = 0;
9792
9793             if (level.use_initial_inventory[i])
9794             {
9795               for (j = 0; j < level.initial_inventory_size[i]; j++)
9796               {
9797                 int element = level.initial_inventory_content[i][j];
9798                 int collect_count = element_info[element].collect_count_initial;
9799
9800                 if (!IS_CUSTOM_ELEMENT(element))
9801                   collect_count = 1;
9802
9803                 if (collect_count == 0)
9804                   player->inventory_infinite_element = element;
9805                 else
9806                   for (k = 0; k < collect_count; k++)
9807                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9808                       player->inventory_element[player->inventory_size++] =
9809                         element;
9810               }
9811             }
9812           }
9813         }
9814       }
9815
9816       break;
9817     }
9818
9819     /* ---------- CE actions  ---------------------------------------------- */
9820
9821     case CA_SET_CE_VALUE:
9822     {
9823       int last_ce_value = CustomValue[x][y];
9824
9825       CustomValue[x][y] = action_arg_number_new;
9826
9827       if (CustomValue[x][y] != last_ce_value)
9828       {
9829         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9830         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9831
9832         if (CustomValue[x][y] == 0)
9833         {
9834           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9835           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9836         }
9837       }
9838
9839       break;
9840     }
9841
9842     case CA_SET_CE_SCORE:
9843     {
9844       int last_ce_score = ei->collect_score;
9845
9846       ei->collect_score = action_arg_number_new;
9847
9848       if (ei->collect_score != last_ce_score)
9849       {
9850         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9851         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9852
9853         if (ei->collect_score == 0)
9854         {
9855           int xx, yy;
9856
9857           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9858           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9859
9860           /*
9861             This is a very special case that seems to be a mixture between
9862             CheckElementChange() and CheckTriggeredElementChange(): while
9863             the first one only affects single elements that are triggered
9864             directly, the second one affects multiple elements in the playfield
9865             that are triggered indirectly by another element. This is a third
9866             case: Changing the CE score always affects multiple identical CEs,
9867             so every affected CE must be checked, not only the single CE for
9868             which the CE score was changed in the first place (as every instance
9869             of that CE shares the same CE score, and therefore also can change)!
9870           */
9871           SCAN_PLAYFIELD(xx, yy)
9872           {
9873             if (Feld[xx][yy] == element)
9874               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9875                                  CE_SCORE_GETS_ZERO);
9876           }
9877         }
9878       }
9879
9880       break;
9881     }
9882
9883     case CA_SET_CE_ARTWORK:
9884     {
9885       int artwork_element = action_arg_element;
9886       boolean reset_frame = FALSE;
9887       int xx, yy;
9888
9889       if (action_arg == CA_ARG_ELEMENT_RESET)
9890         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9891                            element);
9892
9893       if (ei->gfx_element != artwork_element)
9894         reset_frame = TRUE;
9895
9896       ei->gfx_element = artwork_element;
9897
9898       SCAN_PLAYFIELD(xx, yy)
9899       {
9900         if (Feld[xx][yy] == element)
9901         {
9902           if (reset_frame)
9903           {
9904             ResetGfxAnimation(xx, yy);
9905             ResetRandomAnimationValue(xx, yy);
9906           }
9907
9908           TEST_DrawLevelField(xx, yy);
9909         }
9910       }
9911
9912       break;
9913     }
9914
9915     /* ---------- engine actions  ------------------------------------------ */
9916
9917     case CA_SET_ENGINE_SCAN_MODE:
9918     {
9919       InitPlayfieldScanMode(action_arg);
9920
9921       break;
9922     }
9923
9924     default:
9925       break;
9926   }
9927 }
9928
9929 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9930 {
9931   int old_element = Feld[x][y];
9932   int new_element = GetElementFromGroupElement(element);
9933   int previous_move_direction = MovDir[x][y];
9934   int last_ce_value = CustomValue[x][y];
9935   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9936   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9937   boolean add_player_onto_element = (new_element_is_player &&
9938                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9939                                      IS_WALKABLE(old_element));
9940
9941   if (!add_player_onto_element)
9942   {
9943     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9944       RemoveMovingField(x, y);
9945     else
9946       RemoveField(x, y);
9947
9948     Feld[x][y] = new_element;
9949
9950     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9951       MovDir[x][y] = previous_move_direction;
9952
9953     if (element_info[new_element].use_last_ce_value)
9954       CustomValue[x][y] = last_ce_value;
9955
9956     InitField_WithBug1(x, y, FALSE);
9957
9958     new_element = Feld[x][y];   /* element may have changed */
9959
9960     ResetGfxAnimation(x, y);
9961     ResetRandomAnimationValue(x, y);
9962
9963     TEST_DrawLevelField(x, y);
9964
9965     if (GFX_CRUMBLED(new_element))
9966       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9967   }
9968
9969   /* check if element under the player changes from accessible to unaccessible
9970      (needed for special case of dropping element which then changes) */
9971   /* (must be checked after creating new element for walkable group elements) */
9972   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9973       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9974   {
9975     Bang(x, y);
9976
9977     return;
9978   }
9979
9980   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9981   if (new_element_is_player)
9982     RelocatePlayer(x, y, new_element);
9983
9984   if (is_change)
9985     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9986
9987   TestIfBadThingTouchesPlayer(x, y);
9988   TestIfPlayerTouchesCustomElement(x, y);
9989   TestIfElementTouchesCustomElement(x, y);
9990 }
9991
9992 static void CreateField(int x, int y, int element)
9993 {
9994   CreateFieldExt(x, y, element, FALSE);
9995 }
9996
9997 static void CreateElementFromChange(int x, int y, int element)
9998 {
9999   element = GET_VALID_RUNTIME_ELEMENT(element);
10000
10001   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10002   {
10003     int old_element = Feld[x][y];
10004
10005     /* prevent changed element from moving in same engine frame
10006        unless both old and new element can either fall or move */
10007     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10008         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10009       Stop[x][y] = TRUE;
10010   }
10011
10012   CreateFieldExt(x, y, element, TRUE);
10013 }
10014
10015 static boolean ChangeElement(int x, int y, int element, int page)
10016 {
10017   struct ElementInfo *ei = &element_info[element];
10018   struct ElementChangeInfo *change = &ei->change_page[page];
10019   int ce_value = CustomValue[x][y];
10020   int ce_score = ei->collect_score;
10021   int target_element;
10022   int old_element = Feld[x][y];
10023
10024   /* always use default change event to prevent running into a loop */
10025   if (ChangeEvent[x][y] == -1)
10026     ChangeEvent[x][y] = CE_DELAY;
10027
10028   if (ChangeEvent[x][y] == CE_DELAY)
10029   {
10030     /* reset actual trigger element, trigger player and action element */
10031     change->actual_trigger_element = EL_EMPTY;
10032     change->actual_trigger_player = EL_EMPTY;
10033     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10034     change->actual_trigger_side = CH_SIDE_NONE;
10035     change->actual_trigger_ce_value = 0;
10036     change->actual_trigger_ce_score = 0;
10037   }
10038
10039   /* do not change elements more than a specified maximum number of changes */
10040   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10041     return FALSE;
10042
10043   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10044
10045   if (change->explode)
10046   {
10047     Bang(x, y);
10048
10049     return TRUE;
10050   }
10051
10052   if (change->use_target_content)
10053   {
10054     boolean complete_replace = TRUE;
10055     boolean can_replace[3][3];
10056     int xx, yy;
10057
10058     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10059     {
10060       boolean is_empty;
10061       boolean is_walkable;
10062       boolean is_diggable;
10063       boolean is_collectible;
10064       boolean is_removable;
10065       boolean is_destructible;
10066       int ex = x + xx - 1;
10067       int ey = y + yy - 1;
10068       int content_element = change->target_content.e[xx][yy];
10069       int e;
10070
10071       can_replace[xx][yy] = TRUE;
10072
10073       if (ex == x && ey == y)   /* do not check changing element itself */
10074         continue;
10075
10076       if (content_element == EL_EMPTY_SPACE)
10077       {
10078         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10079
10080         continue;
10081       }
10082
10083       if (!IN_LEV_FIELD(ex, ey))
10084       {
10085         can_replace[xx][yy] = FALSE;
10086         complete_replace = FALSE;
10087
10088         continue;
10089       }
10090
10091       e = Feld[ex][ey];
10092
10093       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10094         e = MovingOrBlocked2Element(ex, ey);
10095
10096       is_empty = (IS_FREE(ex, ey) ||
10097                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10098
10099       is_walkable     = (is_empty || IS_WALKABLE(e));
10100       is_diggable     = (is_empty || IS_DIGGABLE(e));
10101       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10102       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10103       is_removable    = (is_diggable || is_collectible);
10104
10105       can_replace[xx][yy] =
10106         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10107           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10108           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10109           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10110           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10111           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10112          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10113
10114       if (!can_replace[xx][yy])
10115         complete_replace = FALSE;
10116     }
10117
10118     if (!change->only_if_complete || complete_replace)
10119     {
10120       boolean something_has_changed = FALSE;
10121
10122       if (change->only_if_complete && change->use_random_replace &&
10123           RND(100) < change->random_percentage)
10124         return FALSE;
10125
10126       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10127       {
10128         int ex = x + xx - 1;
10129         int ey = y + yy - 1;
10130         int content_element;
10131
10132         if (can_replace[xx][yy] && (!change->use_random_replace ||
10133                                     RND(100) < change->random_percentage))
10134         {
10135           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10136             RemoveMovingField(ex, ey);
10137
10138           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10139
10140           content_element = change->target_content.e[xx][yy];
10141           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10142                                               ce_value, ce_score);
10143
10144           CreateElementFromChange(ex, ey, target_element);
10145
10146           something_has_changed = TRUE;
10147
10148           /* for symmetry reasons, freeze newly created border elements */
10149           if (ex != x || ey != y)
10150             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10151         }
10152       }
10153
10154       if (something_has_changed)
10155       {
10156         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10157         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10158       }
10159     }
10160   }
10161   else
10162   {
10163     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10164                                         ce_value, ce_score);
10165
10166     if (element == EL_DIAGONAL_GROWING ||
10167         element == EL_DIAGONAL_SHRINKING)
10168     {
10169       target_element = Store[x][y];
10170
10171       Store[x][y] = EL_EMPTY;
10172     }
10173
10174     CreateElementFromChange(x, y, target_element);
10175
10176     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10177     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10178   }
10179
10180   /* this uses direct change before indirect change */
10181   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10182
10183   return TRUE;
10184 }
10185
10186 static void HandleElementChange(int x, int y, int page)
10187 {
10188   int element = MovingOrBlocked2Element(x, y);
10189   struct ElementInfo *ei = &element_info[element];
10190   struct ElementChangeInfo *change = &ei->change_page[page];
10191   boolean handle_action_before_change = FALSE;
10192
10193 #ifdef DEBUG
10194   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10195       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10196   {
10197     printf("\n\n");
10198     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10199            x, y, element, element_info[element].token_name);
10200     printf("HandleElementChange(): This should never happen!\n");
10201     printf("\n\n");
10202   }
10203 #endif
10204
10205   /* this can happen with classic bombs on walkable, changing elements */
10206   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10207   {
10208     return;
10209   }
10210
10211   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10212   {
10213     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10214
10215     if (change->can_change)
10216     {
10217       /* !!! not clear why graphic animation should be reset at all here !!! */
10218       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10219       /* when a custom element is about to change (for example by change delay),
10220          do not reset graphic animation when the custom element is moving */
10221       if (!IS_MOVING(x, y))
10222       {
10223         ResetGfxAnimation(x, y);
10224         ResetRandomAnimationValue(x, y);
10225       }
10226
10227       if (change->pre_change_function)
10228         change->pre_change_function(x, y);
10229     }
10230   }
10231
10232   ChangeDelay[x][y]--;
10233
10234   if (ChangeDelay[x][y] != 0)           /* continue element change */
10235   {
10236     if (change->can_change)
10237     {
10238       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10239
10240       if (IS_ANIMATED(graphic))
10241         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10242
10243       if (change->change_function)
10244         change->change_function(x, y);
10245     }
10246   }
10247   else                                  /* finish element change */
10248   {
10249     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10250     {
10251       page = ChangePage[x][y];
10252       ChangePage[x][y] = -1;
10253
10254       change = &ei->change_page[page];
10255     }
10256
10257     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10258     {
10259       ChangeDelay[x][y] = 1;            /* try change after next move step */
10260       ChangePage[x][y] = page;          /* remember page to use for change */
10261
10262       return;
10263     }
10264
10265     /* special case: set new level random seed before changing element */
10266     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10267       handle_action_before_change = TRUE;
10268
10269     if (change->has_action && handle_action_before_change)
10270       ExecuteCustomElementAction(x, y, element, page);
10271
10272     if (change->can_change)
10273     {
10274       if (ChangeElement(x, y, element, page))
10275       {
10276         if (change->post_change_function)
10277           change->post_change_function(x, y);
10278       }
10279     }
10280
10281     if (change->has_action && !handle_action_before_change)
10282       ExecuteCustomElementAction(x, y, element, page);
10283   }
10284 }
10285
10286 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10287                                               int trigger_element,
10288                                               int trigger_event,
10289                                               int trigger_player,
10290                                               int trigger_side,
10291                                               int trigger_page)
10292 {
10293   boolean change_done_any = FALSE;
10294   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10295   int i;
10296
10297   if (!(trigger_events[trigger_element][trigger_event]))
10298     return FALSE;
10299
10300   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10301
10302   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10303   {
10304     int element = EL_CUSTOM_START + i;
10305     boolean change_done = FALSE;
10306     int p;
10307
10308     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10309         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10310       continue;
10311
10312     for (p = 0; p < element_info[element].num_change_pages; p++)
10313     {
10314       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10315
10316       if (change->can_change_or_has_action &&
10317           change->has_event[trigger_event] &&
10318           change->trigger_side & trigger_side &&
10319           change->trigger_player & trigger_player &&
10320           change->trigger_page & trigger_page_bits &&
10321           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10322       {
10323         change->actual_trigger_element = trigger_element;
10324         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10325         change->actual_trigger_player_bits = trigger_player;
10326         change->actual_trigger_side = trigger_side;
10327         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10328         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10329
10330         if ((change->can_change && !change_done) || change->has_action)
10331         {
10332           int x, y;
10333
10334           SCAN_PLAYFIELD(x, y)
10335           {
10336             if (Feld[x][y] == element)
10337             {
10338               if (change->can_change && !change_done)
10339               {
10340                 /* if element already changed in this frame, not only prevent
10341                    another element change (checked in ChangeElement()), but
10342                    also prevent additional element actions for this element */
10343
10344                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10345                     !level.use_action_after_change_bug)
10346                   continue;
10347
10348                 ChangeDelay[x][y] = 1;
10349                 ChangeEvent[x][y] = trigger_event;
10350
10351                 HandleElementChange(x, y, p);
10352               }
10353               else if (change->has_action)
10354               {
10355                 /* if element already changed in this frame, not only prevent
10356                    another element change (checked in ChangeElement()), but
10357                    also prevent additional element actions for this element */
10358
10359                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10360                     !level.use_action_after_change_bug)
10361                   continue;
10362
10363                 ExecuteCustomElementAction(x, y, element, p);
10364                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10365               }
10366             }
10367           }
10368
10369           if (change->can_change)
10370           {
10371             change_done = TRUE;
10372             change_done_any = TRUE;
10373           }
10374         }
10375       }
10376     }
10377   }
10378
10379   RECURSION_LOOP_DETECTION_END();
10380
10381   return change_done_any;
10382 }
10383
10384 static boolean CheckElementChangeExt(int x, int y,
10385                                      int element,
10386                                      int trigger_element,
10387                                      int trigger_event,
10388                                      int trigger_player,
10389                                      int trigger_side)
10390 {
10391   boolean change_done = FALSE;
10392   int p;
10393
10394   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10395       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10396     return FALSE;
10397
10398   if (Feld[x][y] == EL_BLOCKED)
10399   {
10400     Blocked2Moving(x, y, &x, &y);
10401     element = Feld[x][y];
10402   }
10403
10404   /* check if element has already changed or is about to change after moving */
10405   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10406        Feld[x][y] != element) ||
10407
10408       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10409        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10410         ChangePage[x][y] != -1)))
10411     return FALSE;
10412
10413   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10414
10415   for (p = 0; p < element_info[element].num_change_pages; p++)
10416   {
10417     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10418
10419     /* check trigger element for all events where the element that is checked
10420        for changing interacts with a directly adjacent element -- this is
10421        different to element changes that affect other elements to change on the
10422        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10423     boolean check_trigger_element =
10424       (trigger_event == CE_TOUCHING_X ||
10425        trigger_event == CE_HITTING_X ||
10426        trigger_event == CE_HIT_BY_X ||
10427        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10428
10429     if (change->can_change_or_has_action &&
10430         change->has_event[trigger_event] &&
10431         change->trigger_side & trigger_side &&
10432         change->trigger_player & trigger_player &&
10433         (!check_trigger_element ||
10434          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10435     {
10436       change->actual_trigger_element = trigger_element;
10437       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10438       change->actual_trigger_player_bits = trigger_player;
10439       change->actual_trigger_side = trigger_side;
10440       change->actual_trigger_ce_value = CustomValue[x][y];
10441       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10442
10443       /* special case: trigger element not at (x,y) position for some events */
10444       if (check_trigger_element)
10445       {
10446         static struct
10447         {
10448           int dx, dy;
10449         } move_xy[] =
10450           {
10451             {  0,  0 },
10452             { -1,  0 },
10453             { +1,  0 },
10454             {  0,  0 },
10455             {  0, -1 },
10456             {  0,  0 }, { 0, 0 }, { 0, 0 },
10457             {  0, +1 }
10458           };
10459
10460         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10461         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10462
10463         change->actual_trigger_ce_value = CustomValue[xx][yy];
10464         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10465       }
10466
10467       if (change->can_change && !change_done)
10468       {
10469         ChangeDelay[x][y] = 1;
10470         ChangeEvent[x][y] = trigger_event;
10471
10472         HandleElementChange(x, y, p);
10473
10474         change_done = TRUE;
10475       }
10476       else if (change->has_action)
10477       {
10478         ExecuteCustomElementAction(x, y, element, p);
10479         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10480       }
10481     }
10482   }
10483
10484   RECURSION_LOOP_DETECTION_END();
10485
10486   return change_done;
10487 }
10488
10489 static void PlayPlayerSound(struct PlayerInfo *player)
10490 {
10491   int jx = player->jx, jy = player->jy;
10492   int sound_element = player->artwork_element;
10493   int last_action = player->last_action_waiting;
10494   int action = player->action_waiting;
10495
10496   if (player->is_waiting)
10497   {
10498     if (action != last_action)
10499       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10500     else
10501       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10502   }
10503   else
10504   {
10505     if (action != last_action)
10506       StopSound(element_info[sound_element].sound[last_action]);
10507
10508     if (last_action == ACTION_SLEEPING)
10509       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10510   }
10511 }
10512
10513 static void PlayAllPlayersSound()
10514 {
10515   int i;
10516
10517   for (i = 0; i < MAX_PLAYERS; i++)
10518     if (stored_player[i].active)
10519       PlayPlayerSound(&stored_player[i]);
10520 }
10521
10522 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10523 {
10524   boolean last_waiting = player->is_waiting;
10525   int move_dir = player->MovDir;
10526
10527   player->dir_waiting = move_dir;
10528   player->last_action_waiting = player->action_waiting;
10529
10530   if (is_waiting)
10531   {
10532     if (!last_waiting)          /* not waiting -> waiting */
10533     {
10534       player->is_waiting = TRUE;
10535
10536       player->frame_counter_bored =
10537         FrameCounter +
10538         game.player_boring_delay_fixed +
10539         GetSimpleRandom(game.player_boring_delay_random);
10540       player->frame_counter_sleeping =
10541         FrameCounter +
10542         game.player_sleeping_delay_fixed +
10543         GetSimpleRandom(game.player_sleeping_delay_random);
10544
10545       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10546     }
10547
10548     if (game.player_sleeping_delay_fixed +
10549         game.player_sleeping_delay_random > 0 &&
10550         player->anim_delay_counter == 0 &&
10551         player->post_delay_counter == 0 &&
10552         FrameCounter >= player->frame_counter_sleeping)
10553       player->is_sleeping = TRUE;
10554     else if (game.player_boring_delay_fixed +
10555              game.player_boring_delay_random > 0 &&
10556              FrameCounter >= player->frame_counter_bored)
10557       player->is_bored = TRUE;
10558
10559     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10560                               player->is_bored ? ACTION_BORING :
10561                               ACTION_WAITING);
10562
10563     if (player->is_sleeping && player->use_murphy)
10564     {
10565       /* special case for sleeping Murphy when leaning against non-free tile */
10566
10567       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10568           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10569            !IS_MOVING(player->jx - 1, player->jy)))
10570         move_dir = MV_LEFT;
10571       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10572                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10573                 !IS_MOVING(player->jx + 1, player->jy)))
10574         move_dir = MV_RIGHT;
10575       else
10576         player->is_sleeping = FALSE;
10577
10578       player->dir_waiting = move_dir;
10579     }
10580
10581     if (player->is_sleeping)
10582     {
10583       if (player->num_special_action_sleeping > 0)
10584       {
10585         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10586         {
10587           int last_special_action = player->special_action_sleeping;
10588           int num_special_action = player->num_special_action_sleeping;
10589           int special_action =
10590             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10591              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10592              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10593              last_special_action + 1 : ACTION_SLEEPING);
10594           int special_graphic =
10595             el_act_dir2img(player->artwork_element, special_action, move_dir);
10596
10597           player->anim_delay_counter =
10598             graphic_info[special_graphic].anim_delay_fixed +
10599             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10600           player->post_delay_counter =
10601             graphic_info[special_graphic].post_delay_fixed +
10602             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10603
10604           player->special_action_sleeping = special_action;
10605         }
10606
10607         if (player->anim_delay_counter > 0)
10608         {
10609           player->action_waiting = player->special_action_sleeping;
10610           player->anim_delay_counter--;
10611         }
10612         else if (player->post_delay_counter > 0)
10613         {
10614           player->post_delay_counter--;
10615         }
10616       }
10617     }
10618     else if (player->is_bored)
10619     {
10620       if (player->num_special_action_bored > 0)
10621       {
10622         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10623         {
10624           int special_action =
10625             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10626           int special_graphic =
10627             el_act_dir2img(player->artwork_element, special_action, move_dir);
10628
10629           player->anim_delay_counter =
10630             graphic_info[special_graphic].anim_delay_fixed +
10631             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10632           player->post_delay_counter =
10633             graphic_info[special_graphic].post_delay_fixed +
10634             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10635
10636           player->special_action_bored = special_action;
10637         }
10638
10639         if (player->anim_delay_counter > 0)
10640         {
10641           player->action_waiting = player->special_action_bored;
10642           player->anim_delay_counter--;
10643         }
10644         else if (player->post_delay_counter > 0)
10645         {
10646           player->post_delay_counter--;
10647         }
10648       }
10649     }
10650   }
10651   else if (last_waiting)        /* waiting -> not waiting */
10652   {
10653     player->is_waiting = FALSE;
10654     player->is_bored = FALSE;
10655     player->is_sleeping = FALSE;
10656
10657     player->frame_counter_bored = -1;
10658     player->frame_counter_sleeping = -1;
10659
10660     player->anim_delay_counter = 0;
10661     player->post_delay_counter = 0;
10662
10663     player->dir_waiting = player->MovDir;
10664     player->action_waiting = ACTION_DEFAULT;
10665
10666     player->special_action_bored = ACTION_DEFAULT;
10667     player->special_action_sleeping = ACTION_DEFAULT;
10668   }
10669 }
10670
10671 static void CheckSingleStepMode(struct PlayerInfo *player)
10672 {
10673   if (tape.single_step && tape.recording && !tape.pausing)
10674   {
10675     /* as it is called "single step mode", just return to pause mode when the
10676        player stopped moving after one tile (or never starts moving at all) */
10677     if (!player->is_moving && !player->is_pushing)
10678     {
10679       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10680       SnapField(player, 0, 0);                  /* stop snapping */
10681     }
10682   }
10683 }
10684
10685 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10686 {
10687   int left      = player_action & JOY_LEFT;
10688   int right     = player_action & JOY_RIGHT;
10689   int up        = player_action & JOY_UP;
10690   int down      = player_action & JOY_DOWN;
10691   int button1   = player_action & JOY_BUTTON_1;
10692   int button2   = player_action & JOY_BUTTON_2;
10693   int dx        = (left ? -1 : right ? 1 : 0);
10694   int dy        = (up   ? -1 : down  ? 1 : 0);
10695
10696   if (!player->active || tape.pausing)
10697     return 0;
10698
10699   if (player_action)
10700   {
10701     if (button1)
10702       SnapField(player, dx, dy);
10703     else
10704     {
10705       if (button2)
10706         DropElement(player);
10707
10708       MovePlayer(player, dx, dy);
10709     }
10710
10711     CheckSingleStepMode(player);
10712
10713     SetPlayerWaiting(player, FALSE);
10714
10715     return player_action;
10716   }
10717   else
10718   {
10719     /* no actions for this player (no input at player's configured device) */
10720
10721     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10722     SnapField(player, 0, 0);
10723     CheckGravityMovementWhenNotMoving(player);
10724
10725     if (player->MovPos == 0)
10726       SetPlayerWaiting(player, TRUE);
10727
10728     if (player->MovPos == 0)    /* needed for tape.playing */
10729       player->is_moving = FALSE;
10730
10731     player->is_dropping = FALSE;
10732     player->is_dropping_pressed = FALSE;
10733     player->drop_pressed_delay = 0;
10734
10735     CheckSingleStepMode(player);
10736
10737     return 0;
10738   }
10739 }
10740
10741 static void CheckLevelTime()
10742 {
10743   int i;
10744
10745   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10746   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10747   {
10748     if (level.native_em_level->lev->home == 0)  /* all players at home */
10749     {
10750       PlayerWins(local_player);
10751
10752       AllPlayersGone = TRUE;
10753
10754       level.native_em_level->lev->home = -1;
10755     }
10756
10757     if (level.native_em_level->ply[0]->alive == 0 &&
10758         level.native_em_level->ply[1]->alive == 0 &&
10759         level.native_em_level->ply[2]->alive == 0 &&
10760         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10761       AllPlayersGone = TRUE;
10762   }
10763   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10764   {
10765     if (game_sp.LevelSolved &&
10766         !game_sp.GameOver)                              /* game won */
10767     {
10768       PlayerWins(local_player);
10769
10770       game_sp.GameOver = TRUE;
10771
10772       AllPlayersGone = TRUE;
10773     }
10774
10775     if (game_sp.GameOver)                               /* game lost */
10776       AllPlayersGone = TRUE;
10777   }
10778
10779   if (TimeFrames >= FRAMES_PER_SECOND)
10780   {
10781     TimeFrames = 0;
10782     TapeTime++;
10783
10784     for (i = 0; i < MAX_PLAYERS; i++)
10785     {
10786       struct PlayerInfo *player = &stored_player[i];
10787
10788       if (SHIELD_ON(player))
10789       {
10790         player->shield_normal_time_left--;
10791
10792         if (player->shield_deadly_time_left > 0)
10793           player->shield_deadly_time_left--;
10794       }
10795     }
10796
10797     if (!local_player->LevelSolved && !level.use_step_counter)
10798     {
10799       TimePlayed++;
10800
10801       if (TimeLeft > 0)
10802       {
10803         TimeLeft--;
10804
10805         if (TimeLeft <= 10 && setup.time_limit)
10806           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10807
10808         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10809            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10810
10811         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10812
10813         if (!TimeLeft && setup.time_limit)
10814         {
10815           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10816             level.native_em_level->lev->killed_out_of_time = TRUE;
10817           else
10818             for (i = 0; i < MAX_PLAYERS; i++)
10819               KillPlayer(&stored_player[i]);
10820         }
10821       }
10822       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10823       {
10824         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10825       }
10826
10827       level.native_em_level->lev->time =
10828         (game.no_time_limit ? TimePlayed : TimeLeft);
10829     }
10830
10831     if (tape.recording || tape.playing)
10832       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10833   }
10834
10835   if (tape.recording || tape.playing)
10836     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10837
10838   UpdateAndDisplayGameControlValues();
10839 }
10840
10841 void AdvanceFrameAndPlayerCounters(int player_nr)
10842 {
10843   int i;
10844
10845   /* advance frame counters (global frame counter and time frame counter) */
10846   FrameCounter++;
10847   TimeFrames++;
10848
10849   /* advance player counters (counters for move delay, move animation etc.) */
10850   for (i = 0; i < MAX_PLAYERS; i++)
10851   {
10852     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10853     int move_delay_value = stored_player[i].move_delay_value;
10854     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10855
10856     if (!advance_player_counters)       /* not all players may be affected */
10857       continue;
10858
10859     if (move_frames == 0)       /* less than one move per game frame */
10860     {
10861       int stepsize = TILEX / move_delay_value;
10862       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10863       int count = (stored_player[i].is_moving ?
10864                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10865
10866       if (count % delay == 0)
10867         move_frames = 1;
10868     }
10869
10870     stored_player[i].Frame += move_frames;
10871
10872     if (stored_player[i].MovPos != 0)
10873       stored_player[i].StepFrame += move_frames;
10874
10875     if (stored_player[i].move_delay > 0)
10876       stored_player[i].move_delay--;
10877
10878     /* due to bugs in previous versions, counter must count up, not down */
10879     if (stored_player[i].push_delay != -1)
10880       stored_player[i].push_delay++;
10881
10882     if (stored_player[i].drop_delay > 0)
10883       stored_player[i].drop_delay--;
10884
10885     if (stored_player[i].is_dropping_pressed)
10886       stored_player[i].drop_pressed_delay++;
10887   }
10888 }
10889
10890 void StartGameActions(boolean init_network_game, boolean record_tape,
10891                       int random_seed)
10892 {
10893   unsigned int new_random_seed = InitRND(random_seed);
10894
10895   if (record_tape)
10896     TapeStartRecording(new_random_seed);
10897
10898 #if defined(NETWORK_AVALIABLE)
10899   if (init_network_game)
10900   {
10901     SendToServer_StartPlaying();
10902
10903     return;
10904   }
10905 #endif
10906
10907   InitGame();
10908 }
10909
10910 void GameActions()
10911 {
10912   static unsigned int game_frame_delay = 0;
10913   unsigned int game_frame_delay_value;
10914   byte *recorded_player_action;
10915   byte summarized_player_action = 0;
10916   byte tape_action[MAX_PLAYERS];
10917   int i;
10918
10919   /* detect endless loops, caused by custom element programming */
10920   if (recursion_loop_detected && recursion_loop_depth == 0)
10921   {
10922     char *message = getStringCat3("Internal Error! Element ",
10923                                   EL_NAME(recursion_loop_element),
10924                                   " caused endless loop! Quit the game?");
10925
10926     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10927           EL_NAME(recursion_loop_element));
10928
10929     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10930
10931     recursion_loop_detected = FALSE;    /* if game should be continued */
10932
10933     free(message);
10934
10935     return;
10936   }
10937
10938   if (game.restart_level)
10939     StartGameActions(options.network, setup.autorecord, level.random_seed);
10940
10941   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10942   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10943   {
10944     if (level.native_em_level->lev->home == 0)  /* all players at home */
10945     {
10946       PlayerWins(local_player);
10947
10948       AllPlayersGone = TRUE;
10949
10950       level.native_em_level->lev->home = -1;
10951     }
10952
10953     if (level.native_em_level->ply[0]->alive == 0 &&
10954         level.native_em_level->ply[1]->alive == 0 &&
10955         level.native_em_level->ply[2]->alive == 0 &&
10956         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10957       AllPlayersGone = TRUE;
10958   }
10959   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10960   {
10961     if (game_sp.LevelSolved &&
10962         !game_sp.GameOver)                              /* game won */
10963     {
10964       PlayerWins(local_player);
10965
10966       game_sp.GameOver = TRUE;
10967
10968       AllPlayersGone = TRUE;
10969     }
10970
10971     if (game_sp.GameOver)                               /* game lost */
10972       AllPlayersGone = TRUE;
10973   }
10974
10975   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10976     GameWon();
10977
10978   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10979     TapeStop();
10980
10981   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10982     return;
10983
10984   game_frame_delay_value =
10985     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10986
10987   if (tape.playing && tape.warp_forward && !tape.pausing)
10988     game_frame_delay_value = 0;
10989
10990   /* ---------- main game synchronization point ---------- */
10991
10992   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10993
10994   if (network_playing && !network_player_action_received)
10995   {
10996     /* try to get network player actions in time */
10997
10998 #if defined(NETWORK_AVALIABLE)
10999     /* last chance to get network player actions without main loop delay */
11000     HandleNetworking();
11001 #endif
11002
11003     /* game was quit by network peer */
11004     if (game_status != GAME_MODE_PLAYING)
11005       return;
11006
11007     if (!network_player_action_received)
11008       return;           /* failed to get network player actions in time */
11009
11010     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11011   }
11012
11013   if (tape.pausing)
11014     return;
11015
11016   /* at this point we know that we really continue executing the game */
11017
11018   network_player_action_received = FALSE;
11019
11020   /* when playing tape, read previously recorded player input from tape data */
11021   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11022
11023   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11024   if (tape.pausing)
11025     return;
11026
11027   if (tape.set_centered_player)
11028   {
11029     game.centered_player_nr_next = tape.centered_player_nr_next;
11030     game.set_centered_player = TRUE;
11031   }
11032
11033   for (i = 0; i < MAX_PLAYERS; i++)
11034   {
11035     summarized_player_action |= stored_player[i].action;
11036
11037     if (!network_playing && (game.team_mode || tape.playing))
11038       stored_player[i].effective_action = stored_player[i].action;
11039   }
11040
11041 #if defined(NETWORK_AVALIABLE)
11042   if (network_playing)
11043     SendToServer_MovePlayer(summarized_player_action);
11044 #endif
11045
11046   if (!options.network && !game.team_mode)
11047     local_player->effective_action = summarized_player_action;
11048
11049   if (tape.recording &&
11050       setup.team_mode &&
11051       setup.input_on_focus &&
11052       game.centered_player_nr != -1)
11053   {
11054     for (i = 0; i < MAX_PLAYERS; i++)
11055       stored_player[i].effective_action =
11056         (i == game.centered_player_nr ? summarized_player_action : 0);
11057   }
11058
11059   if (recorded_player_action != NULL)
11060     for (i = 0; i < MAX_PLAYERS; i++)
11061       stored_player[i].effective_action = recorded_player_action[i];
11062
11063   for (i = 0; i < MAX_PLAYERS; i++)
11064   {
11065     tape_action[i] = stored_player[i].effective_action;
11066
11067     /* (this may happen in the RND game engine if a player was not present on
11068        the playfield on level start, but appeared later from a custom element */
11069     if (setup.team_mode &&
11070         tape.recording &&
11071         tape_action[i] &&
11072         !tape.player_participates[i])
11073       tape.player_participates[i] = TRUE;
11074   }
11075
11076   /* only record actions from input devices, but not programmed actions */
11077   if (tape.recording)
11078     TapeRecordAction(tape_action);
11079
11080 #if USE_NEW_PLAYER_ASSIGNMENTS
11081   // !!! also map player actions in single player mode !!!
11082   // if (game.team_mode)
11083   {
11084     byte mapped_action[MAX_PLAYERS];
11085
11086 #if DEBUG_PLAYER_ACTIONS
11087     printf(":::");
11088     for (i = 0; i < MAX_PLAYERS; i++)
11089       printf(" %d, ", stored_player[i].effective_action);
11090 #endif
11091
11092     for (i = 0; i < MAX_PLAYERS; i++)
11093       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11094
11095     for (i = 0; i < MAX_PLAYERS; i++)
11096       stored_player[i].effective_action = mapped_action[i];
11097
11098 #if DEBUG_PLAYER_ACTIONS
11099     printf(" =>");
11100     for (i = 0; i < MAX_PLAYERS; i++)
11101       printf(" %d, ", stored_player[i].effective_action);
11102     printf("\n");
11103 #endif
11104   }
11105 #if DEBUG_PLAYER_ACTIONS
11106   else
11107   {
11108     printf(":::");
11109     for (i = 0; i < MAX_PLAYERS; i++)
11110       printf(" %d, ", stored_player[i].effective_action);
11111     printf("\n");
11112   }
11113 #endif
11114 #endif
11115
11116   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11117   {
11118     GameActions_EM_Main();
11119   }
11120   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11121   {
11122     GameActions_SP_Main();
11123   }
11124   else
11125   {
11126     GameActions_RND();
11127   }
11128 }
11129
11130 void GameActions_EM_Main()
11131 {
11132   byte effective_action[MAX_PLAYERS];
11133   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11134   int i;
11135
11136   for (i = 0; i < MAX_PLAYERS; i++)
11137     effective_action[i] = stored_player[i].effective_action;
11138
11139   GameActions_EM(effective_action, warp_mode);
11140
11141   CheckLevelTime();
11142
11143   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11144 }
11145
11146 void GameActions_SP_Main()
11147 {
11148   byte effective_action[MAX_PLAYERS];
11149   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11150   int i;
11151
11152   for (i = 0; i < MAX_PLAYERS; i++)
11153     effective_action[i] = stored_player[i].effective_action;
11154
11155   GameActions_SP(effective_action, warp_mode);
11156
11157   CheckLevelTime();
11158
11159   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11160 }
11161
11162 void GameActions_RND()
11163 {
11164   int magic_wall_x = 0, magic_wall_y = 0;
11165   int i, x, y, element, graphic;
11166
11167   InitPlayfieldScanModeVars();
11168
11169   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11170   {
11171     SCAN_PLAYFIELD(x, y)
11172     {
11173       ChangeCount[x][y] = 0;
11174       ChangeEvent[x][y] = -1;
11175     }
11176   }
11177
11178   if (game.set_centered_player)
11179   {
11180     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11181
11182     /* switching to "all players" only possible if all players fit to screen */
11183     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11184     {
11185       game.centered_player_nr_next = game.centered_player_nr;
11186       game.set_centered_player = FALSE;
11187     }
11188
11189     /* do not switch focus to non-existing (or non-active) player */
11190     if (game.centered_player_nr_next >= 0 &&
11191         !stored_player[game.centered_player_nr_next].active)
11192     {
11193       game.centered_player_nr_next = game.centered_player_nr;
11194       game.set_centered_player = FALSE;
11195     }
11196   }
11197
11198   if (game.set_centered_player &&
11199       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11200   {
11201     int sx, sy;
11202
11203     if (game.centered_player_nr_next == -1)
11204     {
11205       setScreenCenteredToAllPlayers(&sx, &sy);
11206     }
11207     else
11208     {
11209       sx = stored_player[game.centered_player_nr_next].jx;
11210       sy = stored_player[game.centered_player_nr_next].jy;
11211     }
11212
11213     game.centered_player_nr = game.centered_player_nr_next;
11214     game.set_centered_player = FALSE;
11215
11216     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11217     DrawGameDoorValues();
11218   }
11219
11220   for (i = 0; i < MAX_PLAYERS; i++)
11221   {
11222     int actual_player_action = stored_player[i].effective_action;
11223
11224 #if 1
11225     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11226        - rnd_equinox_tetrachloride 048
11227        - rnd_equinox_tetrachloride_ii 096
11228        - rnd_emanuel_schmieg 002
11229        - doctor_sloan_ww 001, 020
11230     */
11231     if (stored_player[i].MovPos == 0)
11232       CheckGravityMovement(&stored_player[i]);
11233 #endif
11234
11235     /* overwrite programmed action with tape action */
11236     if (stored_player[i].programmed_action)
11237       actual_player_action = stored_player[i].programmed_action;
11238
11239     PlayerActions(&stored_player[i], actual_player_action);
11240
11241     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11242   }
11243
11244   ScrollScreen(NULL, SCROLL_GO_ON);
11245
11246   /* for backwards compatibility, the following code emulates a fixed bug that
11247      occured when pushing elements (causing elements that just made their last
11248      pushing step to already (if possible) make their first falling step in the
11249      same game frame, which is bad); this code is also needed to use the famous
11250      "spring push bug" which is used in older levels and might be wanted to be
11251      used also in newer levels, but in this case the buggy pushing code is only
11252      affecting the "spring" element and no other elements */
11253
11254   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11255   {
11256     for (i = 0; i < MAX_PLAYERS; i++)
11257     {
11258       struct PlayerInfo *player = &stored_player[i];
11259       int x = player->jx;
11260       int y = player->jy;
11261
11262       if (player->active && player->is_pushing && player->is_moving &&
11263           IS_MOVING(x, y) &&
11264           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11265            Feld[x][y] == EL_SPRING))
11266       {
11267         ContinueMoving(x, y);
11268
11269         /* continue moving after pushing (this is actually a bug) */
11270         if (!IS_MOVING(x, y))
11271           Stop[x][y] = FALSE;
11272       }
11273     }
11274   }
11275
11276   SCAN_PLAYFIELD(x, y)
11277   {
11278     ChangeCount[x][y] = 0;
11279     ChangeEvent[x][y] = -1;
11280
11281     /* this must be handled before main playfield loop */
11282     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11283     {
11284       MovDelay[x][y]--;
11285       if (MovDelay[x][y] <= 0)
11286         RemoveField(x, y);
11287     }
11288
11289     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11290     {
11291       MovDelay[x][y]--;
11292       if (MovDelay[x][y] <= 0)
11293       {
11294         RemoveField(x, y);
11295         TEST_DrawLevelField(x, y);
11296
11297         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11298       }
11299     }
11300
11301 #if DEBUG
11302     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11303     {
11304       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11305       printf("GameActions(): This should never happen!\n");
11306
11307       ChangePage[x][y] = -1;
11308     }
11309 #endif
11310
11311     Stop[x][y] = FALSE;
11312     if (WasJustMoving[x][y] > 0)
11313       WasJustMoving[x][y]--;
11314     if (WasJustFalling[x][y] > 0)
11315       WasJustFalling[x][y]--;
11316     if (CheckCollision[x][y] > 0)
11317       CheckCollision[x][y]--;
11318     if (CheckImpact[x][y] > 0)
11319       CheckImpact[x][y]--;
11320
11321     GfxFrame[x][y]++;
11322
11323     /* reset finished pushing action (not done in ContinueMoving() to allow
11324        continuous pushing animation for elements with zero push delay) */
11325     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11326     {
11327       ResetGfxAnimation(x, y);
11328       TEST_DrawLevelField(x, y);
11329     }
11330
11331 #if DEBUG
11332     if (IS_BLOCKED(x, y))
11333     {
11334       int oldx, oldy;
11335
11336       Blocked2Moving(x, y, &oldx, &oldy);
11337       if (!IS_MOVING(oldx, oldy))
11338       {
11339         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11340         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11341         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11342         printf("GameActions(): This should never happen!\n");
11343       }
11344     }
11345 #endif
11346   }
11347
11348   SCAN_PLAYFIELD(x, y)
11349   {
11350     element = Feld[x][y];
11351     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11352
11353     ResetGfxFrame(x, y, TRUE);
11354
11355     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11356         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11357       ResetRandomAnimationValue(x, y);
11358
11359     SetRandomAnimationValue(x, y);
11360
11361     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11362
11363     if (IS_INACTIVE(element))
11364     {
11365       if (IS_ANIMATED(graphic))
11366         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11367
11368       continue;
11369     }
11370
11371     /* this may take place after moving, so 'element' may have changed */
11372     if (IS_CHANGING(x, y) &&
11373         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11374     {
11375       int page = element_info[element].event_page_nr[CE_DELAY];
11376
11377       HandleElementChange(x, y, page);
11378
11379       element = Feld[x][y];
11380       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11381     }
11382
11383     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11384     {
11385       StartMoving(x, y);
11386
11387       element = Feld[x][y];
11388       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11389
11390       if (IS_ANIMATED(graphic) &&
11391           !IS_MOVING(x, y) &&
11392           !Stop[x][y])
11393         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11394
11395       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11396         TEST_DrawTwinkleOnField(x, y);
11397     }
11398     else if ((element == EL_ACID ||
11399               element == EL_EXIT_OPEN ||
11400               element == EL_EM_EXIT_OPEN ||
11401               element == EL_SP_EXIT_OPEN ||
11402               element == EL_STEEL_EXIT_OPEN ||
11403               element == EL_EM_STEEL_EXIT_OPEN ||
11404               element == EL_SP_TERMINAL ||
11405               element == EL_SP_TERMINAL_ACTIVE ||
11406               element == EL_EXTRA_TIME ||
11407               element == EL_SHIELD_NORMAL ||
11408               element == EL_SHIELD_DEADLY) &&
11409              IS_ANIMATED(graphic))
11410       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11411     else if (IS_MOVING(x, y))
11412       ContinueMoving(x, y);
11413     else if (IS_ACTIVE_BOMB(element))
11414       CheckDynamite(x, y);
11415     else if (element == EL_AMOEBA_GROWING)
11416       AmoebeWaechst(x, y);
11417     else if (element == EL_AMOEBA_SHRINKING)
11418       AmoebaDisappearing(x, y);
11419
11420 #if !USE_NEW_AMOEBA_CODE
11421     else if (IS_AMOEBALIVE(element))
11422       AmoebeAbleger(x, y);
11423 #endif
11424
11425     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11426       Life(x, y);
11427     else if (element == EL_EXIT_CLOSED)
11428       CheckExit(x, y);
11429     else if (element == EL_EM_EXIT_CLOSED)
11430       CheckExitEM(x, y);
11431     else if (element == EL_STEEL_EXIT_CLOSED)
11432       CheckExitSteel(x, y);
11433     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11434       CheckExitSteelEM(x, y);
11435     else if (element == EL_SP_EXIT_CLOSED)
11436       CheckExitSP(x, y);
11437     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11438              element == EL_EXPANDABLE_STEELWALL_GROWING)
11439       MauerWaechst(x, y);
11440     else if (element == EL_EXPANDABLE_WALL ||
11441              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11442              element == EL_EXPANDABLE_WALL_VERTICAL ||
11443              element == EL_EXPANDABLE_WALL_ANY ||
11444              element == EL_BD_EXPANDABLE_WALL)
11445       MauerAbleger(x, y);
11446     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11447              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11448              element == EL_EXPANDABLE_STEELWALL_ANY)
11449       MauerAblegerStahl(x, y);
11450     else if (element == EL_FLAMES)
11451       CheckForDragon(x, y);
11452     else if (element == EL_EXPLOSION)
11453       ; /* drawing of correct explosion animation is handled separately */
11454     else if (element == EL_ELEMENT_SNAPPING ||
11455              element == EL_DIAGONAL_SHRINKING ||
11456              element == EL_DIAGONAL_GROWING)
11457     {
11458       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11459
11460       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11461     }
11462     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11463       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11464
11465     if (IS_BELT_ACTIVE(element))
11466       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11467
11468     if (game.magic_wall_active)
11469     {
11470       int jx = local_player->jx, jy = local_player->jy;
11471
11472       /* play the element sound at the position nearest to the player */
11473       if ((element == EL_MAGIC_WALL_FULL ||
11474            element == EL_MAGIC_WALL_ACTIVE ||
11475            element == EL_MAGIC_WALL_EMPTYING ||
11476            element == EL_BD_MAGIC_WALL_FULL ||
11477            element == EL_BD_MAGIC_WALL_ACTIVE ||
11478            element == EL_BD_MAGIC_WALL_EMPTYING ||
11479            element == EL_DC_MAGIC_WALL_FULL ||
11480            element == EL_DC_MAGIC_WALL_ACTIVE ||
11481            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11482           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11483       {
11484         magic_wall_x = x;
11485         magic_wall_y = y;
11486       }
11487     }
11488   }
11489
11490 #if USE_NEW_AMOEBA_CODE
11491   /* new experimental amoeba growth stuff */
11492   if (!(FrameCounter % 8))
11493   {
11494     static unsigned int random = 1684108901;
11495
11496     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11497     {
11498       x = RND(lev_fieldx);
11499       y = RND(lev_fieldy);
11500       element = Feld[x][y];
11501
11502       if (!IS_PLAYER(x,y) &&
11503           (element == EL_EMPTY ||
11504            CAN_GROW_INTO(element) ||
11505            element == EL_QUICKSAND_EMPTY ||
11506            element == EL_QUICKSAND_FAST_EMPTY ||
11507            element == EL_ACID_SPLASH_LEFT ||
11508            element == EL_ACID_SPLASH_RIGHT))
11509       {
11510         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11511             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11512             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11513             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11514           Feld[x][y] = EL_AMOEBA_DROP;
11515       }
11516
11517       random = random * 129 + 1;
11518     }
11519   }
11520 #endif
11521
11522   game.explosions_delayed = FALSE;
11523
11524   SCAN_PLAYFIELD(x, y)
11525   {
11526     element = Feld[x][y];
11527
11528     if (ExplodeField[x][y])
11529       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11530     else if (element == EL_EXPLOSION)
11531       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11532
11533     ExplodeField[x][y] = EX_TYPE_NONE;
11534   }
11535
11536   game.explosions_delayed = TRUE;
11537
11538   if (game.magic_wall_active)
11539   {
11540     if (!(game.magic_wall_time_left % 4))
11541     {
11542       int element = Feld[magic_wall_x][magic_wall_y];
11543
11544       if (element == EL_BD_MAGIC_WALL_FULL ||
11545           element == EL_BD_MAGIC_WALL_ACTIVE ||
11546           element == EL_BD_MAGIC_WALL_EMPTYING)
11547         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11548       else if (element == EL_DC_MAGIC_WALL_FULL ||
11549                element == EL_DC_MAGIC_WALL_ACTIVE ||
11550                element == EL_DC_MAGIC_WALL_EMPTYING)
11551         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11552       else
11553         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11554     }
11555
11556     if (game.magic_wall_time_left > 0)
11557     {
11558       game.magic_wall_time_left--;
11559
11560       if (!game.magic_wall_time_left)
11561       {
11562         SCAN_PLAYFIELD(x, y)
11563         {
11564           element = Feld[x][y];
11565
11566           if (element == EL_MAGIC_WALL_ACTIVE ||
11567               element == EL_MAGIC_WALL_FULL)
11568           {
11569             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11570             TEST_DrawLevelField(x, y);
11571           }
11572           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11573                    element == EL_BD_MAGIC_WALL_FULL)
11574           {
11575             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11576             TEST_DrawLevelField(x, y);
11577           }
11578           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11579                    element == EL_DC_MAGIC_WALL_FULL)
11580           {
11581             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11582             TEST_DrawLevelField(x, y);
11583           }
11584         }
11585
11586         game.magic_wall_active = FALSE;
11587       }
11588     }
11589   }
11590
11591   if (game.light_time_left > 0)
11592   {
11593     game.light_time_left--;
11594
11595     if (game.light_time_left == 0)
11596       RedrawAllLightSwitchesAndInvisibleElements();
11597   }
11598
11599   if (game.timegate_time_left > 0)
11600   {
11601     game.timegate_time_left--;
11602
11603     if (game.timegate_time_left == 0)
11604       CloseAllOpenTimegates();
11605   }
11606
11607   if (game.lenses_time_left > 0)
11608   {
11609     game.lenses_time_left--;
11610
11611     if (game.lenses_time_left == 0)
11612       RedrawAllInvisibleElementsForLenses();
11613   }
11614
11615   if (game.magnify_time_left > 0)
11616   {
11617     game.magnify_time_left--;
11618
11619     if (game.magnify_time_left == 0)
11620       RedrawAllInvisibleElementsForMagnifier();
11621   }
11622
11623   for (i = 0; i < MAX_PLAYERS; i++)
11624   {
11625     struct PlayerInfo *player = &stored_player[i];
11626
11627     if (SHIELD_ON(player))
11628     {
11629       if (player->shield_deadly_time_left)
11630         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11631       else if (player->shield_normal_time_left)
11632         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11633     }
11634   }
11635
11636 #if USE_DELAYED_GFX_REDRAW
11637   SCAN_PLAYFIELD(x, y)
11638   {
11639     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11640     {
11641       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11642          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11643
11644       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11645         DrawLevelField(x, y);
11646
11647       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11648         DrawLevelFieldCrumbled(x, y);
11649
11650       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11651         DrawLevelFieldCrumbledNeighbours(x, y);
11652
11653       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11654         DrawTwinkleOnField(x, y);
11655     }
11656
11657     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11658   }
11659 #endif
11660
11661   CheckLevelTime();
11662
11663   DrawAllPlayers();
11664   PlayAllPlayersSound();
11665
11666   if (options.debug)                    /* calculate frames per second */
11667   {
11668     static unsigned int fps_counter = 0;
11669     static int fps_frames = 0;
11670     unsigned int fps_delay_ms = Counter() - fps_counter;
11671
11672     fps_frames++;
11673
11674     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11675     {
11676       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11677
11678       fps_frames = 0;
11679       fps_counter = Counter();
11680     }
11681
11682     redraw_mask |= REDRAW_FPS;
11683   }
11684
11685   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11686
11687   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11688   {
11689     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11690
11691     local_player->show_envelope = 0;
11692   }
11693
11694   /* use random number generator in every frame to make it less predictable */
11695   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11696     RND(1);
11697 }
11698
11699 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11700 {
11701   int min_x = x, min_y = y, max_x = x, max_y = y;
11702   int i;
11703
11704   for (i = 0; i < MAX_PLAYERS; i++)
11705   {
11706     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11707
11708     if (!stored_player[i].active || &stored_player[i] == player)
11709       continue;
11710
11711     min_x = MIN(min_x, jx);
11712     min_y = MIN(min_y, jy);
11713     max_x = MAX(max_x, jx);
11714     max_y = MAX(max_y, jy);
11715   }
11716
11717   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11718 }
11719
11720 static boolean AllPlayersInVisibleScreen()
11721 {
11722   int i;
11723
11724   for (i = 0; i < MAX_PLAYERS; i++)
11725   {
11726     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11727
11728     if (!stored_player[i].active)
11729       continue;
11730
11731     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11732       return FALSE;
11733   }
11734
11735   return TRUE;
11736 }
11737
11738 void ScrollLevel(int dx, int dy)
11739 {
11740   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11741   int x, y;
11742
11743   BlitBitmap(drawto_field, drawto_field,
11744              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11745              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11746              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11747              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11748              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11749              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11750
11751   if (dx != 0)
11752   {
11753     x = (dx == 1 ? BX1 : BX2);
11754     for (y = BY1; y <= BY2; y++)
11755       DrawScreenField(x, y);
11756   }
11757
11758   if (dy != 0)
11759   {
11760     y = (dy == 1 ? BY1 : BY2);
11761     for (x = BX1; x <= BX2; x++)
11762       DrawScreenField(x, y);
11763   }
11764
11765   redraw_mask |= REDRAW_FIELD;
11766 }
11767
11768 static boolean canFallDown(struct PlayerInfo *player)
11769 {
11770   int jx = player->jx, jy = player->jy;
11771
11772   return (IN_LEV_FIELD(jx, jy + 1) &&
11773           (IS_FREE(jx, jy + 1) ||
11774            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11775           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11776           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11777 }
11778
11779 static boolean canPassField(int x, int y, int move_dir)
11780 {
11781   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11782   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11783   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11784   int nextx = x + dx;
11785   int nexty = y + dy;
11786   int element = Feld[x][y];
11787
11788   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11789           !CAN_MOVE(element) &&
11790           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11791           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11792           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11793 }
11794
11795 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11796 {
11797   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11798   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11799   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11800   int newx = x + dx;
11801   int newy = y + dy;
11802
11803   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11804           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11805           (IS_DIGGABLE(Feld[newx][newy]) ||
11806            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11807            canPassField(newx, newy, move_dir)));
11808 }
11809
11810 static void CheckGravityMovement(struct PlayerInfo *player)
11811 {
11812   if (player->gravity && !player->programmed_action)
11813   {
11814     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11815     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11816     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11817     int jx = player->jx, jy = player->jy;
11818     boolean player_is_moving_to_valid_field =
11819       (!player_is_snapping &&
11820        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11821         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11822     boolean player_can_fall_down = canFallDown(player);
11823
11824     if (player_can_fall_down &&
11825         !player_is_moving_to_valid_field)
11826       player->programmed_action = MV_DOWN;
11827   }
11828 }
11829
11830 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11831 {
11832   return CheckGravityMovement(player);
11833
11834   if (player->gravity && !player->programmed_action)
11835   {
11836     int jx = player->jx, jy = player->jy;
11837     boolean field_under_player_is_free =
11838       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11839     boolean player_is_standing_on_valid_field =
11840       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11841        (IS_WALKABLE(Feld[jx][jy]) &&
11842         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11843
11844     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11845       player->programmed_action = MV_DOWN;
11846   }
11847 }
11848
11849 /*
11850   MovePlayerOneStep()
11851   -----------------------------------------------------------------------------
11852   dx, dy:               direction (non-diagonal) to try to move the player to
11853   real_dx, real_dy:     direction as read from input device (can be diagonal)
11854 */
11855
11856 boolean MovePlayerOneStep(struct PlayerInfo *player,
11857                           int dx, int dy, int real_dx, int real_dy)
11858 {
11859   int jx = player->jx, jy = player->jy;
11860   int new_jx = jx + dx, new_jy = jy + dy;
11861   int can_move;
11862   boolean player_can_move = !player->cannot_move;
11863
11864   if (!player->active || (!dx && !dy))
11865     return MP_NO_ACTION;
11866
11867   player->MovDir = (dx < 0 ? MV_LEFT :
11868                     dx > 0 ? MV_RIGHT :
11869                     dy < 0 ? MV_UP :
11870                     dy > 0 ? MV_DOWN :  MV_NONE);
11871
11872   if (!IN_LEV_FIELD(new_jx, new_jy))
11873     return MP_NO_ACTION;
11874
11875   if (!player_can_move)
11876   {
11877     if (player->MovPos == 0)
11878     {
11879       player->is_moving = FALSE;
11880       player->is_digging = FALSE;
11881       player->is_collecting = FALSE;
11882       player->is_snapping = FALSE;
11883       player->is_pushing = FALSE;
11884     }
11885   }
11886
11887   if (!options.network && game.centered_player_nr == -1 &&
11888       !AllPlayersInSight(player, new_jx, new_jy))
11889     return MP_NO_ACTION;
11890
11891   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11892   if (can_move != MP_MOVING)
11893     return can_move;
11894
11895   /* check if DigField() has caused relocation of the player */
11896   if (player->jx != jx || player->jy != jy)
11897     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11898
11899   StorePlayer[jx][jy] = 0;
11900   player->last_jx = jx;
11901   player->last_jy = jy;
11902   player->jx = new_jx;
11903   player->jy = new_jy;
11904   StorePlayer[new_jx][new_jy] = player->element_nr;
11905
11906   if (player->move_delay_value_next != -1)
11907   {
11908     player->move_delay_value = player->move_delay_value_next;
11909     player->move_delay_value_next = -1;
11910   }
11911
11912   player->MovPos =
11913     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11914
11915   player->step_counter++;
11916
11917   PlayerVisit[jx][jy] = FrameCounter;
11918
11919   player->is_moving = TRUE;
11920
11921 #if 1
11922   /* should better be called in MovePlayer(), but this breaks some tapes */
11923   ScrollPlayer(player, SCROLL_INIT);
11924 #endif
11925
11926   return MP_MOVING;
11927 }
11928
11929 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11930 {
11931   int jx = player->jx, jy = player->jy;
11932   int old_jx = jx, old_jy = jy;
11933   int moved = MP_NO_ACTION;
11934
11935   if (!player->active)
11936     return FALSE;
11937
11938   if (!dx && !dy)
11939   {
11940     if (player->MovPos == 0)
11941     {
11942       player->is_moving = FALSE;
11943       player->is_digging = FALSE;
11944       player->is_collecting = FALSE;
11945       player->is_snapping = FALSE;
11946       player->is_pushing = FALSE;
11947     }
11948
11949     return FALSE;
11950   }
11951
11952   if (player->move_delay > 0)
11953     return FALSE;
11954
11955   player->move_delay = -1;              /* set to "uninitialized" value */
11956
11957   /* store if player is automatically moved to next field */
11958   player->is_auto_moving = (player->programmed_action != MV_NONE);
11959
11960   /* remove the last programmed player action */
11961   player->programmed_action = 0;
11962
11963   if (player->MovPos)
11964   {
11965     /* should only happen if pre-1.2 tape recordings are played */
11966     /* this is only for backward compatibility */
11967
11968     int original_move_delay_value = player->move_delay_value;
11969
11970 #if DEBUG
11971     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
11972            tape.counter);
11973 #endif
11974
11975     /* scroll remaining steps with finest movement resolution */
11976     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11977
11978     while (player->MovPos)
11979     {
11980       ScrollPlayer(player, SCROLL_GO_ON);
11981       ScrollScreen(NULL, SCROLL_GO_ON);
11982
11983       AdvanceFrameAndPlayerCounters(player->index_nr);
11984
11985       DrawAllPlayers();
11986       BackToFront();
11987     }
11988
11989     player->move_delay_value = original_move_delay_value;
11990   }
11991
11992   player->is_active = FALSE;
11993
11994   if (player->last_move_dir & MV_HORIZONTAL)
11995   {
11996     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11997       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11998   }
11999   else
12000   {
12001     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12002       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12003   }
12004
12005   if (!moved && !player->is_active)
12006   {
12007     player->is_moving = FALSE;
12008     player->is_digging = FALSE;
12009     player->is_collecting = FALSE;
12010     player->is_snapping = FALSE;
12011     player->is_pushing = FALSE;
12012   }
12013
12014   jx = player->jx;
12015   jy = player->jy;
12016
12017   if (moved & MP_MOVING && !ScreenMovPos &&
12018       (player->index_nr == game.centered_player_nr ||
12019        game.centered_player_nr == -1))
12020   {
12021     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12022     int offset = game.scroll_delay_value;
12023
12024     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12025     {
12026       /* actual player has left the screen -- scroll in that direction */
12027       if (jx != old_jx)         /* player has moved horizontally */
12028         scroll_x += (jx - old_jx);
12029       else                      /* player has moved vertically */
12030         scroll_y += (jy - old_jy);
12031     }
12032     else
12033     {
12034       if (jx != old_jx)         /* player has moved horizontally */
12035       {
12036         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12037             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12038           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12039
12040         /* don't scroll over playfield boundaries */
12041         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12042           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12043
12044         /* don't scroll more than one field at a time */
12045         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12046
12047         /* don't scroll against the player's moving direction */
12048         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12049             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12050           scroll_x = old_scroll_x;
12051       }
12052       else                      /* player has moved vertically */
12053       {
12054         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12055             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12056           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12057
12058         /* don't scroll over playfield boundaries */
12059         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12060           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12061
12062         /* don't scroll more than one field at a time */
12063         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12064
12065         /* don't scroll against the player's moving direction */
12066         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12067             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12068           scroll_y = old_scroll_y;
12069       }
12070     }
12071
12072     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12073     {
12074       if (!options.network && game.centered_player_nr == -1 &&
12075           !AllPlayersInVisibleScreen())
12076       {
12077         scroll_x = old_scroll_x;
12078         scroll_y = old_scroll_y;
12079       }
12080       else
12081       {
12082         ScrollScreen(player, SCROLL_INIT);
12083         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12084       }
12085     }
12086   }
12087
12088   player->StepFrame = 0;
12089
12090   if (moved & MP_MOVING)
12091   {
12092     if (old_jx != jx && old_jy == jy)
12093       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12094     else if (old_jx == jx && old_jy != jy)
12095       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12096
12097     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12098
12099     player->last_move_dir = player->MovDir;
12100     player->is_moving = TRUE;
12101     player->is_snapping = FALSE;
12102     player->is_switching = FALSE;
12103     player->is_dropping = FALSE;
12104     player->is_dropping_pressed = FALSE;
12105     player->drop_pressed_delay = 0;
12106
12107 #if 0
12108     /* should better be called here than above, but this breaks some tapes */
12109     ScrollPlayer(player, SCROLL_INIT);
12110 #endif
12111   }
12112   else
12113   {
12114     CheckGravityMovementWhenNotMoving(player);
12115
12116     player->is_moving = FALSE;
12117
12118     /* at this point, the player is allowed to move, but cannot move right now
12119        (e.g. because of something blocking the way) -- ensure that the player
12120        is also allowed to move in the next frame (in old versions before 3.1.1,
12121        the player was forced to wait again for eight frames before next try) */
12122
12123     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12124       player->move_delay = 0;   /* allow direct movement in the next frame */
12125   }
12126
12127   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12128     player->move_delay = player->move_delay_value;
12129
12130   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12131   {
12132     TestIfPlayerTouchesBadThing(jx, jy);
12133     TestIfPlayerTouchesCustomElement(jx, jy);
12134   }
12135
12136   if (!player->active)
12137     RemovePlayer(player);
12138
12139   return moved;
12140 }
12141
12142 void ScrollPlayer(struct PlayerInfo *player, int mode)
12143 {
12144   int jx = player->jx, jy = player->jy;
12145   int last_jx = player->last_jx, last_jy = player->last_jy;
12146   int move_stepsize = TILEX / player->move_delay_value;
12147
12148   if (!player->active)
12149     return;
12150
12151   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12152     return;
12153
12154   if (mode == SCROLL_INIT)
12155   {
12156     player->actual_frame_counter = FrameCounter;
12157     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12158
12159     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12160         Feld[last_jx][last_jy] == EL_EMPTY)
12161     {
12162       int last_field_block_delay = 0;   /* start with no blocking at all */
12163       int block_delay_adjustment = player->block_delay_adjustment;
12164
12165       /* if player blocks last field, add delay for exactly one move */
12166       if (player->block_last_field)
12167       {
12168         last_field_block_delay += player->move_delay_value;
12169
12170         /* when blocking enabled, prevent moving up despite gravity */
12171         if (player->gravity && player->MovDir == MV_UP)
12172           block_delay_adjustment = -1;
12173       }
12174
12175       /* add block delay adjustment (also possible when not blocking) */
12176       last_field_block_delay += block_delay_adjustment;
12177
12178       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12179       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12180     }
12181
12182     if (player->MovPos != 0)    /* player has not yet reached destination */
12183       return;
12184   }
12185   else if (!FrameReached(&player->actual_frame_counter, 1))
12186     return;
12187
12188   if (player->MovPos != 0)
12189   {
12190     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12191     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12192
12193     /* before DrawPlayer() to draw correct player graphic for this case */
12194     if (player->MovPos == 0)
12195       CheckGravityMovement(player);
12196   }
12197
12198   if (player->MovPos == 0)      /* player reached destination field */
12199   {
12200     if (player->move_delay_reset_counter > 0)
12201     {
12202       player->move_delay_reset_counter--;
12203
12204       if (player->move_delay_reset_counter == 0)
12205       {
12206         /* continue with normal speed after quickly moving through gate */
12207         HALVE_PLAYER_SPEED(player);
12208
12209         /* be able to make the next move without delay */
12210         player->move_delay = 0;
12211       }
12212     }
12213
12214     player->last_jx = jx;
12215     player->last_jy = jy;
12216
12217     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12218         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12219         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12220         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12221         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12222         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12223         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12224         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12225     {
12226       DrawPlayer(player);       /* needed here only to cleanup last field */
12227       RemovePlayer(player);
12228
12229       if (local_player->friends_still_needed == 0 ||
12230           IS_SP_ELEMENT(Feld[jx][jy]))
12231         PlayerWins(player);
12232     }
12233
12234     /* this breaks one level: "machine", level 000 */
12235     {
12236       int move_direction = player->MovDir;
12237       int enter_side = MV_DIR_OPPOSITE(move_direction);
12238       int leave_side = move_direction;
12239       int old_jx = last_jx;
12240       int old_jy = last_jy;
12241       int old_element = Feld[old_jx][old_jy];
12242       int new_element = Feld[jx][jy];
12243
12244       if (IS_CUSTOM_ELEMENT(old_element))
12245         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12246                                    CE_LEFT_BY_PLAYER,
12247                                    player->index_bit, leave_side);
12248
12249       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12250                                           CE_PLAYER_LEAVES_X,
12251                                           player->index_bit, leave_side);
12252
12253       if (IS_CUSTOM_ELEMENT(new_element))
12254         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12255                                    player->index_bit, enter_side);
12256
12257       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12258                                           CE_PLAYER_ENTERS_X,
12259                                           player->index_bit, enter_side);
12260
12261       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12262                                         CE_MOVE_OF_X, move_direction);
12263     }
12264
12265     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12266     {
12267       TestIfPlayerTouchesBadThing(jx, jy);
12268       TestIfPlayerTouchesCustomElement(jx, jy);
12269
12270       /* needed because pushed element has not yet reached its destination,
12271          so it would trigger a change event at its previous field location */
12272       if (!player->is_pushing)
12273         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12274
12275       if (!player->active)
12276         RemovePlayer(player);
12277     }
12278
12279     if (!local_player->LevelSolved && level.use_step_counter)
12280     {
12281       int i;
12282
12283       TimePlayed++;
12284
12285       if (TimeLeft > 0)
12286       {
12287         TimeLeft--;
12288
12289         if (TimeLeft <= 10 && setup.time_limit)
12290           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12291
12292         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12293
12294         DisplayGameControlValues();
12295
12296         if (!TimeLeft && setup.time_limit)
12297           for (i = 0; i < MAX_PLAYERS; i++)
12298             KillPlayer(&stored_player[i]);
12299       }
12300       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12301       {
12302         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12303
12304         DisplayGameControlValues();
12305       }
12306     }
12307
12308     if (tape.single_step && tape.recording && !tape.pausing &&
12309         !player->programmed_action)
12310       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12311   }
12312 }
12313
12314 void ScrollScreen(struct PlayerInfo *player, int mode)
12315 {
12316   static unsigned int screen_frame_counter = 0;
12317
12318   if (mode == SCROLL_INIT)
12319   {
12320     /* set scrolling step size according to actual player's moving speed */
12321     ScrollStepSize = TILEX / player->move_delay_value;
12322
12323     screen_frame_counter = FrameCounter;
12324     ScreenMovDir = player->MovDir;
12325     ScreenMovPos = player->MovPos;
12326     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12327     return;
12328   }
12329   else if (!FrameReached(&screen_frame_counter, 1))
12330     return;
12331
12332   if (ScreenMovPos)
12333   {
12334     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12335     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12336     redraw_mask |= REDRAW_FIELD;
12337   }
12338   else
12339     ScreenMovDir = MV_NONE;
12340 }
12341
12342 void TestIfPlayerTouchesCustomElement(int x, int y)
12343 {
12344   static int xy[4][2] =
12345   {
12346     { 0, -1 },
12347     { -1, 0 },
12348     { +1, 0 },
12349     { 0, +1 }
12350   };
12351   static int trigger_sides[4][2] =
12352   {
12353     /* center side       border side */
12354     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12355     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12356     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12357     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12358   };
12359   static int touch_dir[4] =
12360   {
12361     MV_LEFT | MV_RIGHT,
12362     MV_UP   | MV_DOWN,
12363     MV_UP   | MV_DOWN,
12364     MV_LEFT | MV_RIGHT
12365   };
12366   int center_element = Feld[x][y];      /* should always be non-moving! */
12367   int i;
12368
12369   for (i = 0; i < NUM_DIRECTIONS; i++)
12370   {
12371     int xx = x + xy[i][0];
12372     int yy = y + xy[i][1];
12373     int center_side = trigger_sides[i][0];
12374     int border_side = trigger_sides[i][1];
12375     int border_element;
12376
12377     if (!IN_LEV_FIELD(xx, yy))
12378       continue;
12379
12380     if (IS_PLAYER(x, y))                /* player found at center element */
12381     {
12382       struct PlayerInfo *player = PLAYERINFO(x, y);
12383
12384       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12385         border_element = Feld[xx][yy];          /* may be moving! */
12386       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12387         border_element = Feld[xx][yy];
12388       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12389         border_element = MovingOrBlocked2Element(xx, yy);
12390       else
12391         continue;               /* center and border element do not touch */
12392
12393       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12394                                  player->index_bit, border_side);
12395       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12396                                           CE_PLAYER_TOUCHES_X,
12397                                           player->index_bit, border_side);
12398
12399       {
12400         /* use player element that is initially defined in the level playfield,
12401            not the player element that corresponds to the runtime player number
12402            (example: a level that contains EL_PLAYER_3 as the only player would
12403            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12404         int player_element = PLAYERINFO(x, y)->initial_element;
12405
12406         CheckElementChangeBySide(xx, yy, border_element, player_element,
12407                                  CE_TOUCHING_X, border_side);
12408       }
12409     }
12410     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12411     {
12412       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12413
12414       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12415       {
12416         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12417           continue;             /* center and border element do not touch */
12418       }
12419
12420       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12421                                  player->index_bit, center_side);
12422       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12423                                           CE_PLAYER_TOUCHES_X,
12424                                           player->index_bit, center_side);
12425
12426       {
12427         /* use player element that is initially defined in the level playfield,
12428            not the player element that corresponds to the runtime player number
12429            (example: a level that contains EL_PLAYER_3 as the only player would
12430            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12431         int player_element = PLAYERINFO(xx, yy)->initial_element;
12432
12433         CheckElementChangeBySide(x, y, center_element, player_element,
12434                                  CE_TOUCHING_X, center_side);
12435       }
12436
12437       break;
12438     }
12439   }
12440 }
12441
12442 void TestIfElementTouchesCustomElement(int x, int y)
12443 {
12444   static int xy[4][2] =
12445   {
12446     { 0, -1 },
12447     { -1, 0 },
12448     { +1, 0 },
12449     { 0, +1 }
12450   };
12451   static int trigger_sides[4][2] =
12452   {
12453     /* center side      border side */
12454     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12455     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12456     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12457     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12458   };
12459   static int touch_dir[4] =
12460   {
12461     MV_LEFT | MV_RIGHT,
12462     MV_UP   | MV_DOWN,
12463     MV_UP   | MV_DOWN,
12464     MV_LEFT | MV_RIGHT
12465   };
12466   boolean change_center_element = FALSE;
12467   int center_element = Feld[x][y];      /* should always be non-moving! */
12468   int border_element_old[NUM_DIRECTIONS];
12469   int i;
12470
12471   for (i = 0; i < NUM_DIRECTIONS; i++)
12472   {
12473     int xx = x + xy[i][0];
12474     int yy = y + xy[i][1];
12475     int border_element;
12476
12477     border_element_old[i] = -1;
12478
12479     if (!IN_LEV_FIELD(xx, yy))
12480       continue;
12481
12482     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12483       border_element = Feld[xx][yy];    /* may be moving! */
12484     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12485       border_element = Feld[xx][yy];
12486     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12487       border_element = MovingOrBlocked2Element(xx, yy);
12488     else
12489       continue;                 /* center and border element do not touch */
12490
12491     border_element_old[i] = border_element;
12492   }
12493
12494   for (i = 0; i < NUM_DIRECTIONS; i++)
12495   {
12496     int xx = x + xy[i][0];
12497     int yy = y + xy[i][1];
12498     int center_side = trigger_sides[i][0];
12499     int border_element = border_element_old[i];
12500
12501     if (border_element == -1)
12502       continue;
12503
12504     /* check for change of border element */
12505     CheckElementChangeBySide(xx, yy, border_element, center_element,
12506                              CE_TOUCHING_X, center_side);
12507
12508     /* (center element cannot be player, so we dont have to check this here) */
12509   }
12510
12511   for (i = 0; i < NUM_DIRECTIONS; i++)
12512   {
12513     int xx = x + xy[i][0];
12514     int yy = y + xy[i][1];
12515     int border_side = trigger_sides[i][1];
12516     int border_element = border_element_old[i];
12517
12518     if (border_element == -1)
12519       continue;
12520
12521     /* check for change of center element (but change it only once) */
12522     if (!change_center_element)
12523       change_center_element =
12524         CheckElementChangeBySide(x, y, center_element, border_element,
12525                                  CE_TOUCHING_X, border_side);
12526
12527     if (IS_PLAYER(xx, yy))
12528     {
12529       /* use player element that is initially defined in the level playfield,
12530          not the player element that corresponds to the runtime player number
12531          (example: a level that contains EL_PLAYER_3 as the only player would
12532          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12533       int player_element = PLAYERINFO(xx, yy)->initial_element;
12534
12535       CheckElementChangeBySide(x, y, center_element, player_element,
12536                                CE_TOUCHING_X, border_side);
12537     }
12538   }
12539 }
12540
12541 void TestIfElementHitsCustomElement(int x, int y, int direction)
12542 {
12543   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12544   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12545   int hitx = x + dx, hity = y + dy;
12546   int hitting_element = Feld[x][y];
12547   int touched_element;
12548
12549   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12550     return;
12551
12552   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12553                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12554
12555   if (IN_LEV_FIELD(hitx, hity))
12556   {
12557     int opposite_direction = MV_DIR_OPPOSITE(direction);
12558     int hitting_side = direction;
12559     int touched_side = opposite_direction;
12560     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12561                           MovDir[hitx][hity] != direction ||
12562                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12563
12564     object_hit = TRUE;
12565
12566     if (object_hit)
12567     {
12568       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12569                                CE_HITTING_X, touched_side);
12570
12571       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12572                                CE_HIT_BY_X, hitting_side);
12573
12574       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12575                                CE_HIT_BY_SOMETHING, opposite_direction);
12576
12577       if (IS_PLAYER(hitx, hity))
12578       {
12579         /* use player element that is initially defined in the level playfield,
12580            not the player element that corresponds to the runtime player number
12581            (example: a level that contains EL_PLAYER_3 as the only player would
12582            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12583         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12584
12585         CheckElementChangeBySide(x, y, hitting_element, player_element,
12586                                  CE_HITTING_X, touched_side);
12587       }
12588     }
12589   }
12590
12591   /* "hitting something" is also true when hitting the playfield border */
12592   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12593                            CE_HITTING_SOMETHING, direction);
12594 }
12595
12596 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12597 {
12598   int i, kill_x = -1, kill_y = -1;
12599
12600   int bad_element = -1;
12601   static int test_xy[4][2] =
12602   {
12603     { 0, -1 },
12604     { -1, 0 },
12605     { +1, 0 },
12606     { 0, +1 }
12607   };
12608   static int test_dir[4] =
12609   {
12610     MV_UP,
12611     MV_LEFT,
12612     MV_RIGHT,
12613     MV_DOWN
12614   };
12615
12616   for (i = 0; i < NUM_DIRECTIONS; i++)
12617   {
12618     int test_x, test_y, test_move_dir, test_element;
12619
12620     test_x = good_x + test_xy[i][0];
12621     test_y = good_y + test_xy[i][1];
12622
12623     if (!IN_LEV_FIELD(test_x, test_y))
12624       continue;
12625
12626     test_move_dir =
12627       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12628
12629     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12630
12631     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12632        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12633     */
12634     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12635         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12636     {
12637       kill_x = test_x;
12638       kill_y = test_y;
12639       bad_element = test_element;
12640
12641       break;
12642     }
12643   }
12644
12645   if (kill_x != -1 || kill_y != -1)
12646   {
12647     if (IS_PLAYER(good_x, good_y))
12648     {
12649       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12650
12651       if (player->shield_deadly_time_left > 0 &&
12652           !IS_INDESTRUCTIBLE(bad_element))
12653         Bang(kill_x, kill_y);
12654       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12655         KillPlayer(player);
12656     }
12657     else
12658       Bang(good_x, good_y);
12659   }
12660 }
12661
12662 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12663 {
12664   int i, kill_x = -1, kill_y = -1;
12665   int bad_element = Feld[bad_x][bad_y];
12666   static int test_xy[4][2] =
12667   {
12668     { 0, -1 },
12669     { -1, 0 },
12670     { +1, 0 },
12671     { 0, +1 }
12672   };
12673   static int touch_dir[4] =
12674   {
12675     MV_LEFT | MV_RIGHT,
12676     MV_UP   | MV_DOWN,
12677     MV_UP   | MV_DOWN,
12678     MV_LEFT | MV_RIGHT
12679   };
12680   static int test_dir[4] =
12681   {
12682     MV_UP,
12683     MV_LEFT,
12684     MV_RIGHT,
12685     MV_DOWN
12686   };
12687
12688   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12689     return;
12690
12691   for (i = 0; i < NUM_DIRECTIONS; i++)
12692   {
12693     int test_x, test_y, test_move_dir, test_element;
12694
12695     test_x = bad_x + test_xy[i][0];
12696     test_y = bad_y + test_xy[i][1];
12697
12698     if (!IN_LEV_FIELD(test_x, test_y))
12699       continue;
12700
12701     test_move_dir =
12702       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12703
12704     test_element = Feld[test_x][test_y];
12705
12706     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12707        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12708     */
12709     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12710         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12711     {
12712       /* good thing is player or penguin that does not move away */
12713       if (IS_PLAYER(test_x, test_y))
12714       {
12715         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12716
12717         if (bad_element == EL_ROBOT && player->is_moving)
12718           continue;     /* robot does not kill player if he is moving */
12719
12720         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12721         {
12722           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12723             continue;           /* center and border element do not touch */
12724         }
12725
12726         kill_x = test_x;
12727         kill_y = test_y;
12728
12729         break;
12730       }
12731       else if (test_element == EL_PENGUIN)
12732       {
12733         kill_x = test_x;
12734         kill_y = test_y;
12735
12736         break;
12737       }
12738     }
12739   }
12740
12741   if (kill_x != -1 || kill_y != -1)
12742   {
12743     if (IS_PLAYER(kill_x, kill_y))
12744     {
12745       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12746
12747       if (player->shield_deadly_time_left > 0 &&
12748           !IS_INDESTRUCTIBLE(bad_element))
12749         Bang(bad_x, bad_y);
12750       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12751         KillPlayer(player);
12752     }
12753     else
12754       Bang(kill_x, kill_y);
12755   }
12756 }
12757
12758 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12759 {
12760   int bad_element = Feld[bad_x][bad_y];
12761   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12762   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12763   int test_x = bad_x + dx, test_y = bad_y + dy;
12764   int test_move_dir, test_element;
12765   int kill_x = -1, kill_y = -1;
12766
12767   if (!IN_LEV_FIELD(test_x, test_y))
12768     return;
12769
12770   test_move_dir =
12771     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12772
12773   test_element = Feld[test_x][test_y];
12774
12775   if (test_move_dir != bad_move_dir)
12776   {
12777     /* good thing can be player or penguin that does not move away */
12778     if (IS_PLAYER(test_x, test_y))
12779     {
12780       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12781
12782       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12783          player as being hit when he is moving towards the bad thing, because
12784          the "get hit by" condition would be lost after the player stops) */
12785       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12786         return;         /* player moves away from bad thing */
12787
12788       kill_x = test_x;
12789       kill_y = test_y;
12790     }
12791     else if (test_element == EL_PENGUIN)
12792     {
12793       kill_x = test_x;
12794       kill_y = test_y;
12795     }
12796   }
12797
12798   if (kill_x != -1 || kill_y != -1)
12799   {
12800     if (IS_PLAYER(kill_x, kill_y))
12801     {
12802       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12803
12804       if (player->shield_deadly_time_left > 0 &&
12805           !IS_INDESTRUCTIBLE(bad_element))
12806         Bang(bad_x, bad_y);
12807       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12808         KillPlayer(player);
12809     }
12810     else
12811       Bang(kill_x, kill_y);
12812   }
12813 }
12814
12815 void TestIfPlayerTouchesBadThing(int x, int y)
12816 {
12817   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12818 }
12819
12820 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12821 {
12822   TestIfGoodThingHitsBadThing(x, y, move_dir);
12823 }
12824
12825 void TestIfBadThingTouchesPlayer(int x, int y)
12826 {
12827   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12828 }
12829
12830 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12831 {
12832   TestIfBadThingHitsGoodThing(x, y, move_dir);
12833 }
12834
12835 void TestIfFriendTouchesBadThing(int x, int y)
12836 {
12837   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12838 }
12839
12840 void TestIfBadThingTouchesFriend(int x, int y)
12841 {
12842   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12843 }
12844
12845 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12846 {
12847   int i, kill_x = bad_x, kill_y = bad_y;
12848   static int xy[4][2] =
12849   {
12850     { 0, -1 },
12851     { -1, 0 },
12852     { +1, 0 },
12853     { 0, +1 }
12854   };
12855
12856   for (i = 0; i < NUM_DIRECTIONS; i++)
12857   {
12858     int x, y, element;
12859
12860     x = bad_x + xy[i][0];
12861     y = bad_y + xy[i][1];
12862     if (!IN_LEV_FIELD(x, y))
12863       continue;
12864
12865     element = Feld[x][y];
12866     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12867         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12868     {
12869       kill_x = x;
12870       kill_y = y;
12871       break;
12872     }
12873   }
12874
12875   if (kill_x != bad_x || kill_y != bad_y)
12876     Bang(bad_x, bad_y);
12877 }
12878
12879 void KillPlayer(struct PlayerInfo *player)
12880 {
12881   int jx = player->jx, jy = player->jy;
12882
12883   if (!player->active)
12884     return;
12885
12886 #if 0
12887   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12888          player->killed, player->active, player->reanimated);
12889 #endif
12890
12891   /* the following code was introduced to prevent an infinite loop when calling
12892      -> Bang()
12893      -> CheckTriggeredElementChangeExt()
12894      -> ExecuteCustomElementAction()
12895      -> KillPlayer()
12896      -> (infinitely repeating the above sequence of function calls)
12897      which occurs when killing the player while having a CE with the setting
12898      "kill player X when explosion of <player X>"; the solution using a new
12899      field "player->killed" was chosen for backwards compatibility, although
12900      clever use of the fields "player->active" etc. would probably also work */
12901 #if 1
12902   if (player->killed)
12903     return;
12904 #endif
12905
12906   player->killed = TRUE;
12907
12908   /* remove accessible field at the player's position */
12909   Feld[jx][jy] = EL_EMPTY;
12910
12911   /* deactivate shield (else Bang()/Explode() would not work right) */
12912   player->shield_normal_time_left = 0;
12913   player->shield_deadly_time_left = 0;
12914
12915 #if 0
12916   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12917          player->killed, player->active, player->reanimated);
12918 #endif
12919
12920   Bang(jx, jy);
12921
12922 #if 0
12923   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12924          player->killed, player->active, player->reanimated);
12925 #endif
12926
12927   if (player->reanimated)       /* killed player may have been reanimated */
12928     player->killed = player->reanimated = FALSE;
12929   else
12930     BuryPlayer(player);
12931 }
12932
12933 static void KillPlayerUnlessEnemyProtected(int x, int y)
12934 {
12935   if (!PLAYER_ENEMY_PROTECTED(x, y))
12936     KillPlayer(PLAYERINFO(x, y));
12937 }
12938
12939 static void KillPlayerUnlessExplosionProtected(int x, int y)
12940 {
12941   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12942     KillPlayer(PLAYERINFO(x, y));
12943 }
12944
12945 void BuryPlayer(struct PlayerInfo *player)
12946 {
12947   int jx = player->jx, jy = player->jy;
12948
12949   if (!player->active)
12950     return;
12951
12952   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12953   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12954
12955   player->GameOver = TRUE;
12956   RemovePlayer(player);
12957 }
12958
12959 void RemovePlayer(struct PlayerInfo *player)
12960 {
12961   int jx = player->jx, jy = player->jy;
12962   int i, found = FALSE;
12963
12964   player->present = FALSE;
12965   player->active = FALSE;
12966
12967   if (!ExplodeField[jx][jy])
12968     StorePlayer[jx][jy] = 0;
12969
12970   if (player->is_moving)
12971     TEST_DrawLevelField(player->last_jx, player->last_jy);
12972
12973   for (i = 0; i < MAX_PLAYERS; i++)
12974     if (stored_player[i].active)
12975       found = TRUE;
12976
12977   if (!found)
12978     AllPlayersGone = TRUE;
12979
12980   ExitX = ZX = jx;
12981   ExitY = ZY = jy;
12982 }
12983
12984 static void setFieldForSnapping(int x, int y, int element, int direction)
12985 {
12986   struct ElementInfo *ei = &element_info[element];
12987   int direction_bit = MV_DIR_TO_BIT(direction);
12988   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12989   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12990                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12991
12992   Feld[x][y] = EL_ELEMENT_SNAPPING;
12993   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12994
12995   ResetGfxAnimation(x, y);
12996
12997   GfxElement[x][y] = element;
12998   GfxAction[x][y] = action;
12999   GfxDir[x][y] = direction;
13000   GfxFrame[x][y] = -1;
13001 }
13002
13003 /*
13004   =============================================================================
13005   checkDiagonalPushing()
13006   -----------------------------------------------------------------------------
13007   check if diagonal input device direction results in pushing of object
13008   (by checking if the alternative direction is walkable, diggable, ...)
13009   =============================================================================
13010 */
13011
13012 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13013                                     int x, int y, int real_dx, int real_dy)
13014 {
13015   int jx, jy, dx, dy, xx, yy;
13016
13017   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13018     return TRUE;
13019
13020   /* diagonal direction: check alternative direction */
13021   jx = player->jx;
13022   jy = player->jy;
13023   dx = x - jx;
13024   dy = y - jy;
13025   xx = jx + (dx == 0 ? real_dx : 0);
13026   yy = jy + (dy == 0 ? real_dy : 0);
13027
13028   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13029 }
13030
13031 /*
13032   =============================================================================
13033   DigField()
13034   -----------------------------------------------------------------------------
13035   x, y:                 field next to player (non-diagonal) to try to dig to
13036   real_dx, real_dy:     direction as read from input device (can be diagonal)
13037   =============================================================================
13038 */
13039
13040 static int DigField(struct PlayerInfo *player,
13041                     int oldx, int oldy, int x, int y,
13042                     int real_dx, int real_dy, int mode)
13043 {
13044   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13045   boolean player_was_pushing = player->is_pushing;
13046   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13047   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13048   int jx = oldx, jy = oldy;
13049   int dx = x - jx, dy = y - jy;
13050   int nextx = x + dx, nexty = y + dy;
13051   int move_direction = (dx == -1 ? MV_LEFT  :
13052                         dx == +1 ? MV_RIGHT :
13053                         dy == -1 ? MV_UP    :
13054                         dy == +1 ? MV_DOWN  : MV_NONE);
13055   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13056   int dig_side = MV_DIR_OPPOSITE(move_direction);
13057   int old_element = Feld[jx][jy];
13058   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13059   int collect_count;
13060
13061   if (is_player)                /* function can also be called by EL_PENGUIN */
13062   {
13063     if (player->MovPos == 0)
13064     {
13065       player->is_digging = FALSE;
13066       player->is_collecting = FALSE;
13067     }
13068
13069     if (player->MovPos == 0)    /* last pushing move finished */
13070       player->is_pushing = FALSE;
13071
13072     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13073     {
13074       player->is_switching = FALSE;
13075       player->push_delay = -1;
13076
13077       return MP_NO_ACTION;
13078     }
13079   }
13080
13081   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13082     old_element = Back[jx][jy];
13083
13084   /* in case of element dropped at player position, check background */
13085   else if (Back[jx][jy] != EL_EMPTY &&
13086            game.engine_version >= VERSION_IDENT(2,2,0,0))
13087     old_element = Back[jx][jy];
13088
13089   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13090     return MP_NO_ACTION;        /* field has no opening in this direction */
13091
13092   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13093     return MP_NO_ACTION;        /* field has no opening in this direction */
13094
13095   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13096   {
13097     SplashAcid(x, y);
13098
13099     Feld[jx][jy] = player->artwork_element;
13100     InitMovingField(jx, jy, MV_DOWN);
13101     Store[jx][jy] = EL_ACID;
13102     ContinueMoving(jx, jy);
13103     BuryPlayer(player);
13104
13105     return MP_DONT_RUN_INTO;
13106   }
13107
13108   if (player_can_move && DONT_RUN_INTO(element))
13109   {
13110     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13111
13112     return MP_DONT_RUN_INTO;
13113   }
13114
13115   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13116     return MP_NO_ACTION;
13117
13118   collect_count = element_info[element].collect_count_initial;
13119
13120   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13121     return MP_NO_ACTION;
13122
13123   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13124     player_can_move = player_can_move_or_snap;
13125
13126   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13127       game.engine_version >= VERSION_IDENT(2,2,0,0))
13128   {
13129     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13130                                player->index_bit, dig_side);
13131     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13132                                         player->index_bit, dig_side);
13133
13134     if (element == EL_DC_LANDMINE)
13135       Bang(x, y);
13136
13137     if (Feld[x][y] != element)          /* field changed by snapping */
13138       return MP_ACTION;
13139
13140     return MP_NO_ACTION;
13141   }
13142
13143   if (player->gravity && is_player && !player->is_auto_moving &&
13144       canFallDown(player) && move_direction != MV_DOWN &&
13145       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13146     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13147
13148   if (player_can_move &&
13149       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13150   {
13151     int sound_element = SND_ELEMENT(element);
13152     int sound_action = ACTION_WALKING;
13153
13154     if (IS_RND_GATE(element))
13155     {
13156       if (!player->key[RND_GATE_NR(element)])
13157         return MP_NO_ACTION;
13158     }
13159     else if (IS_RND_GATE_GRAY(element))
13160     {
13161       if (!player->key[RND_GATE_GRAY_NR(element)])
13162         return MP_NO_ACTION;
13163     }
13164     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13165     {
13166       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13167         return MP_NO_ACTION;
13168     }
13169     else if (element == EL_EXIT_OPEN ||
13170              element == EL_EM_EXIT_OPEN ||
13171              element == EL_EM_EXIT_OPENING ||
13172              element == EL_STEEL_EXIT_OPEN ||
13173              element == EL_EM_STEEL_EXIT_OPEN ||
13174              element == EL_EM_STEEL_EXIT_OPENING ||
13175              element == EL_SP_EXIT_OPEN ||
13176              element == EL_SP_EXIT_OPENING)
13177     {
13178       sound_action = ACTION_PASSING;    /* player is passing exit */
13179     }
13180     else if (element == EL_EMPTY)
13181     {
13182       sound_action = ACTION_MOVING;             /* nothing to walk on */
13183     }
13184
13185     /* play sound from background or player, whatever is available */
13186     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13187       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13188     else
13189       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13190   }
13191   else if (player_can_move &&
13192            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13193   {
13194     if (!ACCESS_FROM(element, opposite_direction))
13195       return MP_NO_ACTION;      /* field not accessible from this direction */
13196
13197     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13198       return MP_NO_ACTION;
13199
13200     if (IS_EM_GATE(element))
13201     {
13202       if (!player->key[EM_GATE_NR(element)])
13203         return MP_NO_ACTION;
13204     }
13205     else if (IS_EM_GATE_GRAY(element))
13206     {
13207       if (!player->key[EM_GATE_GRAY_NR(element)])
13208         return MP_NO_ACTION;
13209     }
13210     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13211     {
13212       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13213         return MP_NO_ACTION;
13214     }
13215     else if (IS_EMC_GATE(element))
13216     {
13217       if (!player->key[EMC_GATE_NR(element)])
13218         return MP_NO_ACTION;
13219     }
13220     else if (IS_EMC_GATE_GRAY(element))
13221     {
13222       if (!player->key[EMC_GATE_GRAY_NR(element)])
13223         return MP_NO_ACTION;
13224     }
13225     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13226     {
13227       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13228         return MP_NO_ACTION;
13229     }
13230     else if (element == EL_DC_GATE_WHITE ||
13231              element == EL_DC_GATE_WHITE_GRAY ||
13232              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13233     {
13234       if (player->num_white_keys == 0)
13235         return MP_NO_ACTION;
13236
13237       player->num_white_keys--;
13238     }
13239     else if (IS_SP_PORT(element))
13240     {
13241       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13242           element == EL_SP_GRAVITY_PORT_RIGHT ||
13243           element == EL_SP_GRAVITY_PORT_UP ||
13244           element == EL_SP_GRAVITY_PORT_DOWN)
13245         player->gravity = !player->gravity;
13246       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13247                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13248                element == EL_SP_GRAVITY_ON_PORT_UP ||
13249                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13250         player->gravity = TRUE;
13251       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13252                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13253                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13254                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13255         player->gravity = FALSE;
13256     }
13257
13258     /* automatically move to the next field with double speed */
13259     player->programmed_action = move_direction;
13260
13261     if (player->move_delay_reset_counter == 0)
13262     {
13263       player->move_delay_reset_counter = 2;     /* two double speed steps */
13264
13265       DOUBLE_PLAYER_SPEED(player);
13266     }
13267
13268     PlayLevelSoundAction(x, y, ACTION_PASSING);
13269   }
13270   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13271   {
13272     RemoveField(x, y);
13273
13274     if (mode != DF_SNAP)
13275     {
13276       GfxElement[x][y] = GFX_ELEMENT(element);
13277       player->is_digging = TRUE;
13278     }
13279
13280     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13281
13282     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13283                                         player->index_bit, dig_side);
13284
13285     if (mode == DF_SNAP)
13286     {
13287       if (level.block_snap_field)
13288         setFieldForSnapping(x, y, element, move_direction);
13289       else
13290         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13291
13292       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13293                                           player->index_bit, dig_side);
13294     }
13295   }
13296   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13297   {
13298     RemoveField(x, y);
13299
13300     if (is_player && mode != DF_SNAP)
13301     {
13302       GfxElement[x][y] = element;
13303       player->is_collecting = TRUE;
13304     }
13305
13306     if (element == EL_SPEED_PILL)
13307     {
13308       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13309     }
13310     else if (element == EL_EXTRA_TIME && level.time > 0)
13311     {
13312       TimeLeft += level.extra_time;
13313
13314       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13315
13316       DisplayGameControlValues();
13317     }
13318     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13319     {
13320       player->shield_normal_time_left += level.shield_normal_time;
13321       if (element == EL_SHIELD_DEADLY)
13322         player->shield_deadly_time_left += level.shield_deadly_time;
13323     }
13324     else if (element == EL_DYNAMITE ||
13325              element == EL_EM_DYNAMITE ||
13326              element == EL_SP_DISK_RED)
13327     {
13328       if (player->inventory_size < MAX_INVENTORY_SIZE)
13329         player->inventory_element[player->inventory_size++] = element;
13330
13331       DrawGameDoorValues();
13332     }
13333     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13334     {
13335       player->dynabomb_count++;
13336       player->dynabombs_left++;
13337     }
13338     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13339     {
13340       player->dynabomb_size++;
13341     }
13342     else if (element == EL_DYNABOMB_INCREASE_POWER)
13343     {
13344       player->dynabomb_xl = TRUE;
13345     }
13346     else if (IS_KEY(element))
13347     {
13348       player->key[KEY_NR(element)] = TRUE;
13349
13350       DrawGameDoorValues();
13351     }
13352     else if (element == EL_DC_KEY_WHITE)
13353     {
13354       player->num_white_keys++;
13355
13356       /* display white keys? */
13357       /* DrawGameDoorValues(); */
13358     }
13359     else if (IS_ENVELOPE(element))
13360     {
13361       player->show_envelope = element;
13362     }
13363     else if (element == EL_EMC_LENSES)
13364     {
13365       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13366
13367       RedrawAllInvisibleElementsForLenses();
13368     }
13369     else if (element == EL_EMC_MAGNIFIER)
13370     {
13371       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13372
13373       RedrawAllInvisibleElementsForMagnifier();
13374     }
13375     else if (IS_DROPPABLE(element) ||
13376              IS_THROWABLE(element))     /* can be collected and dropped */
13377     {
13378       int i;
13379
13380       if (collect_count == 0)
13381         player->inventory_infinite_element = element;
13382       else
13383         for (i = 0; i < collect_count; i++)
13384           if (player->inventory_size < MAX_INVENTORY_SIZE)
13385             player->inventory_element[player->inventory_size++] = element;
13386
13387       DrawGameDoorValues();
13388     }
13389     else if (collect_count > 0)
13390     {
13391       local_player->gems_still_needed -= collect_count;
13392       if (local_player->gems_still_needed < 0)
13393         local_player->gems_still_needed = 0;
13394
13395       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13396
13397       DisplayGameControlValues();
13398     }
13399
13400     RaiseScoreElement(element);
13401     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13402
13403     if (is_player)
13404       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13405                                           player->index_bit, dig_side);
13406
13407     if (mode == DF_SNAP)
13408     {
13409       if (level.block_snap_field)
13410         setFieldForSnapping(x, y, element, move_direction);
13411       else
13412         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13413
13414       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13415                                           player->index_bit, dig_side);
13416     }
13417   }
13418   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13419   {
13420     if (mode == DF_SNAP && element != EL_BD_ROCK)
13421       return MP_NO_ACTION;
13422
13423     if (CAN_FALL(element) && dy)
13424       return MP_NO_ACTION;
13425
13426     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13427         !(element == EL_SPRING && level.use_spring_bug))
13428       return MP_NO_ACTION;
13429
13430     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13431         ((move_direction & MV_VERTICAL &&
13432           ((element_info[element].move_pattern & MV_LEFT &&
13433             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13434            (element_info[element].move_pattern & MV_RIGHT &&
13435             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13436          (move_direction & MV_HORIZONTAL &&
13437           ((element_info[element].move_pattern & MV_UP &&
13438             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13439            (element_info[element].move_pattern & MV_DOWN &&
13440             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13441       return MP_NO_ACTION;
13442
13443     /* do not push elements already moving away faster than player */
13444     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13445         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13446       return MP_NO_ACTION;
13447
13448     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13449     {
13450       if (player->push_delay_value == -1 || !player_was_pushing)
13451         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13452     }
13453     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13454     {
13455       if (player->push_delay_value == -1)
13456         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13457     }
13458     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13459     {
13460       if (!player->is_pushing)
13461         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13462     }
13463
13464     player->is_pushing = TRUE;
13465     player->is_active = TRUE;
13466
13467     if (!(IN_LEV_FIELD(nextx, nexty) &&
13468           (IS_FREE(nextx, nexty) ||
13469            (IS_SB_ELEMENT(element) &&
13470             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13471            (IS_CUSTOM_ELEMENT(element) &&
13472             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13473       return MP_NO_ACTION;
13474
13475     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13476       return MP_NO_ACTION;
13477
13478     if (player->push_delay == -1)       /* new pushing; restart delay */
13479       player->push_delay = 0;
13480
13481     if (player->push_delay < player->push_delay_value &&
13482         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13483         element != EL_SPRING && element != EL_BALLOON)
13484     {
13485       /* make sure that there is no move delay before next try to push */
13486       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13487         player->move_delay = 0;
13488
13489       return MP_NO_ACTION;
13490     }
13491
13492     if (IS_CUSTOM_ELEMENT(element) &&
13493         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13494     {
13495       if (!DigFieldByCE(nextx, nexty, element))
13496         return MP_NO_ACTION;
13497     }
13498
13499     if (IS_SB_ELEMENT(element))
13500     {
13501       if (element == EL_SOKOBAN_FIELD_FULL)
13502       {
13503         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13504         local_player->sokobanfields_still_needed++;
13505       }
13506
13507       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13508       {
13509         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13510         local_player->sokobanfields_still_needed--;
13511       }
13512
13513       Feld[x][y] = EL_SOKOBAN_OBJECT;
13514
13515       if (Back[x][y] == Back[nextx][nexty])
13516         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13517       else if (Back[x][y] != 0)
13518         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13519                                     ACTION_EMPTYING);
13520       else
13521         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13522                                     ACTION_FILLING);
13523
13524       if (local_player->sokobanfields_still_needed == 0 &&
13525           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13526       {
13527         PlayerWins(player);
13528
13529         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13530       }
13531     }
13532     else
13533       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13534
13535     InitMovingField(x, y, move_direction);
13536     GfxAction[x][y] = ACTION_PUSHING;
13537
13538     if (mode == DF_SNAP)
13539       ContinueMoving(x, y);
13540     else
13541       MovPos[x][y] = (dx != 0 ? dx : dy);
13542
13543     Pushed[x][y] = TRUE;
13544     Pushed[nextx][nexty] = TRUE;
13545
13546     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13547       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13548     else
13549       player->push_delay_value = -1;    /* get new value later */
13550
13551     /* check for element change _after_ element has been pushed */
13552     if (game.use_change_when_pushing_bug)
13553     {
13554       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13555                                  player->index_bit, dig_side);
13556       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13557                                           player->index_bit, dig_side);
13558     }
13559   }
13560   else if (IS_SWITCHABLE(element))
13561   {
13562     if (PLAYER_SWITCHING(player, x, y))
13563     {
13564       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13565                                           player->index_bit, dig_side);
13566
13567       return MP_ACTION;
13568     }
13569
13570     player->is_switching = TRUE;
13571     player->switch_x = x;
13572     player->switch_y = y;
13573
13574     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13575
13576     if (element == EL_ROBOT_WHEEL)
13577     {
13578       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13579       ZX = x;
13580       ZY = y;
13581
13582       game.robot_wheel_active = TRUE;
13583
13584       TEST_DrawLevelField(x, y);
13585     }
13586     else if (element == EL_SP_TERMINAL)
13587     {
13588       int xx, yy;
13589
13590       SCAN_PLAYFIELD(xx, yy)
13591       {
13592         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13593           Bang(xx, yy);
13594         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13595           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13596       }
13597     }
13598     else if (IS_BELT_SWITCH(element))
13599     {
13600       ToggleBeltSwitch(x, y);
13601     }
13602     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13603              element == EL_SWITCHGATE_SWITCH_DOWN ||
13604              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13605              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13606     {
13607       ToggleSwitchgateSwitch(x, y);
13608     }
13609     else if (element == EL_LIGHT_SWITCH ||
13610              element == EL_LIGHT_SWITCH_ACTIVE)
13611     {
13612       ToggleLightSwitch(x, y);
13613     }
13614     else if (element == EL_TIMEGATE_SWITCH ||
13615              element == EL_DC_TIMEGATE_SWITCH)
13616     {
13617       ActivateTimegateSwitch(x, y);
13618     }
13619     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13620              element == EL_BALLOON_SWITCH_RIGHT ||
13621              element == EL_BALLOON_SWITCH_UP    ||
13622              element == EL_BALLOON_SWITCH_DOWN  ||
13623              element == EL_BALLOON_SWITCH_NONE  ||
13624              element == EL_BALLOON_SWITCH_ANY)
13625     {
13626       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13627                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13628                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13629                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13630                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13631                              move_direction);
13632     }
13633     else if (element == EL_LAMP)
13634     {
13635       Feld[x][y] = EL_LAMP_ACTIVE;
13636       local_player->lights_still_needed--;
13637
13638       ResetGfxAnimation(x, y);
13639       TEST_DrawLevelField(x, y);
13640     }
13641     else if (element == EL_TIME_ORB_FULL)
13642     {
13643       Feld[x][y] = EL_TIME_ORB_EMPTY;
13644
13645       if (level.time > 0 || level.use_time_orb_bug)
13646       {
13647         TimeLeft += level.time_orb_time;
13648         game.no_time_limit = FALSE;
13649
13650         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13651
13652         DisplayGameControlValues();
13653       }
13654
13655       ResetGfxAnimation(x, y);
13656       TEST_DrawLevelField(x, y);
13657     }
13658     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13659              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13660     {
13661       int xx, yy;
13662
13663       game.ball_state = !game.ball_state;
13664
13665       SCAN_PLAYFIELD(xx, yy)
13666       {
13667         int e = Feld[xx][yy];
13668
13669         if (game.ball_state)
13670         {
13671           if (e == EL_EMC_MAGIC_BALL)
13672             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13673           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13674             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13675         }
13676         else
13677         {
13678           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13679             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13680           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13681             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13682         }
13683       }
13684     }
13685
13686     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13687                                         player->index_bit, dig_side);
13688
13689     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13690                                         player->index_bit, dig_side);
13691
13692     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13693                                         player->index_bit, dig_side);
13694
13695     return MP_ACTION;
13696   }
13697   else
13698   {
13699     if (!PLAYER_SWITCHING(player, x, y))
13700     {
13701       player->is_switching = TRUE;
13702       player->switch_x = x;
13703       player->switch_y = y;
13704
13705       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13706                                  player->index_bit, dig_side);
13707       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13708                                           player->index_bit, dig_side);
13709
13710       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13711                                  player->index_bit, dig_side);
13712       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13713                                           player->index_bit, dig_side);
13714     }
13715
13716     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13717                                player->index_bit, dig_side);
13718     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13719                                         player->index_bit, dig_side);
13720
13721     return MP_NO_ACTION;
13722   }
13723
13724   player->push_delay = -1;
13725
13726   if (is_player)                /* function can also be called by EL_PENGUIN */
13727   {
13728     if (Feld[x][y] != element)          /* really digged/collected something */
13729     {
13730       player->is_collecting = !player->is_digging;
13731       player->is_active = TRUE;
13732     }
13733   }
13734
13735   return MP_MOVING;
13736 }
13737
13738 static boolean DigFieldByCE(int x, int y, int digging_element)
13739 {
13740   int element = Feld[x][y];
13741
13742   if (!IS_FREE(x, y))
13743   {
13744     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13745                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13746                   ACTION_BREAKING);
13747
13748     /* no element can dig solid indestructible elements */
13749     if (IS_INDESTRUCTIBLE(element) &&
13750         !IS_DIGGABLE(element) &&
13751         !IS_COLLECTIBLE(element))
13752       return FALSE;
13753
13754     if (AmoebaNr[x][y] &&
13755         (element == EL_AMOEBA_FULL ||
13756          element == EL_BD_AMOEBA ||
13757          element == EL_AMOEBA_GROWING))
13758     {
13759       AmoebaCnt[AmoebaNr[x][y]]--;
13760       AmoebaCnt2[AmoebaNr[x][y]]--;
13761     }
13762
13763     if (IS_MOVING(x, y))
13764       RemoveMovingField(x, y);
13765     else
13766     {
13767       RemoveField(x, y);
13768       TEST_DrawLevelField(x, y);
13769     }
13770
13771     /* if digged element was about to explode, prevent the explosion */
13772     ExplodeField[x][y] = EX_TYPE_NONE;
13773
13774     PlayLevelSoundAction(x, y, action);
13775   }
13776
13777   Store[x][y] = EL_EMPTY;
13778
13779   /* this makes it possible to leave the removed element again */
13780   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13781     Store[x][y] = element;
13782
13783   return TRUE;
13784 }
13785
13786 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13787 {
13788   int jx = player->jx, jy = player->jy;
13789   int x = jx + dx, y = jy + dy;
13790   int snap_direction = (dx == -1 ? MV_LEFT  :
13791                         dx == +1 ? MV_RIGHT :
13792                         dy == -1 ? MV_UP    :
13793                         dy == +1 ? MV_DOWN  : MV_NONE);
13794   boolean can_continue_snapping = (level.continuous_snapping &&
13795                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13796
13797   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13798     return FALSE;
13799
13800   if (!player->active || !IN_LEV_FIELD(x, y))
13801     return FALSE;
13802
13803   if (dx && dy)
13804     return FALSE;
13805
13806   if (!dx && !dy)
13807   {
13808     if (player->MovPos == 0)
13809       player->is_pushing = FALSE;
13810
13811     player->is_snapping = FALSE;
13812
13813     if (player->MovPos == 0)
13814     {
13815       player->is_moving = FALSE;
13816       player->is_digging = FALSE;
13817       player->is_collecting = FALSE;
13818     }
13819
13820     return FALSE;
13821   }
13822
13823   /* prevent snapping with already pressed snap key when not allowed */
13824   if (player->is_snapping && !can_continue_snapping)
13825     return FALSE;
13826
13827   player->MovDir = snap_direction;
13828
13829   if (player->MovPos == 0)
13830   {
13831     player->is_moving = FALSE;
13832     player->is_digging = FALSE;
13833     player->is_collecting = FALSE;
13834   }
13835
13836   player->is_dropping = FALSE;
13837   player->is_dropping_pressed = FALSE;
13838   player->drop_pressed_delay = 0;
13839
13840   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13841     return FALSE;
13842
13843   player->is_snapping = TRUE;
13844   player->is_active = TRUE;
13845
13846   if (player->MovPos == 0)
13847   {
13848     player->is_moving = FALSE;
13849     player->is_digging = FALSE;
13850     player->is_collecting = FALSE;
13851   }
13852
13853   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13854     TEST_DrawLevelField(player->last_jx, player->last_jy);
13855
13856   TEST_DrawLevelField(x, y);
13857
13858   return TRUE;
13859 }
13860
13861 static boolean DropElement(struct PlayerInfo *player)
13862 {
13863   int old_element, new_element;
13864   int dropx = player->jx, dropy = player->jy;
13865   int drop_direction = player->MovDir;
13866   int drop_side = drop_direction;
13867   int drop_element = get_next_dropped_element(player);
13868
13869   player->is_dropping_pressed = TRUE;
13870
13871   /* do not drop an element on top of another element; when holding drop key
13872      pressed without moving, dropped element must move away before the next
13873      element can be dropped (this is especially important if the next element
13874      is dynamite, which can be placed on background for historical reasons) */
13875   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13876     return MP_ACTION;
13877
13878   if (IS_THROWABLE(drop_element))
13879   {
13880     dropx += GET_DX_FROM_DIR(drop_direction);
13881     dropy += GET_DY_FROM_DIR(drop_direction);
13882
13883     if (!IN_LEV_FIELD(dropx, dropy))
13884       return FALSE;
13885   }
13886
13887   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13888   new_element = drop_element;           /* default: no change when dropping */
13889
13890   /* check if player is active, not moving and ready to drop */
13891   if (!player->active || player->MovPos || player->drop_delay > 0)
13892     return FALSE;
13893
13894   /* check if player has anything that can be dropped */
13895   if (new_element == EL_UNDEFINED)
13896     return FALSE;
13897
13898   /* check if drop key was pressed long enough for EM style dynamite */
13899   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13900     return FALSE;
13901
13902   /* check if anything can be dropped at the current position */
13903   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13904     return FALSE;
13905
13906   /* collected custom elements can only be dropped on empty fields */
13907   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13908     return FALSE;
13909
13910   if (old_element != EL_EMPTY)
13911     Back[dropx][dropy] = old_element;   /* store old element on this field */
13912
13913   ResetGfxAnimation(dropx, dropy);
13914   ResetRandomAnimationValue(dropx, dropy);
13915
13916   if (player->inventory_size > 0 ||
13917       player->inventory_infinite_element != EL_UNDEFINED)
13918   {
13919     if (player->inventory_size > 0)
13920     {
13921       player->inventory_size--;
13922
13923       DrawGameDoorValues();
13924
13925       if (new_element == EL_DYNAMITE)
13926         new_element = EL_DYNAMITE_ACTIVE;
13927       else if (new_element == EL_EM_DYNAMITE)
13928         new_element = EL_EM_DYNAMITE_ACTIVE;
13929       else if (new_element == EL_SP_DISK_RED)
13930         new_element = EL_SP_DISK_RED_ACTIVE;
13931     }
13932
13933     Feld[dropx][dropy] = new_element;
13934
13935     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13936       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13937                           el2img(Feld[dropx][dropy]), 0);
13938
13939     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13940
13941     /* needed if previous element just changed to "empty" in the last frame */
13942     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13943
13944     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13945                                player->index_bit, drop_side);
13946     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13947                                         CE_PLAYER_DROPS_X,
13948                                         player->index_bit, drop_side);
13949
13950     TestIfElementTouchesCustomElement(dropx, dropy);
13951   }
13952   else          /* player is dropping a dyna bomb */
13953   {
13954     player->dynabombs_left--;
13955
13956     Feld[dropx][dropy] = new_element;
13957
13958     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13959       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13960                           el2img(Feld[dropx][dropy]), 0);
13961
13962     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13963   }
13964
13965   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13966     InitField_WithBug1(dropx, dropy, FALSE);
13967
13968   new_element = Feld[dropx][dropy];     /* element might have changed */
13969
13970   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13971       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13972   {
13973     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13974       MovDir[dropx][dropy] = drop_direction;
13975
13976     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13977
13978     /* do not cause impact style collision by dropping elements that can fall */
13979     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13980   }
13981
13982   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13983   player->is_dropping = TRUE;
13984
13985   player->drop_pressed_delay = 0;
13986   player->is_dropping_pressed = FALSE;
13987
13988   player->drop_x = dropx;
13989   player->drop_y = dropy;
13990
13991   return TRUE;
13992 }
13993
13994 /* ------------------------------------------------------------------------- */
13995 /* game sound playing functions                                              */
13996 /* ------------------------------------------------------------------------- */
13997
13998 static int *loop_sound_frame = NULL;
13999 static int *loop_sound_volume = NULL;
14000
14001 void InitPlayLevelSound()
14002 {
14003   int num_sounds = getSoundListSize();
14004
14005   checked_free(loop_sound_frame);
14006   checked_free(loop_sound_volume);
14007
14008   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14009   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14010 }
14011
14012 static void PlayLevelSound(int x, int y, int nr)
14013 {
14014   int sx = SCREENX(x), sy = SCREENY(y);
14015   int volume, stereo_position;
14016   int max_distance = 8;
14017   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14018
14019   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14020       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14021     return;
14022
14023   if (!IN_LEV_FIELD(x, y) ||
14024       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14025       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14026     return;
14027
14028   volume = SOUND_MAX_VOLUME;
14029
14030   if (!IN_SCR_FIELD(sx, sy))
14031   {
14032     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14033     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14034
14035     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14036   }
14037
14038   stereo_position = (SOUND_MAX_LEFT +
14039                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14040                      (SCR_FIELDX + 2 * max_distance));
14041
14042   if (IS_LOOP_SOUND(nr))
14043   {
14044     /* This assures that quieter loop sounds do not overwrite louder ones,
14045        while restarting sound volume comparison with each new game frame. */
14046
14047     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14048       return;
14049
14050     loop_sound_volume[nr] = volume;
14051     loop_sound_frame[nr] = FrameCounter;
14052   }
14053
14054   PlaySoundExt(nr, volume, stereo_position, type);
14055 }
14056
14057 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14058 {
14059   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14060                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14061                  y < LEVELY(BY1) ? LEVELY(BY1) :
14062                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14063                  sound_action);
14064 }
14065
14066 static void PlayLevelSoundAction(int x, int y, int action)
14067 {
14068   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14069 }
14070
14071 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14072 {
14073   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14074
14075   if (sound_effect != SND_UNDEFINED)
14076     PlayLevelSound(x, y, sound_effect);
14077 }
14078
14079 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14080                                               int action)
14081 {
14082   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14083
14084   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14085     PlayLevelSound(x, y, sound_effect);
14086 }
14087
14088 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14089 {
14090   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14091
14092   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14093     PlayLevelSound(x, y, sound_effect);
14094 }
14095
14096 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14097 {
14098   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14099
14100   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14101     StopSound(sound_effect);
14102 }
14103
14104 static void PlayLevelMusic()
14105 {
14106   if (levelset.music[level_nr] != MUS_UNDEFINED)
14107     PlayMusic(levelset.music[level_nr]);        /* from config file */
14108   else
14109     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14110 }
14111
14112 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14113 {
14114   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14115   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14116   int x = xx - 1 - offset;
14117   int y = yy - 1 - offset;
14118
14119   switch (sample)
14120   {
14121     case SAMPLE_blank:
14122       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14123       break;
14124
14125     case SAMPLE_roll:
14126       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14127       break;
14128
14129     case SAMPLE_stone:
14130       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14131       break;
14132
14133     case SAMPLE_nut:
14134       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14135       break;
14136
14137     case SAMPLE_crack:
14138       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14139       break;
14140
14141     case SAMPLE_bug:
14142       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14143       break;
14144
14145     case SAMPLE_tank:
14146       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14147       break;
14148
14149     case SAMPLE_android_clone:
14150       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14151       break;
14152
14153     case SAMPLE_android_move:
14154       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14155       break;
14156
14157     case SAMPLE_spring:
14158       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14159       break;
14160
14161     case SAMPLE_slurp:
14162       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14163       break;
14164
14165     case SAMPLE_eater:
14166       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14167       break;
14168
14169     case SAMPLE_eater_eat:
14170       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14171       break;
14172
14173     case SAMPLE_alien:
14174       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14175       break;
14176
14177     case SAMPLE_collect:
14178       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14179       break;
14180
14181     case SAMPLE_diamond:
14182       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14183       break;
14184
14185     case SAMPLE_squash:
14186       /* !!! CHECK THIS !!! */
14187 #if 1
14188       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14189 #else
14190       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14191 #endif
14192       break;
14193
14194     case SAMPLE_wonderfall:
14195       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14196       break;
14197
14198     case SAMPLE_drip:
14199       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14200       break;
14201
14202     case SAMPLE_push:
14203       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14204       break;
14205
14206     case SAMPLE_dirt:
14207       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14208       break;
14209
14210     case SAMPLE_acid:
14211       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14212       break;
14213
14214     case SAMPLE_ball:
14215       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14216       break;
14217
14218     case SAMPLE_grow:
14219       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14220       break;
14221
14222     case SAMPLE_wonder:
14223       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14224       break;
14225
14226     case SAMPLE_door:
14227       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14228       break;
14229
14230     case SAMPLE_exit_open:
14231       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14232       break;
14233
14234     case SAMPLE_exit_leave:
14235       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14236       break;
14237
14238     case SAMPLE_dynamite:
14239       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14240       break;
14241
14242     case SAMPLE_tick:
14243       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14244       break;
14245
14246     case SAMPLE_press:
14247       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14248       break;
14249
14250     case SAMPLE_wheel:
14251       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14252       break;
14253
14254     case SAMPLE_boom:
14255       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14256       break;
14257
14258     case SAMPLE_die:
14259       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14260       break;
14261
14262     case SAMPLE_time:
14263       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14264       break;
14265
14266     default:
14267       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14268       break;
14269   }
14270 }
14271
14272 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14273 {
14274   int element = map_element_SP_to_RND(element_sp);
14275   int action = map_action_SP_to_RND(action_sp);
14276   int offset = (setup.sp_show_border_elements ? 0 : 1);
14277   int x = xx - offset;
14278   int y = yy - offset;
14279
14280   PlayLevelSoundElementAction(x, y, element, action);
14281 }
14282
14283 void RaiseScore(int value)
14284 {
14285   local_player->score += value;
14286
14287   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14288
14289   DisplayGameControlValues();
14290 }
14291
14292 void RaiseScoreElement(int element)
14293 {
14294   switch (element)
14295   {
14296     case EL_EMERALD:
14297     case EL_BD_DIAMOND:
14298     case EL_EMERALD_YELLOW:
14299     case EL_EMERALD_RED:
14300     case EL_EMERALD_PURPLE:
14301     case EL_SP_INFOTRON:
14302       RaiseScore(level.score[SC_EMERALD]);
14303       break;
14304     case EL_DIAMOND:
14305       RaiseScore(level.score[SC_DIAMOND]);
14306       break;
14307     case EL_CRYSTAL:
14308       RaiseScore(level.score[SC_CRYSTAL]);
14309       break;
14310     case EL_PEARL:
14311       RaiseScore(level.score[SC_PEARL]);
14312       break;
14313     case EL_BUG:
14314     case EL_BD_BUTTERFLY:
14315     case EL_SP_ELECTRON:
14316       RaiseScore(level.score[SC_BUG]);
14317       break;
14318     case EL_SPACESHIP:
14319     case EL_BD_FIREFLY:
14320     case EL_SP_SNIKSNAK:
14321       RaiseScore(level.score[SC_SPACESHIP]);
14322       break;
14323     case EL_YAMYAM:
14324     case EL_DARK_YAMYAM:
14325       RaiseScore(level.score[SC_YAMYAM]);
14326       break;
14327     case EL_ROBOT:
14328       RaiseScore(level.score[SC_ROBOT]);
14329       break;
14330     case EL_PACMAN:
14331       RaiseScore(level.score[SC_PACMAN]);
14332       break;
14333     case EL_NUT:
14334       RaiseScore(level.score[SC_NUT]);
14335       break;
14336     case EL_DYNAMITE:
14337     case EL_EM_DYNAMITE:
14338     case EL_SP_DISK_RED:
14339     case EL_DYNABOMB_INCREASE_NUMBER:
14340     case EL_DYNABOMB_INCREASE_SIZE:
14341     case EL_DYNABOMB_INCREASE_POWER:
14342       RaiseScore(level.score[SC_DYNAMITE]);
14343       break;
14344     case EL_SHIELD_NORMAL:
14345     case EL_SHIELD_DEADLY:
14346       RaiseScore(level.score[SC_SHIELD]);
14347       break;
14348     case EL_EXTRA_TIME:
14349       RaiseScore(level.extra_time_score);
14350       break;
14351     case EL_KEY_1:
14352     case EL_KEY_2:
14353     case EL_KEY_3:
14354     case EL_KEY_4:
14355     case EL_EM_KEY_1:
14356     case EL_EM_KEY_2:
14357     case EL_EM_KEY_3:
14358     case EL_EM_KEY_4:
14359     case EL_EMC_KEY_5:
14360     case EL_EMC_KEY_6:
14361     case EL_EMC_KEY_7:
14362     case EL_EMC_KEY_8:
14363     case EL_DC_KEY_WHITE:
14364       RaiseScore(level.score[SC_KEY]);
14365       break;
14366     default:
14367       RaiseScore(element_info[element].collect_score);
14368       break;
14369   }
14370 }
14371
14372 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14373 {
14374   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14375   {
14376     /* closing door required in case of envelope style request dialogs */
14377     if (!skip_request)
14378       CloseDoor(DOOR_CLOSE_1);
14379
14380 #if defined(NETWORK_AVALIABLE)
14381     if (options.network)
14382       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14383     else
14384 #endif
14385     {
14386       if (quick_quit)
14387       {
14388         FadeSkipNextFadeIn();
14389
14390         game_status = GAME_MODE_MAIN;
14391
14392         DrawAndFadeInMainMenu(REDRAW_FIELD);
14393       }
14394       else
14395       {
14396         game_status = GAME_MODE_MAIN;
14397
14398         DrawAndFadeInMainMenu(REDRAW_FIELD);
14399       }
14400     }
14401   }
14402   else          /* continue playing the game */
14403   {
14404     if (tape.playing && tape.deactivate_display)
14405       TapeDeactivateDisplayOff(TRUE);
14406
14407     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14408
14409     if (tape.playing && tape.deactivate_display)
14410       TapeDeactivateDisplayOn();
14411   }
14412 }
14413
14414 void RequestQuitGame(boolean ask_if_really_quit)
14415 {
14416   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14417   boolean skip_request = AllPlayersGone || quick_quit;
14418
14419   RequestQuitGameExt(skip_request, quick_quit,
14420                      "Do you really want to quit the game?");
14421 }
14422
14423
14424 /* ------------------------------------------------------------------------- */
14425 /* random generator functions                                                */
14426 /* ------------------------------------------------------------------------- */
14427
14428 unsigned int InitEngineRandom_RND(int seed)
14429 {
14430   game.num_random_calls = 0;
14431
14432   return InitEngineRandom(seed);
14433 }
14434
14435 unsigned int RND(int max)
14436 {
14437   if (max > 0)
14438   {
14439     game.num_random_calls++;
14440
14441     return GetEngineRandom(max);
14442   }
14443
14444   return 0;
14445 }
14446
14447
14448 /* ------------------------------------------------------------------------- */
14449 /* game engine snapshot handling functions                                   */
14450 /* ------------------------------------------------------------------------- */
14451
14452 struct EngineSnapshotInfo
14453 {
14454   /* runtime values for custom element collect score */
14455   int collect_score[NUM_CUSTOM_ELEMENTS];
14456
14457   /* runtime values for group element choice position */
14458   int choice_pos[NUM_GROUP_ELEMENTS];
14459
14460   /* runtime values for belt position animations */
14461   int belt_graphic[4][NUM_BELT_PARTS];
14462   int belt_anim_mode[4][NUM_BELT_PARTS];
14463 };
14464
14465 static struct EngineSnapshotInfo engine_snapshot_rnd;
14466 static char *snapshot_level_identifier = NULL;
14467 static int snapshot_level_nr = -1;
14468
14469 static void SaveEngineSnapshotValues_RND()
14470 {
14471   static int belt_base_active_element[4] =
14472   {
14473     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14474     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14475     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14476     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14477   };
14478   int i, j;
14479
14480   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14481   {
14482     int element = EL_CUSTOM_START + i;
14483
14484     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14485   }
14486
14487   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14488   {
14489     int element = EL_GROUP_START + i;
14490
14491     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14492   }
14493
14494   for (i = 0; i < 4; i++)
14495   {
14496     for (j = 0; j < NUM_BELT_PARTS; j++)
14497     {
14498       int element = belt_base_active_element[i] + j;
14499       int graphic = el2img(element);
14500       int anim_mode = graphic_info[graphic].anim_mode;
14501
14502       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14503       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14504     }
14505   }
14506 }
14507
14508 static void LoadEngineSnapshotValues_RND()
14509 {
14510   unsigned int num_random_calls = game.num_random_calls;
14511   int i, j;
14512
14513   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14514   {
14515     int element = EL_CUSTOM_START + i;
14516
14517     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14518   }
14519
14520   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14521   {
14522     int element = EL_GROUP_START + i;
14523
14524     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14525   }
14526
14527   for (i = 0; i < 4; i++)
14528   {
14529     for (j = 0; j < NUM_BELT_PARTS; j++)
14530     {
14531       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14532       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14533
14534       graphic_info[graphic].anim_mode = anim_mode;
14535     }
14536   }
14537
14538   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14539   {
14540     InitRND(tape.random_seed);
14541     for (i = 0; i < num_random_calls; i++)
14542       RND(1);
14543   }
14544
14545   if (game.num_random_calls != num_random_calls)
14546   {
14547     Error(ERR_INFO, "number of random calls out of sync");
14548     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14549     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14550     Error(ERR_EXIT, "this should not happen -- please debug");
14551   }
14552 }
14553
14554 void FreeEngineSnapshot()
14555 {
14556   FreeEngineSnapshotBuffers();
14557
14558   setString(&snapshot_level_identifier, NULL);
14559   snapshot_level_nr = -1;
14560 }
14561
14562 void SaveEngineSnapshot()
14563 {
14564   /* do not save snapshots from editor */
14565   if (level_editor_test_game)
14566     return;
14567
14568   /* free previous snapshot buffers, if needed */
14569   FreeEngineSnapshotBuffers();
14570
14571   /* copy some special values to a structure better suited for the snapshot */
14572
14573   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14574     SaveEngineSnapshotValues_RND();
14575   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14576     SaveEngineSnapshotValues_EM();
14577   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14578     SaveEngineSnapshotValues_SP();
14579
14580   /* save values stored in special snapshot structure */
14581
14582   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14583     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14584   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14585     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14586   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14587     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14588
14589   /* save further RND engine values */
14590
14591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14594
14595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14599
14600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14605
14606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14609
14610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14611
14612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14613
14614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14616
14617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14635
14636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14638
14639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14642
14643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14645
14646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14650   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14651
14652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14654
14655   /* save level identification information */
14656
14657   setString(&snapshot_level_identifier, leveldir_current->identifier);
14658   snapshot_level_nr = level_nr;
14659
14660 #if 0
14661   ListNode *node = engine_snapshot_list_rnd;
14662   int num_bytes = 0;
14663
14664   while (node != NULL)
14665   {
14666     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14667
14668     node = node->next;
14669   }
14670
14671   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14672 #endif
14673 }
14674
14675 void LoadEngineSnapshot()
14676 {
14677   /* restore generically stored snapshot buffers */
14678
14679   LoadEngineSnapshotBuffers();
14680
14681   /* restore special values from snapshot structure */
14682
14683   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14684     LoadEngineSnapshotValues_RND();
14685   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14686     LoadEngineSnapshotValues_EM();
14687   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14688     LoadEngineSnapshotValues_SP();
14689 }
14690
14691 boolean CheckEngineSnapshot()
14692 {
14693   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14694           snapshot_level_nr == level_nr);
14695 }
14696
14697
14698 /* ---------- new game button stuff ---------------------------------------- */
14699
14700 static struct
14701 {
14702   int graphic;
14703   struct Rect *pos;
14704   int gadget_id;
14705   char *infotext;
14706 } gamebutton_info[NUM_GAME_BUTTONS] =
14707 {
14708   {
14709     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14710     GAME_CTRL_ID_STOP,                  "stop game"
14711   },
14712   {
14713     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14714     GAME_CTRL_ID_PAUSE,                 "pause game"
14715   },
14716   {
14717     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14718     GAME_CTRL_ID_PLAY,                  "play game"
14719   },
14720   {
14721     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14722     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14723   },
14724   {
14725     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14726     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14727   },
14728   {
14729     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14730     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14731   },
14732   {
14733     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14734     GAME_CTRL_ID_SAVE,                  "save game"
14735   },
14736   {
14737     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14738     GAME_CTRL_ID_LOAD,                  "load game"
14739   }
14740 };
14741
14742 void CreateGameButtons()
14743 {
14744   int i;
14745
14746   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14747   {
14748     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14749     struct Rect *pos = gamebutton_info[i].pos;
14750     struct GadgetInfo *gi;
14751     int button_type;
14752     boolean checked;
14753     unsigned int event_mask;
14754     int base_x = (tape.show_game_buttons ? VX : DX);
14755     int base_y = (tape.show_game_buttons ? VY : DY);
14756     int gd_x   = gfx->src_x;
14757     int gd_y   = gfx->src_y;
14758     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14759     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14760     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14761     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14762     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14763     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14764     int id = i;
14765
14766     if (gfx->bitmap == NULL)
14767     {
14768       game_gadget[id] = NULL;
14769
14770       continue;
14771     }
14772
14773     if (id == GAME_CTRL_ID_STOP ||
14774         id == GAME_CTRL_ID_PAUSE ||
14775         id == GAME_CTRL_ID_PLAY ||
14776         id == GAME_CTRL_ID_SAVE ||
14777         id == GAME_CTRL_ID_LOAD)
14778     {
14779       button_type = GD_TYPE_NORMAL_BUTTON;
14780       checked = FALSE;
14781       event_mask = GD_EVENT_RELEASED;
14782     }
14783     else
14784     {
14785       button_type = GD_TYPE_CHECK_BUTTON;
14786       checked =
14787         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14788          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14789          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14790       event_mask = GD_EVENT_PRESSED;
14791     }
14792
14793     gi = CreateGadget(GDI_CUSTOM_ID, id,
14794                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14795                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14796                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14797                       GDI_WIDTH, gfx->width,
14798                       GDI_HEIGHT, gfx->height,
14799                       GDI_TYPE, button_type,
14800                       GDI_STATE, GD_BUTTON_UNPRESSED,
14801                       GDI_CHECKED, checked,
14802                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14803                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14804                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14805                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14806                       GDI_DIRECT_DRAW, FALSE,
14807                       GDI_EVENT_MASK, event_mask,
14808                       GDI_CALLBACK_ACTION, HandleGameButtons,
14809                       GDI_END);
14810
14811     if (gi == NULL)
14812       Error(ERR_EXIT, "cannot create gadget");
14813
14814     game_gadget[id] = gi;
14815   }
14816 }
14817
14818 void FreeGameButtons()
14819 {
14820   int i;
14821
14822   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14823     FreeGadget(game_gadget[i]);
14824 }
14825
14826 void MapGameButtons()
14827 {
14828   int i;
14829
14830   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14831     MapGadget(game_gadget[i]);
14832 }
14833
14834 void UnmapGameButtons()
14835 {
14836   int i;
14837
14838   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14839     UnmapGadget(game_gadget[i]);
14840 }
14841
14842 void RedrawGameButtons()
14843 {
14844   int i;
14845
14846   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14847     RedrawGadget(game_gadget[i]);
14848
14849   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14850   redraw_mask &= ~REDRAW_ALL;
14851 }
14852
14853 static void HandleGameButtonsExt(int id)
14854 {
14855   boolean handle_game_buttons =
14856     (game_status == GAME_MODE_PLAYING ||
14857      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
14858
14859   if (!handle_game_buttons)
14860     return;
14861
14862   switch (id)
14863   {
14864     case GAME_CTRL_ID_STOP:
14865       if (game_status == GAME_MODE_MAIN)
14866         break;
14867
14868       if (tape.playing)
14869         TapeStop();
14870       else
14871         RequestQuitGame(TRUE);
14872
14873       break;
14874
14875     case GAME_CTRL_ID_PAUSE:
14876       if (options.network && game_status == GAME_MODE_PLAYING)
14877       {
14878 #if defined(NETWORK_AVALIABLE)
14879         if (tape.pausing)
14880           SendToServer_ContinuePlaying();
14881         else
14882           SendToServer_PausePlaying();
14883 #endif
14884       }
14885       else
14886         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14887       break;
14888
14889     case GAME_CTRL_ID_PLAY:
14890       if (game_status == GAME_MODE_MAIN)
14891       {
14892         StartGameActions(options.network, setup.autorecord, level.random_seed);
14893       }
14894       else if (tape.pausing)
14895       {
14896 #if defined(NETWORK_AVALIABLE)
14897         if (options.network)
14898           SendToServer_ContinuePlaying();
14899         else
14900 #endif
14901         {
14902           tape.pausing = FALSE;
14903           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14904         }
14905       }
14906       break;
14907
14908     case SOUND_CTRL_ID_MUSIC:
14909       if (setup.sound_music)
14910       { 
14911         setup.sound_music = FALSE;
14912
14913         FadeMusic();
14914       }
14915       else if (audio.music_available)
14916       { 
14917         setup.sound = setup.sound_music = TRUE;
14918
14919         SetAudioMode(setup.sound);
14920
14921         PlayLevelMusic();
14922       }
14923       break;
14924
14925     case SOUND_CTRL_ID_LOOPS:
14926       if (setup.sound_loops)
14927         setup.sound_loops = FALSE;
14928       else if (audio.loops_available)
14929       {
14930         setup.sound = setup.sound_loops = TRUE;
14931
14932         SetAudioMode(setup.sound);
14933       }
14934       break;
14935
14936     case SOUND_CTRL_ID_SIMPLE:
14937       if (setup.sound_simple)
14938         setup.sound_simple = FALSE;
14939       else if (audio.sound_available)
14940       {
14941         setup.sound = setup.sound_simple = TRUE;
14942
14943         SetAudioMode(setup.sound);
14944       }
14945       break;
14946
14947     case GAME_CTRL_ID_SAVE:
14948       TapeQuickSave();
14949       break;
14950
14951     case GAME_CTRL_ID_LOAD:
14952       TapeQuickLoad();
14953       break;
14954
14955     default:
14956       break;
14957   }
14958 }
14959
14960 static void HandleGameButtons(struct GadgetInfo *gi)
14961 {
14962   HandleGameButtonsExt(gi->custom_id);
14963 }
14964
14965 void HandleSoundButtonKeys(Key key)
14966 {
14967
14968   if (key == setup.shortcut.sound_simple)
14969     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
14970   else if (key == setup.shortcut.sound_loops)
14971     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
14972   else if (key == setup.shortcut.sound_music)
14973     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
14974 }