a906aad2379992b0539d746e1d11187071eecbaa
[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         {
2523           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2524                         dst_x - src_x, dst_y - src_y);
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         }
2528         else
2529         {
2530           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2531                      dst_x, dst_y);
2532         }
2533       }
2534     }
2535     else if (type == TYPE_STRING)
2536     {
2537       boolean active = (value != 0);
2538       char *state_normal = "off";
2539       char *state_active = "on";
2540       char *state = (active ? state_active : state_normal);
2541       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2542                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2543                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2544                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2545
2546       if (nr == GAME_PANEL_GRAVITY_STATE)
2547       {
2548         int font1 = pos->font;          /* (used for normal state) */
2549         int font2 = pos->font_alt;      /* (used for active state) */
2550
2551         font = (active ? font2 : font1);
2552       }
2553
2554       if (s != NULL)
2555       {
2556         char *s_cut;
2557
2558         if (size <= 0)
2559         {
2560           /* don't truncate output if "chars" is zero or less */
2561           size = strlen(s);
2562
2563           /* dynamically correct text alignment */
2564           pos->width = size * getFontWidth(font);
2565         }
2566
2567         s_cut = getStringCopyN(s, size);
2568
2569         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2570                     s_cut, font, mask_mode);
2571
2572         free(s_cut);
2573       }
2574     }
2575
2576     redraw_mask |= REDRAW_DOOR_1;
2577   }
2578
2579   game_status = GAME_MODE_PLAYING;
2580 }
2581
2582 void UpdateAndDisplayGameControlValues()
2583 {
2584   if (tape.warp_forward)
2585     return;
2586
2587   UpdateGameControlValues();
2588   DisplayGameControlValues();
2589 }
2590
2591 void UpdateGameDoorValues()
2592 {
2593   UpdateGameControlValues();
2594 }
2595
2596 void DrawGameDoorValues()
2597 {
2598   DisplayGameControlValues();
2599 }
2600
2601
2602 /*
2603   =============================================================================
2604   InitGameEngine()
2605   -----------------------------------------------------------------------------
2606   initialize game engine due to level / tape version number
2607   =============================================================================
2608 */
2609
2610 static void InitGameEngine()
2611 {
2612   int i, j, k, l, x, y;
2613
2614   /* set game engine from tape file when re-playing, else from level file */
2615   game.engine_version = (tape.playing ? tape.engine_version :
2616                          level.game_version);
2617
2618   /* set single or multi-player game mode (needed for re-playing tapes) */
2619   game.team_mode = setup.team_mode;
2620
2621   if (tape.playing)
2622   {
2623     int num_players = 0;
2624
2625     for (i = 0; i < MAX_PLAYERS; i++)
2626       if (tape.player_participates[i])
2627         num_players++;
2628
2629     /* multi-player tapes contain input data for more than one player */
2630     game.team_mode = (num_players > 1);
2631   }
2632
2633   /* ---------------------------------------------------------------------- */
2634   /* set flags for bugs and changes according to active game engine version */
2635   /* ---------------------------------------------------------------------- */
2636
2637   /*
2638     Summary of bugfix/change:
2639     Fixed handling for custom elements that change when pushed by the player.
2640
2641     Fixed/changed in version:
2642     3.1.0
2643
2644     Description:
2645     Before 3.1.0, custom elements that "change when pushing" changed directly
2646     after the player started pushing them (until then handled in "DigField()").
2647     Since 3.1.0, these custom elements are not changed until the "pushing"
2648     move of the element is finished (now handled in "ContinueMoving()").
2649
2650     Affected levels/tapes:
2651     The first condition is generally needed for all levels/tapes before version
2652     3.1.0, which might use the old behaviour before it was changed; known tapes
2653     that are affected are some tapes from the level set "Walpurgis Gardens" by
2654     Jamie Cullen.
2655     The second condition is an exception from the above case and is needed for
2656     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2657     above (including some development versions of 3.1.0), but before it was
2658     known that this change would break tapes like the above and was fixed in
2659     3.1.1, so that the changed behaviour was active although the engine version
2660     while recording maybe was before 3.1.0. There is at least one tape that is
2661     affected by this exception, which is the tape for the one-level set "Bug
2662     Machine" by Juergen Bonhagen.
2663   */
2664
2665   game.use_change_when_pushing_bug =
2666     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2667      !(tape.playing &&
2668        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2669        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2670
2671   /*
2672     Summary of bugfix/change:
2673     Fixed handling for blocking the field the player leaves when moving.
2674
2675     Fixed/changed in version:
2676     3.1.1
2677
2678     Description:
2679     Before 3.1.1, when "block last field when moving" was enabled, the field
2680     the player is leaving when moving was blocked for the time of the move,
2681     and was directly unblocked afterwards. This resulted in the last field
2682     being blocked for exactly one less than the number of frames of one player
2683     move. Additionally, even when blocking was disabled, the last field was
2684     blocked for exactly one frame.
2685     Since 3.1.1, due to changes in player movement handling, the last field
2686     is not blocked at all when blocking is disabled. When blocking is enabled,
2687     the last field is blocked for exactly the number of frames of one player
2688     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2689     last field is blocked for exactly one more than the number of frames of
2690     one player move.
2691
2692     Affected levels/tapes:
2693     (!!! yet to be determined -- probably many !!!)
2694   */
2695
2696   game.use_block_last_field_bug =
2697     (game.engine_version < VERSION_IDENT(3,1,1,0));
2698
2699   /* ---------------------------------------------------------------------- */
2700
2701   /* set maximal allowed number of custom element changes per game frame */
2702   game.max_num_changes_per_frame = 1;
2703
2704   /* default scan direction: scan playfield from top/left to bottom/right */
2705   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2706
2707   /* dynamically adjust element properties according to game engine version */
2708   InitElementPropertiesEngine(game.engine_version);
2709
2710 #if 0
2711   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2712   printf("          tape version == %06d [%s] [file: %06d]\n",
2713          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2714          tape.file_version);
2715   printf("       => game.engine_version == %06d\n", game.engine_version);
2716 #endif
2717
2718   /* ---------- initialize player's initial move delay --------------------- */
2719
2720   /* dynamically adjust player properties according to level information */
2721   for (i = 0; i < MAX_PLAYERS; i++)
2722     game.initial_move_delay_value[i] =
2723       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2724
2725   /* dynamically adjust player properties according to game engine version */
2726   for (i = 0; i < MAX_PLAYERS; i++)
2727     game.initial_move_delay[i] =
2728       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2729        game.initial_move_delay_value[i] : 0);
2730
2731   /* ---------- initialize player's initial push delay --------------------- */
2732
2733   /* dynamically adjust player properties according to game engine version */
2734   game.initial_push_delay_value =
2735     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2736
2737   /* ---------- initialize changing elements ------------------------------- */
2738
2739   /* initialize changing elements information */
2740   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2741   {
2742     struct ElementInfo *ei = &element_info[i];
2743
2744     /* this pointer might have been changed in the level editor */
2745     ei->change = &ei->change_page[0];
2746
2747     if (!IS_CUSTOM_ELEMENT(i))
2748     {
2749       ei->change->target_element = EL_EMPTY_SPACE;
2750       ei->change->delay_fixed = 0;
2751       ei->change->delay_random = 0;
2752       ei->change->delay_frames = 1;
2753     }
2754
2755     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2756     {
2757       ei->has_change_event[j] = FALSE;
2758
2759       ei->event_page_nr[j] = 0;
2760       ei->event_page[j] = &ei->change_page[0];
2761     }
2762   }
2763
2764   /* add changing elements from pre-defined list */
2765   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2766   {
2767     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2768     struct ElementInfo *ei = &element_info[ch_delay->element];
2769
2770     ei->change->target_element       = ch_delay->target_element;
2771     ei->change->delay_fixed          = ch_delay->change_delay;
2772
2773     ei->change->pre_change_function  = ch_delay->pre_change_function;
2774     ei->change->change_function      = ch_delay->change_function;
2775     ei->change->post_change_function = ch_delay->post_change_function;
2776
2777     ei->change->can_change = TRUE;
2778     ei->change->can_change_or_has_action = TRUE;
2779
2780     ei->has_change_event[CE_DELAY] = TRUE;
2781
2782     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2783     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2784   }
2785
2786   /* ---------- initialize internal run-time variables --------------------- */
2787
2788   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2789   {
2790     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2791
2792     for (j = 0; j < ei->num_change_pages; j++)
2793     {
2794       ei->change_page[j].can_change_or_has_action =
2795         (ei->change_page[j].can_change |
2796          ei->change_page[j].has_action);
2797     }
2798   }
2799
2800   /* add change events from custom element configuration */
2801   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2802   {
2803     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2804
2805     for (j = 0; j < ei->num_change_pages; j++)
2806     {
2807       if (!ei->change_page[j].can_change_or_has_action)
2808         continue;
2809
2810       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2811       {
2812         /* only add event page for the first page found with this event */
2813         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2814         {
2815           ei->has_change_event[k] = TRUE;
2816
2817           ei->event_page_nr[k] = j;
2818           ei->event_page[k] = &ei->change_page[j];
2819         }
2820       }
2821     }
2822   }
2823
2824   /* ---------- initialize reference elements in change conditions --------- */
2825
2826   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2827   {
2828     int element = EL_CUSTOM_START + i;
2829     struct ElementInfo *ei = &element_info[element];
2830
2831     for (j = 0; j < ei->num_change_pages; j++)
2832     {
2833       int trigger_element = ei->change_page[j].initial_trigger_element;
2834
2835       if (trigger_element >= EL_PREV_CE_8 &&
2836           trigger_element <= EL_NEXT_CE_8)
2837         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2838
2839       ei->change_page[j].trigger_element = trigger_element;
2840     }
2841   }
2842
2843   /* ---------- initialize run-time trigger player and element ------------- */
2844
2845   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2846   {
2847     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2848
2849     for (j = 0; j < ei->num_change_pages; j++)
2850     {
2851       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2852       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2853       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2854       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2855       ei->change_page[j].actual_trigger_ce_value = 0;
2856       ei->change_page[j].actual_trigger_ce_score = 0;
2857     }
2858   }
2859
2860   /* ---------- initialize trigger events ---------------------------------- */
2861
2862   /* initialize trigger events information */
2863   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2864     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2865       trigger_events[i][j] = FALSE;
2866
2867   /* add trigger events from element change event properties */
2868   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869   {
2870     struct ElementInfo *ei = &element_info[i];
2871
2872     for (j = 0; j < ei->num_change_pages; j++)
2873     {
2874       if (!ei->change_page[j].can_change_or_has_action)
2875         continue;
2876
2877       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2878       {
2879         int trigger_element = ei->change_page[j].trigger_element;
2880
2881         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2882         {
2883           if (ei->change_page[j].has_event[k])
2884           {
2885             if (IS_GROUP_ELEMENT(trigger_element))
2886             {
2887               struct ElementGroupInfo *group =
2888                 element_info[trigger_element].group;
2889
2890               for (l = 0; l < group->num_elements_resolved; l++)
2891                 trigger_events[group->element_resolved[l]][k] = TRUE;
2892             }
2893             else if (trigger_element == EL_ANY_ELEMENT)
2894               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2895                 trigger_events[l][k] = TRUE;
2896             else
2897               trigger_events[trigger_element][k] = TRUE;
2898           }
2899         }
2900       }
2901     }
2902   }
2903
2904   /* ---------- initialize push delay -------------------------------------- */
2905
2906   /* initialize push delay values to default */
2907   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2908   {
2909     if (!IS_CUSTOM_ELEMENT(i))
2910     {
2911       /* set default push delay values (corrected since version 3.0.7-1) */
2912       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2913       {
2914         element_info[i].push_delay_fixed = 2;
2915         element_info[i].push_delay_random = 8;
2916       }
2917       else
2918       {
2919         element_info[i].push_delay_fixed = 8;
2920         element_info[i].push_delay_random = 8;
2921       }
2922     }
2923   }
2924
2925   /* set push delay value for certain elements from pre-defined list */
2926   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2927   {
2928     int e = push_delay_list[i].element;
2929
2930     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2931     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2932   }
2933
2934   /* set push delay value for Supaplex elements for newer engine versions */
2935   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2936   {
2937     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2938     {
2939       if (IS_SP_ELEMENT(i))
2940       {
2941         /* set SP push delay to just enough to push under a falling zonk */
2942         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2943
2944         element_info[i].push_delay_fixed  = delay;
2945         element_info[i].push_delay_random = 0;
2946       }
2947     }
2948   }
2949
2950   /* ---------- initialize move stepsize ----------------------------------- */
2951
2952   /* initialize move stepsize values to default */
2953   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2954     if (!IS_CUSTOM_ELEMENT(i))
2955       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2956
2957   /* set move stepsize value for certain elements from pre-defined list */
2958   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2959   {
2960     int e = move_stepsize_list[i].element;
2961
2962     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2963   }
2964
2965   /* ---------- initialize collect score ----------------------------------- */
2966
2967   /* initialize collect score values for custom elements from initial value */
2968   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2969     if (IS_CUSTOM_ELEMENT(i))
2970       element_info[i].collect_score = element_info[i].collect_score_initial;
2971
2972   /* ---------- initialize collect count ----------------------------------- */
2973
2974   /* initialize collect count values for non-custom elements */
2975   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2976     if (!IS_CUSTOM_ELEMENT(i))
2977       element_info[i].collect_count_initial = 0;
2978
2979   /* add collect count values for all elements from pre-defined list */
2980   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2981     element_info[collect_count_list[i].element].collect_count_initial =
2982       collect_count_list[i].count;
2983
2984   /* ---------- initialize access direction -------------------------------- */
2985
2986   /* initialize access direction values to default (access from every side) */
2987   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2988     if (!IS_CUSTOM_ELEMENT(i))
2989       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2990
2991   /* set access direction value for certain elements from pre-defined list */
2992   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2993     element_info[access_direction_list[i].element].access_direction =
2994       access_direction_list[i].direction;
2995
2996   /* ---------- initialize explosion content ------------------------------- */
2997   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2998   {
2999     if (IS_CUSTOM_ELEMENT(i))
3000       continue;
3001
3002     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3003     {
3004       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3005
3006       element_info[i].content.e[x][y] =
3007         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3008          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3009          i == EL_PLAYER_3 ? EL_EMERALD :
3010          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3011          i == EL_MOLE ? EL_EMERALD_RED :
3012          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3013          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3014          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3015          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3016          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3017          i == EL_WALL_EMERALD ? EL_EMERALD :
3018          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3019          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3020          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3021          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3022          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3023          i == EL_WALL_PEARL ? EL_PEARL :
3024          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3025          EL_EMPTY);
3026     }
3027   }
3028
3029   /* ---------- initialize recursion detection ------------------------------ */
3030   recursion_loop_depth = 0;
3031   recursion_loop_detected = FALSE;
3032   recursion_loop_element = EL_UNDEFINED;
3033
3034   /* ---------- initialize graphics engine ---------------------------------- */
3035   game.scroll_delay_value =
3036     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3037      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3038   game.scroll_delay_value =
3039     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3040 }
3041
3042 int get_num_special_action(int element, int action_first, int action_last)
3043 {
3044   int num_special_action = 0;
3045   int i, j;
3046
3047   for (i = action_first; i <= action_last; i++)
3048   {
3049     boolean found = FALSE;
3050
3051     for (j = 0; j < NUM_DIRECTIONS; j++)
3052       if (el_act_dir2img(element, i, j) !=
3053           el_act_dir2img(element, ACTION_DEFAULT, j))
3054         found = TRUE;
3055
3056     if (found)
3057       num_special_action++;
3058     else
3059       break;
3060   }
3061
3062   return num_special_action;
3063 }
3064
3065
3066 /*
3067   =============================================================================
3068   InitGame()
3069   -----------------------------------------------------------------------------
3070   initialize and start new game
3071   =============================================================================
3072 */
3073
3074 void InitGame()
3075 {
3076   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3077   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3078
3079   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3080   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3081   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3082   int initial_move_dir = MV_DOWN;
3083   int i, j, x, y;
3084
3085   game_status = GAME_MODE_PLAYING;
3086
3087   StopAnimation();
3088
3089   if (!game.restart_level)
3090     CloseDoor(DOOR_CLOSE_1);
3091
3092   if (level_editor_test_game)
3093     FadeSkipNextFadeIn();
3094   else
3095     FadeSetEnterScreen();
3096
3097   FadeOut(REDRAW_FIELD);
3098
3099   /* needed if different viewport properties defined for playing */
3100   ChangeViewportPropertiesIfNeeded();
3101
3102   DrawCompleteVideoDisplay();
3103
3104   InitGameEngine();
3105   InitGameControlValues();
3106
3107   /* don't play tapes over network */
3108   network_playing = (options.network && !tape.playing);
3109
3110   for (i = 0; i < MAX_PLAYERS; i++)
3111   {
3112     struct PlayerInfo *player = &stored_player[i];
3113
3114     player->index_nr = i;
3115     player->index_bit = (1 << i);
3116     player->element_nr = EL_PLAYER_1 + i;
3117
3118     player->present = FALSE;
3119     player->active = FALSE;
3120     player->mapped = FALSE;
3121
3122     player->killed = FALSE;
3123     player->reanimated = FALSE;
3124
3125     player->action = 0;
3126     player->effective_action = 0;
3127     player->programmed_action = 0;
3128
3129     player->score = 0;
3130     player->score_final = 0;
3131
3132     player->gems_still_needed = level.gems_needed;
3133     player->sokobanfields_still_needed = 0;
3134     player->lights_still_needed = 0;
3135     player->friends_still_needed = 0;
3136
3137     for (j = 0; j < MAX_NUM_KEYS; j++)
3138       player->key[j] = FALSE;
3139
3140     player->num_white_keys = 0;
3141
3142     player->dynabomb_count = 0;
3143     player->dynabomb_size = 1;
3144     player->dynabombs_left = 0;
3145     player->dynabomb_xl = FALSE;
3146
3147     player->MovDir = initial_move_dir;
3148     player->MovPos = 0;
3149     player->GfxPos = 0;
3150     player->GfxDir = initial_move_dir;
3151     player->GfxAction = ACTION_DEFAULT;
3152     player->Frame = 0;
3153     player->StepFrame = 0;
3154
3155     player->initial_element = player->element_nr;
3156     player->artwork_element =
3157       (level.use_artwork_element[i] ? level.artwork_element[i] :
3158        player->element_nr);
3159     player->use_murphy = FALSE;
3160
3161     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3162     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3163
3164     player->gravity = level.initial_player_gravity[i];
3165
3166     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3167
3168     player->actual_frame_counter = 0;
3169
3170     player->step_counter = 0;
3171
3172     player->last_move_dir = initial_move_dir;
3173
3174     player->is_active = FALSE;
3175
3176     player->is_waiting = FALSE;
3177     player->is_moving = FALSE;
3178     player->is_auto_moving = FALSE;
3179     player->is_digging = FALSE;
3180     player->is_snapping = FALSE;
3181     player->is_collecting = FALSE;
3182     player->is_pushing = FALSE;
3183     player->is_switching = FALSE;
3184     player->is_dropping = FALSE;
3185     player->is_dropping_pressed = FALSE;
3186
3187     player->is_bored = FALSE;
3188     player->is_sleeping = FALSE;
3189
3190     player->frame_counter_bored = -1;
3191     player->frame_counter_sleeping = -1;
3192
3193     player->anim_delay_counter = 0;
3194     player->post_delay_counter = 0;
3195
3196     player->dir_waiting = initial_move_dir;
3197     player->action_waiting = ACTION_DEFAULT;
3198     player->last_action_waiting = ACTION_DEFAULT;
3199     player->special_action_bored = ACTION_DEFAULT;
3200     player->special_action_sleeping = ACTION_DEFAULT;
3201
3202     player->switch_x = -1;
3203     player->switch_y = -1;
3204
3205     player->drop_x = -1;
3206     player->drop_y = -1;
3207
3208     player->show_envelope = 0;
3209
3210     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3211
3212     player->push_delay       = -1;      /* initialized when pushing starts */
3213     player->push_delay_value = game.initial_push_delay_value;
3214
3215     player->drop_delay = 0;
3216     player->drop_pressed_delay = 0;
3217
3218     player->last_jx = -1;
3219     player->last_jy = -1;
3220     player->jx = -1;
3221     player->jy = -1;
3222
3223     player->shield_normal_time_left = 0;
3224     player->shield_deadly_time_left = 0;
3225
3226     player->inventory_infinite_element = EL_UNDEFINED;
3227     player->inventory_size = 0;
3228
3229     if (level.use_initial_inventory[i])
3230     {
3231       for (j = 0; j < level.initial_inventory_size[i]; j++)
3232       {
3233         int element = level.initial_inventory_content[i][j];
3234         int collect_count = element_info[element].collect_count_initial;
3235         int k;
3236
3237         if (!IS_CUSTOM_ELEMENT(element))
3238           collect_count = 1;
3239
3240         if (collect_count == 0)
3241           player->inventory_infinite_element = element;
3242         else
3243           for (k = 0; k < collect_count; k++)
3244             if (player->inventory_size < MAX_INVENTORY_SIZE)
3245               player->inventory_element[player->inventory_size++] = element;
3246       }
3247     }
3248
3249     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3250     SnapField(player, 0, 0);
3251
3252     player->LevelSolved = FALSE;
3253     player->GameOver = FALSE;
3254
3255     player->LevelSolved_GameWon = FALSE;
3256     player->LevelSolved_GameEnd = FALSE;
3257     player->LevelSolved_PanelOff = FALSE;
3258     player->LevelSolved_SaveTape = FALSE;
3259     player->LevelSolved_SaveScore = FALSE;
3260     player->LevelSolved_CountingTime = 0;
3261     player->LevelSolved_CountingScore = 0;
3262
3263     map_player_action[i] = i;
3264   }
3265
3266   network_player_action_received = FALSE;
3267
3268 #if defined(NETWORK_AVALIABLE)
3269   /* initial null action */
3270   if (network_playing)
3271     SendToServer_MovePlayer(MV_NONE);
3272 #endif
3273
3274   ZX = ZY = -1;
3275   ExitX = ExitY = -1;
3276
3277   FrameCounter = 0;
3278   TimeFrames = 0;
3279   TimePlayed = 0;
3280   TimeLeft = level.time;
3281   TapeTime = 0;
3282
3283   ScreenMovDir = MV_NONE;
3284   ScreenMovPos = 0;
3285   ScreenGfxPos = 0;
3286
3287   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3288
3289   AllPlayersGone = FALSE;
3290
3291   game.no_time_limit = (level.time == 0);
3292
3293   game.yamyam_content_nr = 0;
3294   game.robot_wheel_active = FALSE;
3295   game.magic_wall_active = FALSE;
3296   game.magic_wall_time_left = 0;
3297   game.light_time_left = 0;
3298   game.timegate_time_left = 0;
3299   game.switchgate_pos = 0;
3300   game.wind_direction = level.wind_direction_initial;
3301
3302   game.lenses_time_left = 0;
3303   game.magnify_time_left = 0;
3304
3305   game.ball_state = level.ball_state_initial;
3306   game.ball_content_nr = 0;
3307
3308   game.envelope_active = FALSE;
3309
3310   /* set focus to local player for network games, else to all players */
3311   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3312   game.centered_player_nr_next = game.centered_player_nr;
3313   game.set_centered_player = FALSE;
3314
3315   if (network_playing && tape.recording)
3316   {
3317     /* store client dependent player focus when recording network games */
3318     tape.centered_player_nr_next = game.centered_player_nr_next;
3319     tape.set_centered_player = TRUE;
3320   }
3321
3322   for (i = 0; i < NUM_BELTS; i++)
3323   {
3324     game.belt_dir[i] = MV_NONE;
3325     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3326   }
3327
3328   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3329     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3330
3331 #if DEBUG_INIT_PLAYER
3332   if (options.debug)
3333   {
3334     printf("Player status at level initialization:\n");
3335   }
3336 #endif
3337
3338   SCAN_PLAYFIELD(x, y)
3339   {
3340     Feld[x][y] = level.field[x][y];
3341     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3342     ChangeDelay[x][y] = 0;
3343     ChangePage[x][y] = -1;
3344     CustomValue[x][y] = 0;              /* initialized in InitField() */
3345     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3346     AmoebaNr[x][y] = 0;
3347     WasJustMoving[x][y] = 0;
3348     WasJustFalling[x][y] = 0;
3349     CheckCollision[x][y] = 0;
3350     CheckImpact[x][y] = 0;
3351     Stop[x][y] = FALSE;
3352     Pushed[x][y] = FALSE;
3353
3354     ChangeCount[x][y] = 0;
3355     ChangeEvent[x][y] = -1;
3356
3357     ExplodePhase[x][y] = 0;
3358     ExplodeDelay[x][y] = 0;
3359     ExplodeField[x][y] = EX_TYPE_NONE;
3360
3361     RunnerVisit[x][y] = 0;
3362     PlayerVisit[x][y] = 0;
3363
3364     GfxFrame[x][y] = 0;
3365     GfxRandom[x][y] = INIT_GFX_RANDOM();
3366     GfxElement[x][y] = EL_UNDEFINED;
3367     GfxAction[x][y] = ACTION_DEFAULT;
3368     GfxDir[x][y] = MV_NONE;
3369     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3370   }
3371
3372   SCAN_PLAYFIELD(x, y)
3373   {
3374     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3375       emulate_bd = FALSE;
3376     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3377       emulate_sb = FALSE;
3378     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3379       emulate_sp = FALSE;
3380
3381     InitField(x, y, TRUE);
3382
3383     ResetGfxAnimation(x, y);
3384   }
3385
3386   InitBeltMovement();
3387
3388   for (i = 0; i < MAX_PLAYERS; i++)
3389   {
3390     struct PlayerInfo *player = &stored_player[i];
3391
3392     /* set number of special actions for bored and sleeping animation */
3393     player->num_special_action_bored =
3394       get_num_special_action(player->artwork_element,
3395                              ACTION_BORING_1, ACTION_BORING_LAST);
3396     player->num_special_action_sleeping =
3397       get_num_special_action(player->artwork_element,
3398                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3399   }
3400
3401   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3402                     emulate_sb ? EMU_SOKOBAN :
3403                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3404
3405   /* initialize type of slippery elements */
3406   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3407   {
3408     if (!IS_CUSTOM_ELEMENT(i))
3409     {
3410       /* default: elements slip down either to the left or right randomly */
3411       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3412
3413       /* SP style elements prefer to slip down on the left side */
3414       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3415         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3416
3417       /* BD style elements prefer to slip down on the left side */
3418       if (game.emulation == EMU_BOULDERDASH)
3419         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3420     }
3421   }
3422
3423   /* initialize explosion and ignition delay */
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425   {
3426     if (!IS_CUSTOM_ELEMENT(i))
3427     {
3428       int num_phase = 8;
3429       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3430                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3431                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3432       int last_phase = (num_phase + 1) * delay;
3433       int half_phase = (num_phase / 2) * delay;
3434
3435       element_info[i].explosion_delay = last_phase - 1;
3436       element_info[i].ignition_delay = half_phase;
3437
3438       if (i == EL_BLACK_ORB)
3439         element_info[i].ignition_delay = 1;
3440     }
3441   }
3442
3443   /* correct non-moving belts to start moving left */
3444   for (i = 0; i < NUM_BELTS; i++)
3445     if (game.belt_dir[i] == MV_NONE)
3446       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3447
3448 #if USE_NEW_PLAYER_ASSIGNMENTS
3449   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3450   /* choose default local player */
3451   local_player = &stored_player[0];
3452
3453   for (i = 0; i < MAX_PLAYERS; i++)
3454     stored_player[i].connected = FALSE;
3455
3456   local_player->connected = TRUE;
3457   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3458
3459   if (tape.playing)
3460   {
3461     for (i = 0; i < MAX_PLAYERS; i++)
3462       stored_player[i].connected = tape.player_participates[i];
3463   }
3464   else if (game.team_mode && !options.network)
3465   {
3466     /* try to guess locally connected team mode players (needed for correct
3467        assignment of player figures from level to locally playing players) */
3468
3469     for (i = 0; i < MAX_PLAYERS; i++)
3470       if (setup.input[i].use_joystick ||
3471           setup.input[i].key.left != KSYM_UNDEFINED)
3472         stored_player[i].connected = TRUE;
3473   }
3474
3475 #if DEBUG_INIT_PLAYER
3476   if (options.debug)
3477   {
3478     printf("Player status after level initialization:\n");
3479
3480     for (i = 0; i < MAX_PLAYERS; i++)
3481     {
3482       struct PlayerInfo *player = &stored_player[i];
3483
3484       printf("- player %d: present == %d, connected == %d, active == %d",
3485              i + 1,
3486              player->present,
3487              player->connected,
3488              player->active);
3489
3490       if (local_player == player)
3491         printf(" (local player)");
3492
3493       printf("\n");
3494     }
3495   }
3496 #endif
3497
3498 #if DEBUG_INIT_PLAYER
3499   if (options.debug)
3500     printf("Reassigning players ...\n");
3501 #endif
3502
3503   /* check if any connected player was not found in playfield */
3504   for (i = 0; i < MAX_PLAYERS; i++)
3505   {
3506     struct PlayerInfo *player = &stored_player[i];
3507
3508     if (player->connected && !player->present)
3509     {
3510       struct PlayerInfo *field_player = NULL;
3511
3512 #if DEBUG_INIT_PLAYER
3513       if (options.debug)
3514         printf("- looking for field player for player %d ...\n", i + 1);
3515 #endif
3516
3517       /* assign first free player found that is present in the playfield */
3518
3519       /* first try: look for unmapped playfield player that is not connected */
3520       for (j = 0; j < MAX_PLAYERS; j++)
3521         if (field_player == NULL &&
3522             stored_player[j].present &&
3523             !stored_player[j].mapped &&
3524             !stored_player[j].connected)
3525           field_player = &stored_player[j];
3526
3527       /* second try: look for *any* unmapped playfield player */
3528       for (j = 0; j < MAX_PLAYERS; j++)
3529         if (field_player == NULL &&
3530             stored_player[j].present &&
3531             !stored_player[j].mapped)
3532           field_player = &stored_player[j];
3533
3534       if (field_player != NULL)
3535       {
3536         int jx = field_player->jx, jy = field_player->jy;
3537
3538 #if DEBUG_INIT_PLAYER
3539         if (options.debug)
3540           printf("- found player %d\n", field_player->index_nr + 1);
3541 #endif
3542
3543         player->present = FALSE;
3544         player->active = FALSE;
3545
3546         field_player->present = TRUE;
3547         field_player->active = TRUE;
3548
3549         /*
3550         player->initial_element = field_player->initial_element;
3551         player->artwork_element = field_player->artwork_element;
3552
3553         player->block_last_field       = field_player->block_last_field;
3554         player->block_delay_adjustment = field_player->block_delay_adjustment;
3555         */
3556
3557         StorePlayer[jx][jy] = field_player->element_nr;
3558
3559         field_player->jx = field_player->last_jx = jx;
3560         field_player->jy = field_player->last_jy = jy;
3561
3562         if (local_player == player)
3563           local_player = field_player;
3564
3565         map_player_action[field_player->index_nr] = i;
3566
3567         field_player->mapped = TRUE;
3568
3569 #if DEBUG_INIT_PLAYER
3570         if (options.debug)
3571           printf("- map_player_action[%d] == %d\n",
3572                  field_player->index_nr + 1, i + 1);
3573 #endif
3574       }
3575     }
3576
3577     if (player->connected && player->present)
3578       player->mapped = TRUE;
3579   }
3580
3581 #if DEBUG_INIT_PLAYER
3582   if (options.debug)
3583   {
3584     printf("Player status after player assignment (first stage):\n");
3585
3586     for (i = 0; i < MAX_PLAYERS; i++)
3587     {
3588       struct PlayerInfo *player = &stored_player[i];
3589
3590       printf("- player %d: present == %d, connected == %d, active == %d",
3591              i + 1,
3592              player->present,
3593              player->connected,
3594              player->active);
3595
3596       if (local_player == player)
3597         printf(" (local player)");
3598
3599       printf("\n");
3600     }
3601   }
3602 #endif
3603
3604 #else
3605
3606   /* check if any connected player was not found in playfield */
3607   for (i = 0; i < MAX_PLAYERS; i++)
3608   {
3609     struct PlayerInfo *player = &stored_player[i];
3610
3611     if (player->connected && !player->present)
3612     {
3613       for (j = 0; j < MAX_PLAYERS; j++)
3614       {
3615         struct PlayerInfo *field_player = &stored_player[j];
3616         int jx = field_player->jx, jy = field_player->jy;
3617
3618         /* assign first free player found that is present in the playfield */
3619         if (field_player->present && !field_player->connected)
3620         {
3621           player->present = TRUE;
3622           player->active = TRUE;
3623
3624           field_player->present = FALSE;
3625           field_player->active = FALSE;
3626
3627           player->initial_element = field_player->initial_element;
3628           player->artwork_element = field_player->artwork_element;
3629
3630           player->block_last_field       = field_player->block_last_field;
3631           player->block_delay_adjustment = field_player->block_delay_adjustment;
3632
3633           StorePlayer[jx][jy] = player->element_nr;
3634
3635           player->jx = player->last_jx = jx;
3636           player->jy = player->last_jy = jy;
3637
3638           break;
3639         }
3640       }
3641     }
3642   }
3643 #endif
3644
3645 #if 0
3646   printf("::: local_player->present == %d\n", local_player->present);
3647 #endif
3648
3649   if (tape.playing)
3650   {
3651     /* when playing a tape, eliminate all players who do not participate */
3652
3653 #if USE_NEW_PLAYER_ASSIGNMENTS
3654
3655     if (!game.team_mode)
3656     {
3657       for (i = 0; i < MAX_PLAYERS; i++)
3658       {
3659         if (stored_player[i].active &&
3660             !tape.player_participates[map_player_action[i]])
3661         {
3662           struct PlayerInfo *player = &stored_player[i];
3663           int jx = player->jx, jy = player->jy;
3664
3665 #if DEBUG_INIT_PLAYER
3666           if (options.debug)
3667             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3668 #endif
3669
3670           player->active = FALSE;
3671           StorePlayer[jx][jy] = 0;
3672           Feld[jx][jy] = EL_EMPTY;
3673         }
3674       }
3675     }
3676
3677 #else
3678
3679     for (i = 0; i < MAX_PLAYERS; i++)
3680     {
3681       if (stored_player[i].active &&
3682           !tape.player_participates[i])
3683       {
3684         struct PlayerInfo *player = &stored_player[i];
3685         int jx = player->jx, jy = player->jy;
3686
3687         player->active = FALSE;
3688         StorePlayer[jx][jy] = 0;
3689         Feld[jx][jy] = EL_EMPTY;
3690       }
3691     }
3692 #endif
3693   }
3694   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3695   {
3696     /* when in single player mode, eliminate all but the first active player */
3697
3698     for (i = 0; i < MAX_PLAYERS; i++)
3699     {
3700       if (stored_player[i].active)
3701       {
3702         for (j = i + 1; j < MAX_PLAYERS; j++)
3703         {
3704           if (stored_player[j].active)
3705           {
3706             struct PlayerInfo *player = &stored_player[j];
3707             int jx = player->jx, jy = player->jy;
3708
3709             player->active = FALSE;
3710             player->present = FALSE;
3711
3712             StorePlayer[jx][jy] = 0;
3713             Feld[jx][jy] = EL_EMPTY;
3714           }
3715         }
3716       }
3717     }
3718   }
3719
3720   /* when recording the game, store which players take part in the game */
3721   if (tape.recording)
3722   {
3723 #if USE_NEW_PLAYER_ASSIGNMENTS
3724     for (i = 0; i < MAX_PLAYERS; i++)
3725       if (stored_player[i].connected)
3726         tape.player_participates[i] = TRUE;
3727 #else
3728     for (i = 0; i < MAX_PLAYERS; i++)
3729       if (stored_player[i].active)
3730         tape.player_participates[i] = TRUE;
3731 #endif
3732   }
3733
3734 #if DEBUG_INIT_PLAYER
3735   if (options.debug)
3736   {
3737     printf("Player status after player assignment (final stage):\n");
3738
3739     for (i = 0; i < MAX_PLAYERS; i++)
3740     {
3741       struct PlayerInfo *player = &stored_player[i];
3742
3743       printf("- player %d: present == %d, connected == %d, active == %d",
3744              i + 1,
3745              player->present,
3746              player->connected,
3747              player->active);
3748
3749       if (local_player == player)
3750         printf(" (local player)");
3751
3752       printf("\n");
3753     }
3754   }
3755 #endif
3756
3757   if (BorderElement == EL_EMPTY)
3758   {
3759     SBX_Left = 0;
3760     SBX_Right = lev_fieldx - SCR_FIELDX;
3761     SBY_Upper = 0;
3762     SBY_Lower = lev_fieldy - SCR_FIELDY;
3763   }
3764   else
3765   {
3766     SBX_Left = -1;
3767     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3768     SBY_Upper = -1;
3769     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3770   }
3771
3772   if (full_lev_fieldx <= SCR_FIELDX)
3773     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3774   if (full_lev_fieldy <= SCR_FIELDY)
3775     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3776
3777   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3778     SBX_Left--;
3779   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3780     SBY_Upper--;
3781
3782   /* if local player not found, look for custom element that might create
3783      the player (make some assumptions about the right custom element) */
3784   if (!local_player->present)
3785   {
3786     int start_x = 0, start_y = 0;
3787     int found_rating = 0;
3788     int found_element = EL_UNDEFINED;
3789     int player_nr = local_player->index_nr;
3790
3791     SCAN_PLAYFIELD(x, y)
3792     {
3793       int element = Feld[x][y];
3794       int content;
3795       int xx, yy;
3796       boolean is_player;
3797
3798       if (level.use_start_element[player_nr] &&
3799           level.start_element[player_nr] == element &&
3800           found_rating < 4)
3801       {
3802         start_x = x;
3803         start_y = y;
3804
3805         found_rating = 4;
3806         found_element = element;
3807       }
3808
3809       if (!IS_CUSTOM_ELEMENT(element))
3810         continue;
3811
3812       if (CAN_CHANGE(element))
3813       {
3814         for (i = 0; i < element_info[element].num_change_pages; i++)
3815         {
3816           /* check for player created from custom element as single target */
3817           content = element_info[element].change_page[i].target_element;
3818           is_player = ELEM_IS_PLAYER(content);
3819
3820           if (is_player && (found_rating < 3 ||
3821                             (found_rating == 3 && element < found_element)))
3822           {
3823             start_x = x;
3824             start_y = y;
3825
3826             found_rating = 3;
3827             found_element = element;
3828           }
3829         }
3830       }
3831
3832       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3833       {
3834         /* check for player created from custom element as explosion content */
3835         content = element_info[element].content.e[xx][yy];
3836         is_player = ELEM_IS_PLAYER(content);
3837
3838         if (is_player && (found_rating < 2 ||
3839                           (found_rating == 2 && element < found_element)))
3840         {
3841           start_x = x + xx - 1;
3842           start_y = y + yy - 1;
3843
3844           found_rating = 2;
3845           found_element = element;
3846         }
3847
3848         if (!CAN_CHANGE(element))
3849           continue;
3850
3851         for (i = 0; i < element_info[element].num_change_pages; i++)
3852         {
3853           /* check for player created from custom element as extended target */
3854           content =
3855             element_info[element].change_page[i].target_content.e[xx][yy];
3856
3857           is_player = ELEM_IS_PLAYER(content);
3858
3859           if (is_player && (found_rating < 1 ||
3860                             (found_rating == 1 && element < found_element)))
3861           {
3862             start_x = x + xx - 1;
3863             start_y = y + yy - 1;
3864
3865             found_rating = 1;
3866             found_element = element;
3867           }
3868         }
3869       }
3870     }
3871
3872     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3873                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3874                 start_x - MIDPOSX);
3875
3876     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3877                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3878                 start_y - MIDPOSY);
3879   }
3880   else
3881   {
3882     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3883                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3884                 local_player->jx - MIDPOSX);
3885
3886     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3887                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3888                 local_player->jy - MIDPOSY);
3889   }
3890
3891   /* !!! FIX THIS (START) !!! */
3892   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3893   {
3894     InitGameEngine_EM();
3895   }
3896   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3897   {
3898     InitGameEngine_SP();
3899   }
3900   else
3901   {
3902     DrawLevel(REDRAW_FIELD);
3903     DrawAllPlayers();
3904
3905     /* after drawing the level, correct some elements */
3906     if (game.timegate_time_left == 0)
3907       CloseAllOpenTimegates();
3908   }
3909
3910   /* blit playfield from scroll buffer to normal back buffer for fading in */
3911   BlitScreenToBitmap(backbuffer);
3912
3913   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3914   /* !!! FIX THIS (END) !!! */
3915
3916   FadeIn(REDRAW_FIELD);
3917
3918   if (!game.restart_level)
3919   {
3920     /* copy default game door content to main double buffer */
3921
3922     /* !!! CHECK AGAIN !!! */
3923     SetPanelBackground();
3924     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3925     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3926   }
3927
3928   SetPanelBackground();
3929   SetDrawBackgroundMask(REDRAW_DOOR_1);
3930
3931   UpdateAndDisplayGameControlValues();
3932
3933   if (!game.restart_level)
3934   {
3935     UnmapGameButtons();
3936     UnmapTapeButtons();
3937     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3938     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3939     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3940     MapGameButtons();
3941     MapTapeButtons();
3942
3943     /* copy actual game door content to door double buffer for OpenDoor() */
3944     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3945
3946     OpenDoor(DOOR_OPEN_ALL);
3947
3948     PlaySound(SND_GAME_STARTING);
3949
3950     if (setup.sound_music)
3951       PlayLevelMusic();
3952
3953     KeyboardAutoRepeatOffUnlessAutoplay();
3954
3955 #if DEBUG_INIT_PLAYER
3956     if (options.debug)
3957     {
3958       printf("Player status (final):\n");
3959
3960       for (i = 0; i < MAX_PLAYERS; i++)
3961       {
3962         struct PlayerInfo *player = &stored_player[i];
3963
3964         printf("- player %d: present == %d, connected == %d, active == %d",
3965                i + 1,
3966                player->present,
3967                player->connected,
3968                player->active);
3969
3970         if (local_player == player)
3971           printf(" (local player)");
3972
3973         printf("\n");
3974       }
3975     }
3976 #endif
3977   }
3978
3979   UnmapAllGadgets();
3980
3981   MapGameButtons();
3982   MapTapeButtons();
3983
3984   if (!game.restart_level && !tape.playing)
3985   {
3986     LevelStats_incPlayed(level_nr);
3987
3988     SaveLevelSetup_SeriesInfo();
3989   }
3990
3991   game.restart_level = FALSE;
3992 }
3993
3994 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3995 {
3996   /* this is used for non-R'n'D game engines to update certain engine values */
3997
3998   /* needed to determine if sounds are played within the visible screen area */
3999   scroll_x = actual_scroll_x;
4000   scroll_y = actual_scroll_y;
4001 }
4002
4003 void InitMovDir(int x, int y)
4004 {
4005   int i, element = Feld[x][y];
4006   static int xy[4][2] =
4007   {
4008     {  0, +1 },
4009     { +1,  0 },
4010     {  0, -1 },
4011     { -1,  0 }
4012   };
4013   static int direction[3][4] =
4014   {
4015     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4016     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4017     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4018   };
4019
4020   switch (element)
4021   {
4022     case EL_BUG_RIGHT:
4023     case EL_BUG_UP:
4024     case EL_BUG_LEFT:
4025     case EL_BUG_DOWN:
4026       Feld[x][y] = EL_BUG;
4027       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4028       break;
4029
4030     case EL_SPACESHIP_RIGHT:
4031     case EL_SPACESHIP_UP:
4032     case EL_SPACESHIP_LEFT:
4033     case EL_SPACESHIP_DOWN:
4034       Feld[x][y] = EL_SPACESHIP;
4035       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4036       break;
4037
4038     case EL_BD_BUTTERFLY_RIGHT:
4039     case EL_BD_BUTTERFLY_UP:
4040     case EL_BD_BUTTERFLY_LEFT:
4041     case EL_BD_BUTTERFLY_DOWN:
4042       Feld[x][y] = EL_BD_BUTTERFLY;
4043       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4044       break;
4045
4046     case EL_BD_FIREFLY_RIGHT:
4047     case EL_BD_FIREFLY_UP:
4048     case EL_BD_FIREFLY_LEFT:
4049     case EL_BD_FIREFLY_DOWN:
4050       Feld[x][y] = EL_BD_FIREFLY;
4051       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4052       break;
4053
4054     case EL_PACMAN_RIGHT:
4055     case EL_PACMAN_UP:
4056     case EL_PACMAN_LEFT:
4057     case EL_PACMAN_DOWN:
4058       Feld[x][y] = EL_PACMAN;
4059       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4060       break;
4061
4062     case EL_YAMYAM_LEFT:
4063     case EL_YAMYAM_RIGHT:
4064     case EL_YAMYAM_UP:
4065     case EL_YAMYAM_DOWN:
4066       Feld[x][y] = EL_YAMYAM;
4067       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4068       break;
4069
4070     case EL_SP_SNIKSNAK:
4071       MovDir[x][y] = MV_UP;
4072       break;
4073
4074     case EL_SP_ELECTRON:
4075       MovDir[x][y] = MV_LEFT;
4076       break;
4077
4078     case EL_MOLE_LEFT:
4079     case EL_MOLE_RIGHT:
4080     case EL_MOLE_UP:
4081     case EL_MOLE_DOWN:
4082       Feld[x][y] = EL_MOLE;
4083       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4084       break;
4085
4086     default:
4087       if (IS_CUSTOM_ELEMENT(element))
4088       {
4089         struct ElementInfo *ei = &element_info[element];
4090         int move_direction_initial = ei->move_direction_initial;
4091         int move_pattern = ei->move_pattern;
4092
4093         if (move_direction_initial == MV_START_PREVIOUS)
4094         {
4095           if (MovDir[x][y] != MV_NONE)
4096             return;
4097
4098           move_direction_initial = MV_START_AUTOMATIC;
4099         }
4100
4101         if (move_direction_initial == MV_START_RANDOM)
4102           MovDir[x][y] = 1 << RND(4);
4103         else if (move_direction_initial & MV_ANY_DIRECTION)
4104           MovDir[x][y] = move_direction_initial;
4105         else if (move_pattern == MV_ALL_DIRECTIONS ||
4106                  move_pattern == MV_TURNING_LEFT ||
4107                  move_pattern == MV_TURNING_RIGHT ||
4108                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4109                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4110                  move_pattern == MV_TURNING_RANDOM)
4111           MovDir[x][y] = 1 << RND(4);
4112         else if (move_pattern == MV_HORIZONTAL)
4113           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4114         else if (move_pattern == MV_VERTICAL)
4115           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4116         else if (move_pattern & MV_ANY_DIRECTION)
4117           MovDir[x][y] = element_info[element].move_pattern;
4118         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4119                  move_pattern == MV_ALONG_RIGHT_SIDE)
4120         {
4121           /* use random direction as default start direction */
4122           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4123             MovDir[x][y] = 1 << RND(4);
4124
4125           for (i = 0; i < NUM_DIRECTIONS; i++)
4126           {
4127             int x1 = x + xy[i][0];
4128             int y1 = y + xy[i][1];
4129
4130             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4131             {
4132               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4133                 MovDir[x][y] = direction[0][i];
4134               else
4135                 MovDir[x][y] = direction[1][i];
4136
4137               break;
4138             }
4139           }
4140         }                
4141       }
4142       else
4143       {
4144         MovDir[x][y] = 1 << RND(4);
4145
4146         if (element != EL_BUG &&
4147             element != EL_SPACESHIP &&
4148             element != EL_BD_BUTTERFLY &&
4149             element != EL_BD_FIREFLY)
4150           break;
4151
4152         for (i = 0; i < NUM_DIRECTIONS; i++)
4153         {
4154           int x1 = x + xy[i][0];
4155           int y1 = y + xy[i][1];
4156
4157           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4158           {
4159             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4160             {
4161               MovDir[x][y] = direction[0][i];
4162               break;
4163             }
4164             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4165                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4166             {
4167               MovDir[x][y] = direction[1][i];
4168               break;
4169             }
4170           }
4171         }
4172       }
4173       break;
4174   }
4175
4176   GfxDir[x][y] = MovDir[x][y];
4177 }
4178
4179 void InitAmoebaNr(int x, int y)
4180 {
4181   int i;
4182   int group_nr = AmoebeNachbarNr(x, y);
4183
4184   if (group_nr == 0)
4185   {
4186     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4187     {
4188       if (AmoebaCnt[i] == 0)
4189       {
4190         group_nr = i;
4191         break;
4192       }
4193     }
4194   }
4195
4196   AmoebaNr[x][y] = group_nr;
4197   AmoebaCnt[group_nr]++;
4198   AmoebaCnt2[group_nr]++;
4199 }
4200
4201 static void PlayerWins(struct PlayerInfo *player)
4202 {
4203   player->LevelSolved = TRUE;
4204   player->GameOver = TRUE;
4205
4206   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4207                          level.native_em_level->lev->score : player->score);
4208
4209   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4210                                       TimeLeft);
4211   player->LevelSolved_CountingScore = player->score_final;
4212 }
4213
4214 void GameWon()
4215 {
4216   static int time, time_final;
4217   static int score, score_final;
4218   static int game_over_delay_1 = 0;
4219   static int game_over_delay_2 = 0;
4220   int game_over_delay_value_1 = 50;
4221   int game_over_delay_value_2 = 50;
4222
4223   if (!local_player->LevelSolved_GameWon)
4224   {
4225     int i;
4226
4227     /* do not start end game actions before the player stops moving (to exit) */
4228     if (local_player->MovPos)
4229       return;
4230
4231     local_player->LevelSolved_GameWon = TRUE;
4232     local_player->LevelSolved_SaveTape = tape.recording;
4233     local_player->LevelSolved_SaveScore = !tape.playing;
4234
4235     if (!tape.playing)
4236     {
4237       LevelStats_incSolved(level_nr);
4238
4239       SaveLevelSetup_SeriesInfo();
4240     }
4241
4242     if (tape.auto_play)         /* tape might already be stopped here */
4243       tape.auto_play_level_solved = TRUE;
4244
4245     TapeStop();
4246
4247     game_over_delay_1 = game_over_delay_value_1;
4248     game_over_delay_2 = game_over_delay_value_2;
4249
4250     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4251     score = score_final = local_player->score_final;
4252
4253     if (TimeLeft > 0)
4254     {
4255       time_final = 0;
4256       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4257     }
4258     else if (game.no_time_limit && TimePlayed < 999)
4259     {
4260       time_final = 999;
4261       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4262     }
4263
4264     local_player->score_final = score_final;
4265
4266     if (level_editor_test_game)
4267     {
4268       time = time_final;
4269       score = score_final;
4270
4271       local_player->LevelSolved_CountingTime = time;
4272       local_player->LevelSolved_CountingScore = score;
4273
4274       game_panel_controls[GAME_PANEL_TIME].value = time;
4275       game_panel_controls[GAME_PANEL_SCORE].value = score;
4276
4277       DisplayGameControlValues();
4278     }
4279
4280     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4281     {
4282       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4283       {
4284         /* close exit door after last player */
4285         if ((AllPlayersGone &&
4286              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4287               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4288               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4289             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4290             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4291         {
4292           int element = Feld[ExitX][ExitY];
4293
4294           Feld[ExitX][ExitY] =
4295             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4296              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4297              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4298              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4299              EL_EM_STEEL_EXIT_CLOSING);
4300
4301           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4302         }
4303
4304         /* player disappears */
4305         DrawLevelField(ExitX, ExitY);
4306       }
4307
4308       for (i = 0; i < MAX_PLAYERS; i++)
4309       {
4310         struct PlayerInfo *player = &stored_player[i];
4311
4312         if (player->present)
4313         {
4314           RemovePlayer(player);
4315
4316           /* player disappears */
4317           DrawLevelField(player->jx, player->jy);
4318         }
4319       }
4320     }
4321
4322     PlaySound(SND_GAME_WINNING);
4323   }
4324
4325   if (game_over_delay_1 > 0)
4326   {
4327     game_over_delay_1--;
4328
4329     return;
4330   }
4331
4332   if (time != time_final)
4333   {
4334     int time_to_go = ABS(time_final - time);
4335     int time_count_dir = (time < time_final ? +1 : -1);
4336     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4337
4338     time  += time_count_steps * time_count_dir;
4339     score += time_count_steps * level.score[SC_TIME_BONUS];
4340
4341     local_player->LevelSolved_CountingTime = time;
4342     local_player->LevelSolved_CountingScore = score;
4343
4344     game_panel_controls[GAME_PANEL_TIME].value = time;
4345     game_panel_controls[GAME_PANEL_SCORE].value = score;
4346
4347     DisplayGameControlValues();
4348
4349     if (time == time_final)
4350       StopSound(SND_GAME_LEVELTIME_BONUS);
4351     else if (setup.sound_loops)
4352       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4353     else
4354       PlaySound(SND_GAME_LEVELTIME_BONUS);
4355
4356     return;
4357   }
4358
4359   local_player->LevelSolved_PanelOff = TRUE;
4360
4361   if (game_over_delay_2 > 0)
4362   {
4363     game_over_delay_2--;
4364
4365     return;
4366   }
4367
4368   GameEnd();
4369 }
4370
4371 void GameEnd()
4372 {
4373   int hi_pos;
4374   boolean raise_level = FALSE;
4375
4376   local_player->LevelSolved_GameEnd = TRUE;
4377
4378   CloseDoor(DOOR_CLOSE_1);
4379
4380   if (local_player->LevelSolved_SaveTape)
4381   {
4382     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4383   }
4384
4385   if (level_editor_test_game)
4386   {
4387     game_status = GAME_MODE_MAIN;
4388
4389     DrawAndFadeInMainMenu(REDRAW_FIELD);
4390
4391     return;
4392   }
4393
4394   if (!local_player->LevelSolved_SaveScore)
4395   {
4396     FadeOut(REDRAW_FIELD);
4397
4398     game_status = GAME_MODE_MAIN;
4399
4400     DrawAndFadeInMainMenu(REDRAW_FIELD);
4401
4402     return;
4403   }
4404
4405   if (level_nr == leveldir_current->handicap_level)
4406   {
4407     leveldir_current->handicap_level++;
4408
4409     SaveLevelSetup_SeriesInfo();
4410   }
4411
4412   if (level_nr < leveldir_current->last_level)
4413     raise_level = TRUE;                 /* advance to next level */
4414
4415   if ((hi_pos = NewHiScore()) >= 0) 
4416   {
4417     game_status = GAME_MODE_SCORES;
4418
4419     DrawHallOfFame(hi_pos);
4420
4421     if (raise_level)
4422     {
4423       level_nr++;
4424       TapeErase();
4425     }
4426   }
4427   else
4428   {
4429     FadeOut(REDRAW_FIELD);
4430
4431     game_status = GAME_MODE_MAIN;
4432
4433     if (raise_level)
4434     {
4435       level_nr++;
4436       TapeErase();
4437     }
4438
4439     DrawAndFadeInMainMenu(REDRAW_FIELD);
4440   }
4441 }
4442
4443 int NewHiScore()
4444 {
4445   int k, l;
4446   int position = -1;
4447
4448   LoadScore(level_nr);
4449
4450   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4451       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4452     return -1;
4453
4454   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4455   {
4456     if (local_player->score_final > highscore[k].Score)
4457     {
4458       /* player has made it to the hall of fame */
4459
4460       if (k < MAX_SCORE_ENTRIES - 1)
4461       {
4462         int m = MAX_SCORE_ENTRIES - 1;
4463
4464 #ifdef ONE_PER_NAME
4465         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4466           if (strEqual(setup.player_name, highscore[l].Name))
4467             m = l;
4468         if (m == k)     /* player's new highscore overwrites his old one */
4469           goto put_into_list;
4470 #endif
4471
4472         for (l = m; l > k; l--)
4473         {
4474           strcpy(highscore[l].Name, highscore[l - 1].Name);
4475           highscore[l].Score = highscore[l - 1].Score;
4476         }
4477       }
4478
4479 #ifdef ONE_PER_NAME
4480       put_into_list:
4481 #endif
4482       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4483       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4484       highscore[k].Score = local_player->score_final; 
4485       position = k;
4486       break;
4487     }
4488
4489 #ifdef ONE_PER_NAME
4490     else if (!strncmp(setup.player_name, highscore[k].Name,
4491                       MAX_PLAYER_NAME_LEN))
4492       break;    /* player already there with a higher score */
4493 #endif
4494
4495   }
4496
4497   if (position >= 0) 
4498     SaveScore(level_nr);
4499
4500   return position;
4501 }
4502
4503 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4504 {
4505   int element = Feld[x][y];
4506   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4507   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4508   int horiz_move = (dx != 0);
4509   int sign = (horiz_move ? dx : dy);
4510   int step = sign * element_info[element].move_stepsize;
4511
4512   /* special values for move stepsize for spring and things on conveyor belt */
4513   if (horiz_move)
4514   {
4515     if (CAN_FALL(element) &&
4516         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4517       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4518     else if (element == EL_SPRING)
4519       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4520   }
4521
4522   return step;
4523 }
4524
4525 inline static int getElementMoveStepsize(int x, int y)
4526 {
4527   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4528 }
4529
4530 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4531 {
4532   if (player->GfxAction != action || player->GfxDir != dir)
4533   {
4534     player->GfxAction = action;
4535     player->GfxDir = dir;
4536     player->Frame = 0;
4537     player->StepFrame = 0;
4538   }
4539 }
4540
4541 static void ResetGfxFrame(int x, int y, boolean redraw)
4542 {
4543   int element = Feld[x][y];
4544   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4545   int last_gfx_frame = GfxFrame[x][y];
4546
4547   if (graphic_info[graphic].anim_global_sync)
4548     GfxFrame[x][y] = FrameCounter;
4549   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4550     GfxFrame[x][y] = CustomValue[x][y];
4551   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4552     GfxFrame[x][y] = element_info[element].collect_score;
4553   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4554     GfxFrame[x][y] = ChangeDelay[x][y];
4555
4556   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4557     DrawLevelGraphicAnimation(x, y, graphic);
4558 }
4559
4560 static void ResetGfxAnimation(int x, int y)
4561 {
4562   GfxAction[x][y] = ACTION_DEFAULT;
4563   GfxDir[x][y] = MovDir[x][y];
4564   GfxFrame[x][y] = 0;
4565
4566   ResetGfxFrame(x, y, FALSE);
4567 }
4568
4569 static void ResetRandomAnimationValue(int x, int y)
4570 {
4571   GfxRandom[x][y] = INIT_GFX_RANDOM();
4572 }
4573
4574 void InitMovingField(int x, int y, int direction)
4575 {
4576   int element = Feld[x][y];
4577   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4578   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4579   int newx = x + dx;
4580   int newy = y + dy;
4581   boolean is_moving_before, is_moving_after;
4582
4583   /* check if element was/is moving or being moved before/after mode change */
4584   is_moving_before = (WasJustMoving[x][y] != 0);
4585   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4586
4587   /* reset animation only for moving elements which change direction of moving
4588      or which just started or stopped moving
4589      (else CEs with property "can move" / "not moving" are reset each frame) */
4590   if (is_moving_before != is_moving_after ||
4591       direction != MovDir[x][y])
4592     ResetGfxAnimation(x, y);
4593
4594   MovDir[x][y] = direction;
4595   GfxDir[x][y] = direction;
4596
4597   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4598                      direction == MV_DOWN && CAN_FALL(element) ?
4599                      ACTION_FALLING : ACTION_MOVING);
4600
4601   /* this is needed for CEs with property "can move" / "not moving" */
4602
4603   if (is_moving_after)
4604   {
4605     if (Feld[newx][newy] == EL_EMPTY)
4606       Feld[newx][newy] = EL_BLOCKED;
4607
4608     MovDir[newx][newy] = MovDir[x][y];
4609
4610     CustomValue[newx][newy] = CustomValue[x][y];
4611
4612     GfxFrame[newx][newy] = GfxFrame[x][y];
4613     GfxRandom[newx][newy] = GfxRandom[x][y];
4614     GfxAction[newx][newy] = GfxAction[x][y];
4615     GfxDir[newx][newy] = GfxDir[x][y];
4616   }
4617 }
4618
4619 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4620 {
4621   int direction = MovDir[x][y];
4622   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4623   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4624
4625   *goes_to_x = newx;
4626   *goes_to_y = newy;
4627 }
4628
4629 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4630 {
4631   int oldx = x, oldy = y;
4632   int direction = MovDir[x][y];
4633
4634   if (direction == MV_LEFT)
4635     oldx++;
4636   else if (direction == MV_RIGHT)
4637     oldx--;
4638   else if (direction == MV_UP)
4639     oldy++;
4640   else if (direction == MV_DOWN)
4641     oldy--;
4642
4643   *comes_from_x = oldx;
4644   *comes_from_y = oldy;
4645 }
4646
4647 int MovingOrBlocked2Element(int x, int y)
4648 {
4649   int element = Feld[x][y];
4650
4651   if (element == EL_BLOCKED)
4652   {
4653     int oldx, oldy;
4654
4655     Blocked2Moving(x, y, &oldx, &oldy);
4656     return Feld[oldx][oldy];
4657   }
4658   else
4659     return element;
4660 }
4661
4662 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4663 {
4664   /* like MovingOrBlocked2Element(), but if element is moving
4665      and (x,y) is the field the moving element is just leaving,
4666      return EL_BLOCKED instead of the element value */
4667   int element = Feld[x][y];
4668
4669   if (IS_MOVING(x, y))
4670   {
4671     if (element == EL_BLOCKED)
4672     {
4673       int oldx, oldy;
4674
4675       Blocked2Moving(x, y, &oldx, &oldy);
4676       return Feld[oldx][oldy];
4677     }
4678     else
4679       return EL_BLOCKED;
4680   }
4681   else
4682     return element;
4683 }
4684
4685 static void RemoveField(int x, int y)
4686 {
4687   Feld[x][y] = EL_EMPTY;
4688
4689   MovPos[x][y] = 0;
4690   MovDir[x][y] = 0;
4691   MovDelay[x][y] = 0;
4692
4693   CustomValue[x][y] = 0;
4694
4695   AmoebaNr[x][y] = 0;
4696   ChangeDelay[x][y] = 0;
4697   ChangePage[x][y] = -1;
4698   Pushed[x][y] = FALSE;
4699
4700   GfxElement[x][y] = EL_UNDEFINED;
4701   GfxAction[x][y] = ACTION_DEFAULT;
4702   GfxDir[x][y] = MV_NONE;
4703 }
4704
4705 void RemoveMovingField(int x, int y)
4706 {
4707   int oldx = x, oldy = y, newx = x, newy = y;
4708   int element = Feld[x][y];
4709   int next_element = EL_UNDEFINED;
4710
4711   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4712     return;
4713
4714   if (IS_MOVING(x, y))
4715   {
4716     Moving2Blocked(x, y, &newx, &newy);
4717
4718     if (Feld[newx][newy] != EL_BLOCKED)
4719     {
4720       /* element is moving, but target field is not free (blocked), but
4721          already occupied by something different (example: acid pool);
4722          in this case, only remove the moving field, but not the target */
4723
4724       RemoveField(oldx, oldy);
4725
4726       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4727
4728       TEST_DrawLevelField(oldx, oldy);
4729
4730       return;
4731     }
4732   }
4733   else if (element == EL_BLOCKED)
4734   {
4735     Blocked2Moving(x, y, &oldx, &oldy);
4736     if (!IS_MOVING(oldx, oldy))
4737       return;
4738   }
4739
4740   if (element == EL_BLOCKED &&
4741       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4742        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4743        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4744        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4745        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4746        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4747     next_element = get_next_element(Feld[oldx][oldy]);
4748
4749   RemoveField(oldx, oldy);
4750   RemoveField(newx, newy);
4751
4752   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4753
4754   if (next_element != EL_UNDEFINED)
4755     Feld[oldx][oldy] = next_element;
4756
4757   TEST_DrawLevelField(oldx, oldy);
4758   TEST_DrawLevelField(newx, newy);
4759 }
4760
4761 void DrawDynamite(int x, int y)
4762 {
4763   int sx = SCREENX(x), sy = SCREENY(y);
4764   int graphic = el2img(Feld[x][y]);
4765   int frame;
4766
4767   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4768     return;
4769
4770   if (IS_WALKABLE_INSIDE(Back[x][y]))
4771     return;
4772
4773   if (Back[x][y])
4774     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4775   else if (Store[x][y])
4776     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4777
4778   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4779
4780   if (Back[x][y] || Store[x][y])
4781     DrawGraphicThruMask(sx, sy, graphic, frame);
4782   else
4783     DrawGraphic(sx, sy, graphic, frame);
4784 }
4785
4786 void CheckDynamite(int x, int y)
4787 {
4788   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4789   {
4790     MovDelay[x][y]--;
4791
4792     if (MovDelay[x][y] != 0)
4793     {
4794       DrawDynamite(x, y);
4795       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4796
4797       return;
4798     }
4799   }
4800
4801   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4802
4803   Bang(x, y);
4804 }
4805
4806 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4807 {
4808   boolean num_checked_players = 0;
4809   int i;
4810
4811   for (i = 0; i < MAX_PLAYERS; i++)
4812   {
4813     if (stored_player[i].active)
4814     {
4815       int sx = stored_player[i].jx;
4816       int sy = stored_player[i].jy;
4817
4818       if (num_checked_players == 0)
4819       {
4820         *sx1 = *sx2 = sx;
4821         *sy1 = *sy2 = sy;
4822       }
4823       else
4824       {
4825         *sx1 = MIN(*sx1, sx);
4826         *sy1 = MIN(*sy1, sy);
4827         *sx2 = MAX(*sx2, sx);
4828         *sy2 = MAX(*sy2, sy);
4829       }
4830
4831       num_checked_players++;
4832     }
4833   }
4834 }
4835
4836 static boolean checkIfAllPlayersFitToScreen_RND()
4837 {
4838   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4839
4840   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4841
4842   return (sx2 - sx1 < SCR_FIELDX &&
4843           sy2 - sy1 < SCR_FIELDY);
4844 }
4845
4846 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4847 {
4848   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4849
4850   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4851
4852   *sx = (sx1 + sx2) / 2;
4853   *sy = (sy1 + sy2) / 2;
4854 }
4855
4856 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4857                         boolean center_screen, boolean quick_relocation)
4858 {
4859   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4860   boolean no_delay = (tape.warp_forward);
4861   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4862   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4863
4864   if (quick_relocation)
4865   {
4866     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4867     {
4868       if (!level.shifted_relocation || center_screen)
4869       {
4870         /* quick relocation (without scrolling), with centering of screen */
4871
4872         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4873                     x > SBX_Right + MIDPOSX ? SBX_Right :
4874                     x - MIDPOSX);
4875
4876         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4877                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4878                     y - MIDPOSY);
4879       }
4880       else
4881       {
4882         /* quick relocation (without scrolling), but do not center screen */
4883
4884         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4885                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4886                                old_x - MIDPOSX);
4887
4888         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4889                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4890                                old_y - MIDPOSY);
4891
4892         int offset_x = x + (scroll_x - center_scroll_x);
4893         int offset_y = y + (scroll_y - center_scroll_y);
4894
4895         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4896                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4897                     offset_x - MIDPOSX);
4898
4899         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4900                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4901                     offset_y - MIDPOSY);
4902       }
4903     }
4904     else
4905     {
4906       if (!level.shifted_relocation || center_screen)
4907       {
4908         /* quick relocation (without scrolling), with centering of screen */
4909
4910         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4911                     x > SBX_Right + MIDPOSX ? SBX_Right :
4912                     x - MIDPOSX);
4913
4914         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4915                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4916                     y - MIDPOSY);
4917       }
4918       else
4919       {
4920         /* quick relocation (without scrolling), but do not center screen */
4921
4922         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4923                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4924                                old_x - MIDPOSX);
4925
4926         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4927                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4928                                old_y - MIDPOSY);
4929
4930         int offset_x = x + (scroll_x - center_scroll_x);
4931         int offset_y = y + (scroll_y - center_scroll_y);
4932
4933         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4934                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4935                     offset_x - MIDPOSX);
4936
4937         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4938                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4939                     offset_y - MIDPOSY);
4940       }
4941     }
4942
4943     RedrawPlayfield(TRUE, 0,0,0,0);
4944   }
4945   else
4946   {
4947     int scroll_xx, scroll_yy;
4948
4949     if (!level.shifted_relocation || center_screen)
4950     {
4951       /* visible relocation (with scrolling), with centering of screen */
4952
4953       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4954                    x > SBX_Right + MIDPOSX ? SBX_Right :
4955                    x - MIDPOSX);
4956
4957       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4958                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4959                    y - MIDPOSY);
4960     }
4961     else
4962     {
4963       /* visible relocation (with scrolling), but do not center screen */
4964
4965       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4966                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4967                              old_x - MIDPOSX);
4968
4969       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4970                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4971                              old_y - MIDPOSY);
4972
4973       int offset_x = x + (scroll_x - center_scroll_x);
4974       int offset_y = y + (scroll_y - center_scroll_y);
4975
4976       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4977                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4978                    offset_x - MIDPOSX);
4979
4980       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4981                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4982                    offset_y - MIDPOSY);
4983     }
4984
4985
4986     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4987
4988     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4989     {
4990       int dx = 0, dy = 0;
4991       int fx = FX, fy = FY;
4992
4993       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4994       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4995
4996       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4997         break;
4998
4999       scroll_x -= dx;
5000       scroll_y -= dy;
5001
5002       fx += dx * TILEX / 2;
5003       fy += dy * TILEY / 2;
5004
5005       ScrollLevel(dx, dy);
5006       DrawAllPlayers();
5007
5008       /* scroll in two steps of half tile size to make things smoother */
5009       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5010       FlushDisplay();
5011       Delay(wait_delay_value);
5012
5013       /* scroll second step to align at full tile size */
5014       BackToFront();
5015       Delay(wait_delay_value);
5016     }
5017
5018     DrawAllPlayers();
5019     BackToFront();
5020     Delay(wait_delay_value);
5021   }
5022 }
5023
5024 void RelocatePlayer(int jx, int jy, int el_player_raw)
5025 {
5026   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5027   int player_nr = GET_PLAYER_NR(el_player);
5028   struct PlayerInfo *player = &stored_player[player_nr];
5029   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5030   boolean no_delay = (tape.warp_forward);
5031   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5032   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5033   int old_jx = player->jx;
5034   int old_jy = player->jy;
5035   int old_element = Feld[old_jx][old_jy];
5036   int element = Feld[jx][jy];
5037   boolean player_relocated = (old_jx != jx || old_jy != jy);
5038
5039   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5040   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5041   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5042   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5043   int leave_side_horiz = move_dir_horiz;
5044   int leave_side_vert  = move_dir_vert;
5045   int enter_side = enter_side_horiz | enter_side_vert;
5046   int leave_side = leave_side_horiz | leave_side_vert;
5047
5048   if (player->GameOver)         /* do not reanimate dead player */
5049     return;
5050
5051   if (!player_relocated)        /* no need to relocate the player */
5052     return;
5053
5054   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5055   {
5056     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5057     DrawLevelField(jx, jy);
5058   }
5059
5060   if (player->present)
5061   {
5062     while (player->MovPos)
5063     {
5064       ScrollPlayer(player, SCROLL_GO_ON);
5065       ScrollScreen(NULL, SCROLL_GO_ON);
5066
5067       AdvanceFrameAndPlayerCounters(player->index_nr);
5068
5069       DrawPlayer(player);
5070
5071       BackToFront();
5072       Delay(wait_delay_value);
5073     }
5074
5075     DrawPlayer(player);         /* needed here only to cleanup last field */
5076     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5077
5078     player->is_moving = FALSE;
5079   }
5080
5081   if (IS_CUSTOM_ELEMENT(old_element))
5082     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5083                                CE_LEFT_BY_PLAYER,
5084                                player->index_bit, leave_side);
5085
5086   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5087                                       CE_PLAYER_LEAVES_X,
5088                                       player->index_bit, leave_side);
5089
5090   Feld[jx][jy] = el_player;
5091   InitPlayerField(jx, jy, el_player, TRUE);
5092
5093   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5094      possible that the relocation target field did not contain a player element,
5095      but a walkable element, to which the new player was relocated -- in this
5096      case, restore that (already initialized!) element on the player field */
5097   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5098   {
5099     Feld[jx][jy] = element;     /* restore previously existing element */
5100   }
5101
5102   /* only visually relocate centered player */
5103   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5104                      FALSE, level.instant_relocation);
5105
5106   TestIfPlayerTouchesBadThing(jx, jy);
5107   TestIfPlayerTouchesCustomElement(jx, jy);
5108
5109   if (IS_CUSTOM_ELEMENT(element))
5110     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5111                                player->index_bit, enter_side);
5112
5113   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5114                                       player->index_bit, enter_side);
5115
5116   if (player->is_switching)
5117   {
5118     /* ensure that relocation while still switching an element does not cause
5119        a new element to be treated as also switched directly after relocation
5120        (this is important for teleporter switches that teleport the player to
5121        a place where another teleporter switch is in the same direction, which
5122        would then incorrectly be treated as immediately switched before the
5123        direction key that caused the switch was released) */
5124
5125     player->switch_x += jx - old_jx;
5126     player->switch_y += jy - old_jy;
5127   }
5128 }
5129
5130 void Explode(int ex, int ey, int phase, int mode)
5131 {
5132   int x, y;
5133   int last_phase;
5134   int border_element;
5135
5136   /* !!! eliminate this variable !!! */
5137   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5138
5139   if (game.explosions_delayed)
5140   {
5141     ExplodeField[ex][ey] = mode;
5142     return;
5143   }
5144
5145   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5146   {
5147     int center_element = Feld[ex][ey];
5148     int artwork_element, explosion_element;     /* set these values later */
5149
5150     /* remove things displayed in background while burning dynamite */
5151     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5152       Back[ex][ey] = 0;
5153
5154     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5155     {
5156       /* put moving element to center field (and let it explode there) */
5157       center_element = MovingOrBlocked2Element(ex, ey);
5158       RemoveMovingField(ex, ey);
5159       Feld[ex][ey] = center_element;
5160     }
5161
5162     /* now "center_element" is finally determined -- set related values now */
5163     artwork_element = center_element;           /* for custom player artwork */
5164     explosion_element = center_element;         /* for custom player artwork */
5165
5166     if (IS_PLAYER(ex, ey))
5167     {
5168       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5169
5170       artwork_element = stored_player[player_nr].artwork_element;
5171
5172       if (level.use_explosion_element[player_nr])
5173       {
5174         explosion_element = level.explosion_element[player_nr];
5175         artwork_element = explosion_element;
5176       }
5177     }
5178
5179     if (mode == EX_TYPE_NORMAL ||
5180         mode == EX_TYPE_CENTER ||
5181         mode == EX_TYPE_CROSS)
5182       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5183
5184     last_phase = element_info[explosion_element].explosion_delay + 1;
5185
5186     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5187     {
5188       int xx = x - ex + 1;
5189       int yy = y - ey + 1;
5190       int element;
5191
5192       if (!IN_LEV_FIELD(x, y) ||
5193           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5194           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5195         continue;
5196
5197       element = Feld[x][y];
5198
5199       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5200       {
5201         element = MovingOrBlocked2Element(x, y);
5202
5203         if (!IS_EXPLOSION_PROOF(element))
5204           RemoveMovingField(x, y);
5205       }
5206
5207       /* indestructible elements can only explode in center (but not flames) */
5208       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5209                                            mode == EX_TYPE_BORDER)) ||
5210           element == EL_FLAMES)
5211         continue;
5212
5213       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5214          behaviour, for example when touching a yamyam that explodes to rocks
5215          with active deadly shield, a rock is created under the player !!! */
5216       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5217 #if 0
5218       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5219           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5220            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5221 #else
5222       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5223 #endif
5224       {
5225         if (IS_ACTIVE_BOMB(element))
5226         {
5227           /* re-activate things under the bomb like gate or penguin */
5228           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5229           Back[x][y] = 0;
5230         }
5231
5232         continue;
5233       }
5234
5235       /* save walkable background elements while explosion on same tile */
5236       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5237           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5238         Back[x][y] = element;
5239
5240       /* ignite explodable elements reached by other explosion */
5241       if (element == EL_EXPLOSION)
5242         element = Store2[x][y];
5243
5244       if (AmoebaNr[x][y] &&
5245           (element == EL_AMOEBA_FULL ||
5246            element == EL_BD_AMOEBA ||
5247            element == EL_AMOEBA_GROWING))
5248       {
5249         AmoebaCnt[AmoebaNr[x][y]]--;
5250         AmoebaCnt2[AmoebaNr[x][y]]--;
5251       }
5252
5253       RemoveField(x, y);
5254
5255       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5256       {
5257         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5258
5259         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5260
5261         if (PLAYERINFO(ex, ey)->use_murphy)
5262           Store[x][y] = EL_EMPTY;
5263       }
5264
5265       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5266          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5267       else if (ELEM_IS_PLAYER(center_element))
5268         Store[x][y] = EL_EMPTY;
5269       else if (center_element == EL_YAMYAM)
5270         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5271       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5272         Store[x][y] = element_info[center_element].content.e[xx][yy];
5273 #if 1
5274       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5275          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5276          otherwise) -- FIX THIS !!! */
5277       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5278         Store[x][y] = element_info[element].content.e[1][1];
5279 #else
5280       else if (!CAN_EXPLODE(element))
5281         Store[x][y] = element_info[element].content.e[1][1];
5282 #endif
5283       else
5284         Store[x][y] = EL_EMPTY;
5285
5286       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5287           center_element == EL_AMOEBA_TO_DIAMOND)
5288         Store2[x][y] = element;
5289
5290       Feld[x][y] = EL_EXPLOSION;
5291       GfxElement[x][y] = artwork_element;
5292
5293       ExplodePhase[x][y] = 1;
5294       ExplodeDelay[x][y] = last_phase;
5295
5296       Stop[x][y] = TRUE;
5297     }
5298
5299     if (center_element == EL_YAMYAM)
5300       game.yamyam_content_nr =
5301         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5302
5303     return;
5304   }
5305
5306   if (Stop[ex][ey])
5307     return;
5308
5309   x = ex;
5310   y = ey;
5311
5312   if (phase == 1)
5313     GfxFrame[x][y] = 0;         /* restart explosion animation */
5314
5315   last_phase = ExplodeDelay[x][y];
5316
5317   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5318
5319   /* this can happen if the player leaves an explosion just in time */
5320   if (GfxElement[x][y] == EL_UNDEFINED)
5321     GfxElement[x][y] = EL_EMPTY;
5322
5323   border_element = Store2[x][y];
5324   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5325     border_element = StorePlayer[x][y];
5326
5327   if (phase == element_info[border_element].ignition_delay ||
5328       phase == last_phase)
5329   {
5330     boolean border_explosion = FALSE;
5331
5332     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5333         !PLAYER_EXPLOSION_PROTECTED(x, y))
5334     {
5335       KillPlayerUnlessExplosionProtected(x, y);
5336       border_explosion = TRUE;
5337     }
5338     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5339     {
5340       Feld[x][y] = Store2[x][y];
5341       Store2[x][y] = 0;
5342       Bang(x, y);
5343       border_explosion = TRUE;
5344     }
5345     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5346     {
5347       AmoebeUmwandeln(x, y);
5348       Store2[x][y] = 0;
5349       border_explosion = TRUE;
5350     }
5351
5352     /* if an element just explodes due to another explosion (chain-reaction),
5353        do not immediately end the new explosion when it was the last frame of
5354        the explosion (as it would be done in the following "if"-statement!) */
5355     if (border_explosion && phase == last_phase)
5356       return;
5357   }
5358
5359   if (phase == last_phase)
5360   {
5361     int element;
5362
5363     element = Feld[x][y] = Store[x][y];
5364     Store[x][y] = Store2[x][y] = 0;
5365     GfxElement[x][y] = EL_UNDEFINED;
5366
5367     /* player can escape from explosions and might therefore be still alive */
5368     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5369         element <= EL_PLAYER_IS_EXPLODING_4)
5370     {
5371       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5372       int explosion_element = EL_PLAYER_1 + player_nr;
5373       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5374       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5375
5376       if (level.use_explosion_element[player_nr])
5377         explosion_element = level.explosion_element[player_nr];
5378
5379       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5380                     element_info[explosion_element].content.e[xx][yy]);
5381     }
5382
5383     /* restore probably existing indestructible background element */
5384     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5385       element = Feld[x][y] = Back[x][y];
5386     Back[x][y] = 0;
5387
5388     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5389     GfxDir[x][y] = MV_NONE;
5390     ChangeDelay[x][y] = 0;
5391     ChangePage[x][y] = -1;
5392
5393     CustomValue[x][y] = 0;
5394
5395     InitField_WithBug2(x, y, FALSE);
5396
5397     TEST_DrawLevelField(x, y);
5398
5399     TestIfElementTouchesCustomElement(x, y);
5400
5401     if (GFX_CRUMBLED(element))
5402       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5403
5404     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5405       StorePlayer[x][y] = 0;
5406
5407     if (ELEM_IS_PLAYER(element))
5408       RelocatePlayer(x, y, element);
5409   }
5410   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5411   {
5412     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5413     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5414
5415     if (phase == delay)
5416       TEST_DrawLevelFieldCrumbled(x, y);
5417
5418     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5419     {
5420       DrawLevelElement(x, y, Back[x][y]);
5421       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5422     }
5423     else if (IS_WALKABLE_UNDER(Back[x][y]))
5424     {
5425       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5426       DrawLevelElementThruMask(x, y, Back[x][y]);
5427     }
5428     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5429       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5430   }
5431 }
5432
5433 void DynaExplode(int ex, int ey)
5434 {
5435   int i, j;
5436   int dynabomb_element = Feld[ex][ey];
5437   int dynabomb_size = 1;
5438   boolean dynabomb_xl = FALSE;
5439   struct PlayerInfo *player;
5440   static int xy[4][2] =
5441   {
5442     { 0, -1 },
5443     { -1, 0 },
5444     { +1, 0 },
5445     { 0, +1 }
5446   };
5447
5448   if (IS_ACTIVE_BOMB(dynabomb_element))
5449   {
5450     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5451     dynabomb_size = player->dynabomb_size;
5452     dynabomb_xl = player->dynabomb_xl;
5453     player->dynabombs_left++;
5454   }
5455
5456   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5457
5458   for (i = 0; i < NUM_DIRECTIONS; i++)
5459   {
5460     for (j = 1; j <= dynabomb_size; j++)
5461     {
5462       int x = ex + j * xy[i][0];
5463       int y = ey + j * xy[i][1];
5464       int element;
5465
5466       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5467         break;
5468
5469       element = Feld[x][y];
5470
5471       /* do not restart explosions of fields with active bombs */
5472       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5473         continue;
5474
5475       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5476
5477       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5478           !IS_DIGGABLE(element) && !dynabomb_xl)
5479         break;
5480     }
5481   }
5482 }
5483
5484 void Bang(int x, int y)
5485 {
5486   int element = MovingOrBlocked2Element(x, y);
5487   int explosion_type = EX_TYPE_NORMAL;
5488
5489   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5490   {
5491     struct PlayerInfo *player = PLAYERINFO(x, y);
5492
5493     element = Feld[x][y] = player->initial_element;
5494
5495     if (level.use_explosion_element[player->index_nr])
5496     {
5497       int explosion_element = level.explosion_element[player->index_nr];
5498
5499       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5500         explosion_type = EX_TYPE_CROSS;
5501       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5502         explosion_type = EX_TYPE_CENTER;
5503     }
5504   }
5505
5506   switch (element)
5507   {
5508     case EL_BUG:
5509     case EL_SPACESHIP:
5510     case EL_BD_BUTTERFLY:
5511     case EL_BD_FIREFLY:
5512     case EL_YAMYAM:
5513     case EL_DARK_YAMYAM:
5514     case EL_ROBOT:
5515     case EL_PACMAN:
5516     case EL_MOLE:
5517       RaiseScoreElement(element);
5518       break;
5519
5520     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5521     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5522     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5523     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5524     case EL_DYNABOMB_INCREASE_NUMBER:
5525     case EL_DYNABOMB_INCREASE_SIZE:
5526     case EL_DYNABOMB_INCREASE_POWER:
5527       explosion_type = EX_TYPE_DYNA;
5528       break;
5529
5530     case EL_DC_LANDMINE:
5531       explosion_type = EX_TYPE_CENTER;
5532       break;
5533
5534     case EL_PENGUIN:
5535     case EL_LAMP:
5536     case EL_LAMP_ACTIVE:
5537     case EL_AMOEBA_TO_DIAMOND:
5538       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5539         explosion_type = EX_TYPE_CENTER;
5540       break;
5541
5542     default:
5543       if (element_info[element].explosion_type == EXPLODES_CROSS)
5544         explosion_type = EX_TYPE_CROSS;
5545       else if (element_info[element].explosion_type == EXPLODES_1X1)
5546         explosion_type = EX_TYPE_CENTER;
5547       break;
5548   }
5549
5550   if (explosion_type == EX_TYPE_DYNA)
5551     DynaExplode(x, y);
5552   else
5553     Explode(x, y, EX_PHASE_START, explosion_type);
5554
5555   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5556 }
5557
5558 void SplashAcid(int x, int y)
5559 {
5560   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5561       (!IN_LEV_FIELD(x - 1, y - 2) ||
5562        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5563     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5564
5565   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5566       (!IN_LEV_FIELD(x + 1, y - 2) ||
5567        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5568     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5569
5570   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5571 }
5572
5573 static void InitBeltMovement()
5574 {
5575   static int belt_base_element[4] =
5576   {
5577     EL_CONVEYOR_BELT_1_LEFT,
5578     EL_CONVEYOR_BELT_2_LEFT,
5579     EL_CONVEYOR_BELT_3_LEFT,
5580     EL_CONVEYOR_BELT_4_LEFT
5581   };
5582   static int belt_base_active_element[4] =
5583   {
5584     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5585     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5586     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5587     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5588   };
5589
5590   int x, y, i, j;
5591
5592   /* set frame order for belt animation graphic according to belt direction */
5593   for (i = 0; i < NUM_BELTS; i++)
5594   {
5595     int belt_nr = i;
5596
5597     for (j = 0; j < NUM_BELT_PARTS; j++)
5598     {
5599       int element = belt_base_active_element[belt_nr] + j;
5600       int graphic_1 = el2img(element);
5601       int graphic_2 = el2panelimg(element);
5602
5603       if (game.belt_dir[i] == MV_LEFT)
5604       {
5605         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5606         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5607       }
5608       else
5609       {
5610         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5611         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5612       }
5613     }
5614   }
5615
5616   SCAN_PLAYFIELD(x, y)
5617   {
5618     int element = Feld[x][y];
5619
5620     for (i = 0; i < NUM_BELTS; i++)
5621     {
5622       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5623       {
5624         int e_belt_nr = getBeltNrFromBeltElement(element);
5625         int belt_nr = i;
5626
5627         if (e_belt_nr == belt_nr)
5628         {
5629           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5630
5631           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5632         }
5633       }
5634     }
5635   }
5636 }
5637
5638 static void ToggleBeltSwitch(int x, int y)
5639 {
5640   static int belt_base_element[4] =
5641   {
5642     EL_CONVEYOR_BELT_1_LEFT,
5643     EL_CONVEYOR_BELT_2_LEFT,
5644     EL_CONVEYOR_BELT_3_LEFT,
5645     EL_CONVEYOR_BELT_4_LEFT
5646   };
5647   static int belt_base_active_element[4] =
5648   {
5649     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5650     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5651     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5652     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5653   };
5654   static int belt_base_switch_element[4] =
5655   {
5656     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5657     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5658     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5659     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5660   };
5661   static int belt_move_dir[4] =
5662   {
5663     MV_LEFT,
5664     MV_NONE,
5665     MV_RIGHT,
5666     MV_NONE,
5667   };
5668
5669   int element = Feld[x][y];
5670   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5671   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5672   int belt_dir = belt_move_dir[belt_dir_nr];
5673   int xx, yy, i;
5674
5675   if (!IS_BELT_SWITCH(element))
5676     return;
5677
5678   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5679   game.belt_dir[belt_nr] = belt_dir;
5680
5681   if (belt_dir_nr == 3)
5682     belt_dir_nr = 1;
5683
5684   /* set frame order for belt animation graphic according to belt direction */
5685   for (i = 0; i < NUM_BELT_PARTS; i++)
5686   {
5687     int element = belt_base_active_element[belt_nr] + i;
5688     int graphic_1 = el2img(element);
5689     int graphic_2 = el2panelimg(element);
5690
5691     if (belt_dir == MV_LEFT)
5692     {
5693       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5694       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5695     }
5696     else
5697     {
5698       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5699       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5700     }
5701   }
5702
5703   SCAN_PLAYFIELD(xx, yy)
5704   {
5705     int element = Feld[xx][yy];
5706
5707     if (IS_BELT_SWITCH(element))
5708     {
5709       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5710
5711       if (e_belt_nr == belt_nr)
5712       {
5713         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5714         TEST_DrawLevelField(xx, yy);
5715       }
5716     }
5717     else if (IS_BELT(element) && belt_dir != MV_NONE)
5718     {
5719       int e_belt_nr = getBeltNrFromBeltElement(element);
5720
5721       if (e_belt_nr == belt_nr)
5722       {
5723         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5724
5725         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5726         TEST_DrawLevelField(xx, yy);
5727       }
5728     }
5729     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5730     {
5731       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5732
5733       if (e_belt_nr == belt_nr)
5734       {
5735         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5736
5737         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5738         TEST_DrawLevelField(xx, yy);
5739       }
5740     }
5741   }
5742 }
5743
5744 static void ToggleSwitchgateSwitch(int x, int y)
5745 {
5746   int xx, yy;
5747
5748   game.switchgate_pos = !game.switchgate_pos;
5749
5750   SCAN_PLAYFIELD(xx, yy)
5751   {
5752     int element = Feld[xx][yy];
5753
5754     if (element == EL_SWITCHGATE_SWITCH_UP)
5755     {
5756       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5757       TEST_DrawLevelField(xx, yy);
5758     }
5759     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5760     {
5761       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5762       TEST_DrawLevelField(xx, yy);
5763     }
5764     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5765     {
5766       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5767       TEST_DrawLevelField(xx, yy);
5768     }
5769     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5770     {
5771       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5772       TEST_DrawLevelField(xx, yy);
5773     }
5774     else if (element == EL_SWITCHGATE_OPEN ||
5775              element == EL_SWITCHGATE_OPENING)
5776     {
5777       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5778
5779       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5780     }
5781     else if (element == EL_SWITCHGATE_CLOSED ||
5782              element == EL_SWITCHGATE_CLOSING)
5783     {
5784       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5785
5786       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5787     }
5788   }
5789 }
5790
5791 static int getInvisibleActiveFromInvisibleElement(int element)
5792 {
5793   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5794           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5795           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5796           element);
5797 }
5798
5799 static int getInvisibleFromInvisibleActiveElement(int element)
5800 {
5801   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5802           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5803           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5804           element);
5805 }
5806
5807 static void RedrawAllLightSwitchesAndInvisibleElements()
5808 {
5809   int x, y;
5810
5811   SCAN_PLAYFIELD(x, y)
5812   {
5813     int element = Feld[x][y];
5814
5815     if (element == EL_LIGHT_SWITCH &&
5816         game.light_time_left > 0)
5817     {
5818       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5819       TEST_DrawLevelField(x, y);
5820     }
5821     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5822              game.light_time_left == 0)
5823     {
5824       Feld[x][y] = EL_LIGHT_SWITCH;
5825       TEST_DrawLevelField(x, y);
5826     }
5827     else if (element == EL_EMC_DRIPPER &&
5828              game.light_time_left > 0)
5829     {
5830       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5831       TEST_DrawLevelField(x, y);
5832     }
5833     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5834              game.light_time_left == 0)
5835     {
5836       Feld[x][y] = EL_EMC_DRIPPER;
5837       TEST_DrawLevelField(x, y);
5838     }
5839     else if (element == EL_INVISIBLE_STEELWALL ||
5840              element == EL_INVISIBLE_WALL ||
5841              element == EL_INVISIBLE_SAND)
5842     {
5843       if (game.light_time_left > 0)
5844         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5845
5846       TEST_DrawLevelField(x, y);
5847
5848       /* uncrumble neighbour fields, if needed */
5849       if (element == EL_INVISIBLE_SAND)
5850         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5851     }
5852     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5853              element == EL_INVISIBLE_WALL_ACTIVE ||
5854              element == EL_INVISIBLE_SAND_ACTIVE)
5855     {
5856       if (game.light_time_left == 0)
5857         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5858
5859       TEST_DrawLevelField(x, y);
5860
5861       /* re-crumble neighbour fields, if needed */
5862       if (element == EL_INVISIBLE_SAND)
5863         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5864     }
5865   }
5866 }
5867
5868 static void RedrawAllInvisibleElementsForLenses()
5869 {
5870   int x, y;
5871
5872   SCAN_PLAYFIELD(x, y)
5873   {
5874     int element = Feld[x][y];
5875
5876     if (element == EL_EMC_DRIPPER &&
5877         game.lenses_time_left > 0)
5878     {
5879       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5880       TEST_DrawLevelField(x, y);
5881     }
5882     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5883              game.lenses_time_left == 0)
5884     {
5885       Feld[x][y] = EL_EMC_DRIPPER;
5886       TEST_DrawLevelField(x, y);
5887     }
5888     else if (element == EL_INVISIBLE_STEELWALL ||
5889              element == EL_INVISIBLE_WALL ||
5890              element == EL_INVISIBLE_SAND)
5891     {
5892       if (game.lenses_time_left > 0)
5893         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5894
5895       TEST_DrawLevelField(x, y);
5896
5897       /* uncrumble neighbour fields, if needed */
5898       if (element == EL_INVISIBLE_SAND)
5899         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5900     }
5901     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5902              element == EL_INVISIBLE_WALL_ACTIVE ||
5903              element == EL_INVISIBLE_SAND_ACTIVE)
5904     {
5905       if (game.lenses_time_left == 0)
5906         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5907
5908       TEST_DrawLevelField(x, y);
5909
5910       /* re-crumble neighbour fields, if needed */
5911       if (element == EL_INVISIBLE_SAND)
5912         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5913     }
5914   }
5915 }
5916
5917 static void RedrawAllInvisibleElementsForMagnifier()
5918 {
5919   int x, y;
5920
5921   SCAN_PLAYFIELD(x, y)
5922   {
5923     int element = Feld[x][y];
5924
5925     if (element == EL_EMC_FAKE_GRASS &&
5926         game.magnify_time_left > 0)
5927     {
5928       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5929       TEST_DrawLevelField(x, y);
5930     }
5931     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5932              game.magnify_time_left == 0)
5933     {
5934       Feld[x][y] = EL_EMC_FAKE_GRASS;
5935       TEST_DrawLevelField(x, y);
5936     }
5937     else if (IS_GATE_GRAY(element) &&
5938              game.magnify_time_left > 0)
5939     {
5940       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5941                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5942                     IS_EM_GATE_GRAY(element) ?
5943                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5944                     IS_EMC_GATE_GRAY(element) ?
5945                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5946                     IS_DC_GATE_GRAY(element) ?
5947                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5948                     element);
5949       TEST_DrawLevelField(x, y);
5950     }
5951     else if (IS_GATE_GRAY_ACTIVE(element) &&
5952              game.magnify_time_left == 0)
5953     {
5954       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5955                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5956                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5957                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5958                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5959                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5960                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5961                     EL_DC_GATE_WHITE_GRAY :
5962                     element);
5963       TEST_DrawLevelField(x, y);
5964     }
5965   }
5966 }
5967
5968 static void ToggleLightSwitch(int x, int y)
5969 {
5970   int element = Feld[x][y];
5971
5972   game.light_time_left =
5973     (element == EL_LIGHT_SWITCH ?
5974      level.time_light * FRAMES_PER_SECOND : 0);
5975
5976   RedrawAllLightSwitchesAndInvisibleElements();
5977 }
5978
5979 static void ActivateTimegateSwitch(int x, int y)
5980 {
5981   int xx, yy;
5982
5983   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5984
5985   SCAN_PLAYFIELD(xx, yy)
5986   {
5987     int element = Feld[xx][yy];
5988
5989     if (element == EL_TIMEGATE_CLOSED ||
5990         element == EL_TIMEGATE_CLOSING)
5991     {
5992       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5993       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5994     }
5995
5996     /*
5997     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5998     {
5999       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6000       TEST_DrawLevelField(xx, yy);
6001     }
6002     */
6003
6004   }
6005
6006   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6007                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6008 }
6009
6010 void Impact(int x, int y)
6011 {
6012   boolean last_line = (y == lev_fieldy - 1);
6013   boolean object_hit = FALSE;
6014   boolean impact = (last_line || object_hit);
6015   int element = Feld[x][y];
6016   int smashed = EL_STEELWALL;
6017
6018   if (!last_line)       /* check if element below was hit */
6019   {
6020     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6021       return;
6022
6023     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6024                                          MovDir[x][y + 1] != MV_DOWN ||
6025                                          MovPos[x][y + 1] <= TILEY / 2));
6026
6027     /* do not smash moving elements that left the smashed field in time */
6028     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6029         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6030       object_hit = FALSE;
6031
6032 #if USE_QUICKSAND_IMPACT_BUGFIX
6033     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6034     {
6035       RemoveMovingField(x, y + 1);
6036       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6037       Feld[x][y + 2] = EL_ROCK;
6038       TEST_DrawLevelField(x, y + 2);
6039
6040       object_hit = TRUE;
6041     }
6042
6043     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6044     {
6045       RemoveMovingField(x, y + 1);
6046       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6047       Feld[x][y + 2] = EL_ROCK;
6048       TEST_DrawLevelField(x, y + 2);
6049
6050       object_hit = TRUE;
6051     }
6052 #endif
6053
6054     if (object_hit)
6055       smashed = MovingOrBlocked2Element(x, y + 1);
6056
6057     impact = (last_line || object_hit);
6058   }
6059
6060   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6061   {
6062     SplashAcid(x, y + 1);
6063     return;
6064   }
6065
6066   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6067   /* only reset graphic animation if graphic really changes after impact */
6068   if (impact &&
6069       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6070   {
6071     ResetGfxAnimation(x, y);
6072     TEST_DrawLevelField(x, y);
6073   }
6074
6075   if (impact && CAN_EXPLODE_IMPACT(element))
6076   {
6077     Bang(x, y);
6078     return;
6079   }
6080   else if (impact && element == EL_PEARL &&
6081            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6082   {
6083     ResetGfxAnimation(x, y);
6084
6085     Feld[x][y] = EL_PEARL_BREAKING;
6086     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6087     return;
6088   }
6089   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6090   {
6091     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6092
6093     return;
6094   }
6095
6096   if (impact && element == EL_AMOEBA_DROP)
6097   {
6098     if (object_hit && IS_PLAYER(x, y + 1))
6099       KillPlayerUnlessEnemyProtected(x, y + 1);
6100     else if (object_hit && smashed == EL_PENGUIN)
6101       Bang(x, y + 1);
6102     else
6103     {
6104       Feld[x][y] = EL_AMOEBA_GROWING;
6105       Store[x][y] = EL_AMOEBA_WET;
6106
6107       ResetRandomAnimationValue(x, y);
6108     }
6109     return;
6110   }
6111
6112   if (object_hit)               /* check which object was hit */
6113   {
6114     if ((CAN_PASS_MAGIC_WALL(element) && 
6115          (smashed == EL_MAGIC_WALL ||
6116           smashed == EL_BD_MAGIC_WALL)) ||
6117         (CAN_PASS_DC_MAGIC_WALL(element) &&
6118          smashed == EL_DC_MAGIC_WALL))
6119     {
6120       int xx, yy;
6121       int activated_magic_wall =
6122         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6123          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6124          EL_DC_MAGIC_WALL_ACTIVE);
6125
6126       /* activate magic wall / mill */
6127       SCAN_PLAYFIELD(xx, yy)
6128       {
6129         if (Feld[xx][yy] == smashed)
6130           Feld[xx][yy] = activated_magic_wall;
6131       }
6132
6133       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6134       game.magic_wall_active = TRUE;
6135
6136       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6137                             SND_MAGIC_WALL_ACTIVATING :
6138                             smashed == EL_BD_MAGIC_WALL ?
6139                             SND_BD_MAGIC_WALL_ACTIVATING :
6140                             SND_DC_MAGIC_WALL_ACTIVATING));
6141     }
6142
6143     if (IS_PLAYER(x, y + 1))
6144     {
6145       if (CAN_SMASH_PLAYER(element))
6146       {
6147         KillPlayerUnlessEnemyProtected(x, y + 1);
6148         return;
6149       }
6150     }
6151     else if (smashed == EL_PENGUIN)
6152     {
6153       if (CAN_SMASH_PLAYER(element))
6154       {
6155         Bang(x, y + 1);
6156         return;
6157       }
6158     }
6159     else if (element == EL_BD_DIAMOND)
6160     {
6161       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6162       {
6163         Bang(x, y + 1);
6164         return;
6165       }
6166     }
6167     else if (((element == EL_SP_INFOTRON ||
6168                element == EL_SP_ZONK) &&
6169               (smashed == EL_SP_SNIKSNAK ||
6170                smashed == EL_SP_ELECTRON ||
6171                smashed == EL_SP_DISK_ORANGE)) ||
6172              (element == EL_SP_INFOTRON &&
6173               smashed == EL_SP_DISK_YELLOW))
6174     {
6175       Bang(x, y + 1);
6176       return;
6177     }
6178     else if (CAN_SMASH_EVERYTHING(element))
6179     {
6180       if (IS_CLASSIC_ENEMY(smashed) ||
6181           CAN_EXPLODE_SMASHED(smashed))
6182       {
6183         Bang(x, y + 1);
6184         return;
6185       }
6186       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6187       {
6188         if (smashed == EL_LAMP ||
6189             smashed == EL_LAMP_ACTIVE)
6190         {
6191           Bang(x, y + 1);
6192           return;
6193         }
6194         else if (smashed == EL_NUT)
6195         {
6196           Feld[x][y + 1] = EL_NUT_BREAKING;
6197           PlayLevelSound(x, y, SND_NUT_BREAKING);
6198           RaiseScoreElement(EL_NUT);
6199           return;
6200         }
6201         else if (smashed == EL_PEARL)
6202         {
6203           ResetGfxAnimation(x, y);
6204
6205           Feld[x][y + 1] = EL_PEARL_BREAKING;
6206           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6207           return;
6208         }
6209         else if (smashed == EL_DIAMOND)
6210         {
6211           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6212           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6213           return;
6214         }
6215         else if (IS_BELT_SWITCH(smashed))
6216         {
6217           ToggleBeltSwitch(x, y + 1);
6218         }
6219         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6220                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6221                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6222                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6223         {
6224           ToggleSwitchgateSwitch(x, y + 1);
6225         }
6226         else if (smashed == EL_LIGHT_SWITCH ||
6227                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6228         {
6229           ToggleLightSwitch(x, y + 1);
6230         }
6231         else
6232         {
6233           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6234
6235           CheckElementChangeBySide(x, y + 1, smashed, element,
6236                                    CE_SWITCHED, CH_SIDE_TOP);
6237           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6238                                             CH_SIDE_TOP);
6239         }
6240       }
6241       else
6242       {
6243         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6244       }
6245     }
6246   }
6247
6248   /* play sound of magic wall / mill */
6249   if (!last_line &&
6250       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6251        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6252        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6253   {
6254     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6255       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6256     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6257       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6258     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6259       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6260
6261     return;
6262   }
6263
6264   /* play sound of object that hits the ground */
6265   if (last_line || object_hit)
6266     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6267 }
6268
6269 inline static void TurnRoundExt(int x, int y)
6270 {
6271   static struct
6272   {
6273     int dx, dy;
6274   } move_xy[] =
6275   {
6276     {  0,  0 },
6277     { -1,  0 },
6278     { +1,  0 },
6279     {  0,  0 },
6280     {  0, -1 },
6281     {  0,  0 }, { 0, 0 }, { 0, 0 },
6282     {  0, +1 }
6283   };
6284   static struct
6285   {
6286     int left, right, back;
6287   } turn[] =
6288   {
6289     { 0,        0,              0        },
6290     { MV_DOWN,  MV_UP,          MV_RIGHT },
6291     { MV_UP,    MV_DOWN,        MV_LEFT  },
6292     { 0,        0,              0        },
6293     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6294     { 0,        0,              0        },
6295     { 0,        0,              0        },
6296     { 0,        0,              0        },
6297     { MV_RIGHT, MV_LEFT,        MV_UP    }
6298   };
6299
6300   int element = Feld[x][y];
6301   int move_pattern = element_info[element].move_pattern;
6302
6303   int old_move_dir = MovDir[x][y];
6304   int left_dir  = turn[old_move_dir].left;
6305   int right_dir = turn[old_move_dir].right;
6306   int back_dir  = turn[old_move_dir].back;
6307
6308   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6309   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6310   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6311   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6312
6313   int left_x  = x + left_dx,  left_y  = y + left_dy;
6314   int right_x = x + right_dx, right_y = y + right_dy;
6315   int move_x  = x + move_dx,  move_y  = y + move_dy;
6316
6317   int xx, yy;
6318
6319   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6320   {
6321     TestIfBadThingTouchesOtherBadThing(x, y);
6322
6323     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6324       MovDir[x][y] = right_dir;
6325     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6326       MovDir[x][y] = left_dir;
6327
6328     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6329       MovDelay[x][y] = 9;
6330     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6331       MovDelay[x][y] = 1;
6332   }
6333   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6334   {
6335     TestIfBadThingTouchesOtherBadThing(x, y);
6336
6337     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6338       MovDir[x][y] = left_dir;
6339     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6340       MovDir[x][y] = right_dir;
6341
6342     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6343       MovDelay[x][y] = 9;
6344     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6345       MovDelay[x][y] = 1;
6346   }
6347   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6348   {
6349     TestIfBadThingTouchesOtherBadThing(x, y);
6350
6351     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6352       MovDir[x][y] = left_dir;
6353     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6354       MovDir[x][y] = right_dir;
6355
6356     if (MovDir[x][y] != old_move_dir)
6357       MovDelay[x][y] = 9;
6358   }
6359   else if (element == EL_YAMYAM)
6360   {
6361     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6362     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6363
6364     if (can_turn_left && can_turn_right)
6365       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6366     else if (can_turn_left)
6367       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6368     else if (can_turn_right)
6369       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6370     else
6371       MovDir[x][y] = back_dir;
6372
6373     MovDelay[x][y] = 16 + 16 * RND(3);
6374   }
6375   else if (element == EL_DARK_YAMYAM)
6376   {
6377     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6378                                                          left_x, left_y);
6379     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6380                                                          right_x, right_y);
6381
6382     if (can_turn_left && can_turn_right)
6383       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6384     else if (can_turn_left)
6385       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6386     else if (can_turn_right)
6387       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6388     else
6389       MovDir[x][y] = back_dir;
6390
6391     MovDelay[x][y] = 16 + 16 * RND(3);
6392   }
6393   else if (element == EL_PACMAN)
6394   {
6395     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6396     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6397
6398     if (can_turn_left && can_turn_right)
6399       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6400     else if (can_turn_left)
6401       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6402     else if (can_turn_right)
6403       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6404     else
6405       MovDir[x][y] = back_dir;
6406
6407     MovDelay[x][y] = 6 + RND(40);
6408   }
6409   else if (element == EL_PIG)
6410   {
6411     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6412     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6413     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6414     boolean should_turn_left, should_turn_right, should_move_on;
6415     int rnd_value = 24;
6416     int rnd = RND(rnd_value);
6417
6418     should_turn_left = (can_turn_left &&
6419                         (!can_move_on ||
6420                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6421                                                    y + back_dy + left_dy)));
6422     should_turn_right = (can_turn_right &&
6423                          (!can_move_on ||
6424                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6425                                                     y + back_dy + right_dy)));
6426     should_move_on = (can_move_on &&
6427                       (!can_turn_left ||
6428                        !can_turn_right ||
6429                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6430                                                  y + move_dy + left_dy) ||
6431                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6432                                                  y + move_dy + right_dy)));
6433
6434     if (should_turn_left || should_turn_right || should_move_on)
6435     {
6436       if (should_turn_left && should_turn_right && should_move_on)
6437         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6438                         rnd < 2 * rnd_value / 3 ? right_dir :
6439                         old_move_dir);
6440       else if (should_turn_left && should_turn_right)
6441         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6442       else if (should_turn_left && should_move_on)
6443         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6444       else if (should_turn_right && should_move_on)
6445         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6446       else if (should_turn_left)
6447         MovDir[x][y] = left_dir;
6448       else if (should_turn_right)
6449         MovDir[x][y] = right_dir;
6450       else if (should_move_on)
6451         MovDir[x][y] = old_move_dir;
6452     }
6453     else if (can_move_on && rnd > rnd_value / 8)
6454       MovDir[x][y] = old_move_dir;
6455     else if (can_turn_left && can_turn_right)
6456       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6457     else if (can_turn_left && rnd > rnd_value / 8)
6458       MovDir[x][y] = left_dir;
6459     else if (can_turn_right && rnd > rnd_value/8)
6460       MovDir[x][y] = right_dir;
6461     else
6462       MovDir[x][y] = back_dir;
6463
6464     xx = x + move_xy[MovDir[x][y]].dx;
6465     yy = y + move_xy[MovDir[x][y]].dy;
6466
6467     if (!IN_LEV_FIELD(xx, yy) ||
6468         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6469       MovDir[x][y] = old_move_dir;
6470
6471     MovDelay[x][y] = 0;
6472   }
6473   else if (element == EL_DRAGON)
6474   {
6475     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6476     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6477     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6478     int rnd_value = 24;
6479     int rnd = RND(rnd_value);
6480
6481     if (can_move_on && rnd > rnd_value / 8)
6482       MovDir[x][y] = old_move_dir;
6483     else if (can_turn_left && can_turn_right)
6484       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6485     else if (can_turn_left && rnd > rnd_value / 8)
6486       MovDir[x][y] = left_dir;
6487     else if (can_turn_right && rnd > rnd_value / 8)
6488       MovDir[x][y] = right_dir;
6489     else
6490       MovDir[x][y] = back_dir;
6491
6492     xx = x + move_xy[MovDir[x][y]].dx;
6493     yy = y + move_xy[MovDir[x][y]].dy;
6494
6495     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6496       MovDir[x][y] = old_move_dir;
6497
6498     MovDelay[x][y] = 0;
6499   }
6500   else if (element == EL_MOLE)
6501   {
6502     boolean can_move_on =
6503       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6504                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6505                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6506     if (!can_move_on)
6507     {
6508       boolean can_turn_left =
6509         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6510                               IS_AMOEBOID(Feld[left_x][left_y])));
6511
6512       boolean can_turn_right =
6513         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6514                               IS_AMOEBOID(Feld[right_x][right_y])));
6515
6516       if (can_turn_left && can_turn_right)
6517         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6518       else if (can_turn_left)
6519         MovDir[x][y] = left_dir;
6520       else
6521         MovDir[x][y] = right_dir;
6522     }
6523
6524     if (MovDir[x][y] != old_move_dir)
6525       MovDelay[x][y] = 9;
6526   }
6527   else if (element == EL_BALLOON)
6528   {
6529     MovDir[x][y] = game.wind_direction;
6530     MovDelay[x][y] = 0;
6531   }
6532   else if (element == EL_SPRING)
6533   {
6534     if (MovDir[x][y] & MV_HORIZONTAL)
6535     {
6536       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6537           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6538       {
6539         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6540         ResetGfxAnimation(move_x, move_y);
6541         TEST_DrawLevelField(move_x, move_y);
6542
6543         MovDir[x][y] = back_dir;
6544       }
6545       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6546                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6547         MovDir[x][y] = MV_NONE;
6548     }
6549
6550     MovDelay[x][y] = 0;
6551   }
6552   else if (element == EL_ROBOT ||
6553            element == EL_SATELLITE ||
6554            element == EL_PENGUIN ||
6555            element == EL_EMC_ANDROID)
6556   {
6557     int attr_x = -1, attr_y = -1;
6558
6559     if (AllPlayersGone)
6560     {
6561       attr_x = ExitX;
6562       attr_y = ExitY;
6563     }
6564     else
6565     {
6566       int i;
6567
6568       for (i = 0; i < MAX_PLAYERS; i++)
6569       {
6570         struct PlayerInfo *player = &stored_player[i];
6571         int jx = player->jx, jy = player->jy;
6572
6573         if (!player->active)
6574           continue;
6575
6576         if (attr_x == -1 ||
6577             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6578         {
6579           attr_x = jx;
6580           attr_y = jy;
6581         }
6582       }
6583     }
6584
6585     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6586         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6587          game.engine_version < VERSION_IDENT(3,1,0,0)))
6588     {
6589       attr_x = ZX;
6590       attr_y = ZY;
6591     }
6592
6593     if (element == EL_PENGUIN)
6594     {
6595       int i;
6596       static int xy[4][2] =
6597       {
6598         { 0, -1 },
6599         { -1, 0 },
6600         { +1, 0 },
6601         { 0, +1 }
6602       };
6603
6604       for (i = 0; i < NUM_DIRECTIONS; i++)
6605       {
6606         int ex = x + xy[i][0];
6607         int ey = y + xy[i][1];
6608
6609         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6610                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6611                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6612                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6613         {
6614           attr_x = ex;
6615           attr_y = ey;
6616           break;
6617         }
6618       }
6619     }
6620
6621     MovDir[x][y] = MV_NONE;
6622     if (attr_x < x)
6623       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6624     else if (attr_x > x)
6625       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6626     if (attr_y < y)
6627       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6628     else if (attr_y > y)
6629       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6630
6631     if (element == EL_ROBOT)
6632     {
6633       int newx, newy;
6634
6635       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6636         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6637       Moving2Blocked(x, y, &newx, &newy);
6638
6639       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6640         MovDelay[x][y] = 8 + 8 * !RND(3);
6641       else
6642         MovDelay[x][y] = 16;
6643     }
6644     else if (element == EL_PENGUIN)
6645     {
6646       int newx, newy;
6647
6648       MovDelay[x][y] = 1;
6649
6650       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6651       {
6652         boolean first_horiz = RND(2);
6653         int new_move_dir = MovDir[x][y];
6654
6655         MovDir[x][y] =
6656           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657         Moving2Blocked(x, y, &newx, &newy);
6658
6659         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6660           return;
6661
6662         MovDir[x][y] =
6663           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6664         Moving2Blocked(x, y, &newx, &newy);
6665
6666         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6667           return;
6668
6669         MovDir[x][y] = old_move_dir;
6670         return;
6671       }
6672     }
6673     else if (element == EL_SATELLITE)
6674     {
6675       int newx, newy;
6676
6677       MovDelay[x][y] = 1;
6678
6679       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6680       {
6681         boolean first_horiz = RND(2);
6682         int new_move_dir = MovDir[x][y];
6683
6684         MovDir[x][y] =
6685           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6686         Moving2Blocked(x, y, &newx, &newy);
6687
6688         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6689           return;
6690
6691         MovDir[x][y] =
6692           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6693         Moving2Blocked(x, y, &newx, &newy);
6694
6695         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6696           return;
6697
6698         MovDir[x][y] = old_move_dir;
6699         return;
6700       }
6701     }
6702     else if (element == EL_EMC_ANDROID)
6703     {
6704       static int check_pos[16] =
6705       {
6706         -1,             /*  0 => (invalid)          */
6707         7,              /*  1 => MV_LEFT            */
6708         3,              /*  2 => MV_RIGHT           */
6709         -1,             /*  3 => (invalid)          */
6710         1,              /*  4 =>            MV_UP   */
6711         0,              /*  5 => MV_LEFT  | MV_UP   */
6712         2,              /*  6 => MV_RIGHT | MV_UP   */
6713         -1,             /*  7 => (invalid)          */
6714         5,              /*  8 =>            MV_DOWN */
6715         6,              /*  9 => MV_LEFT  | MV_DOWN */
6716         4,              /* 10 => MV_RIGHT | MV_DOWN */
6717         -1,             /* 11 => (invalid)          */
6718         -1,             /* 12 => (invalid)          */
6719         -1,             /* 13 => (invalid)          */
6720         -1,             /* 14 => (invalid)          */
6721         -1,             /* 15 => (invalid)          */
6722       };
6723       static struct
6724       {
6725         int dx, dy;
6726         int dir;
6727       } check_xy[8] =
6728       {
6729         { -1, -1,       MV_LEFT  | MV_UP   },
6730         {  0, -1,                  MV_UP   },
6731         { +1, -1,       MV_RIGHT | MV_UP   },
6732         { +1,  0,       MV_RIGHT           },
6733         { +1, +1,       MV_RIGHT | MV_DOWN },
6734         {  0, +1,                  MV_DOWN },
6735         { -1, +1,       MV_LEFT  | MV_DOWN },
6736         { -1,  0,       MV_LEFT            },
6737       };
6738       int start_pos, check_order;
6739       boolean can_clone = FALSE;
6740       int i;
6741
6742       /* check if there is any free field around current position */
6743       for (i = 0; i < 8; i++)
6744       {
6745         int newx = x + check_xy[i].dx;
6746         int newy = y + check_xy[i].dy;
6747
6748         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6749         {
6750           can_clone = TRUE;
6751
6752           break;
6753         }
6754       }
6755
6756       if (can_clone)            /* randomly find an element to clone */
6757       {
6758         can_clone = FALSE;
6759
6760         start_pos = check_pos[RND(8)];
6761         check_order = (RND(2) ? -1 : +1);
6762
6763         for (i = 0; i < 8; i++)
6764         {
6765           int pos_raw = start_pos + i * check_order;
6766           int pos = (pos_raw + 8) % 8;
6767           int newx = x + check_xy[pos].dx;
6768           int newy = y + check_xy[pos].dy;
6769
6770           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6771           {
6772             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6773             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6774
6775             Store[x][y] = Feld[newx][newy];
6776
6777             can_clone = TRUE;
6778
6779             break;
6780           }
6781         }
6782       }
6783
6784       if (can_clone)            /* randomly find a direction to move */
6785       {
6786         can_clone = FALSE;
6787
6788         start_pos = check_pos[RND(8)];
6789         check_order = (RND(2) ? -1 : +1);
6790
6791         for (i = 0; i < 8; i++)
6792         {
6793           int pos_raw = start_pos + i * check_order;
6794           int pos = (pos_raw + 8) % 8;
6795           int newx = x + check_xy[pos].dx;
6796           int newy = y + check_xy[pos].dy;
6797           int new_move_dir = check_xy[pos].dir;
6798
6799           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6800           {
6801             MovDir[x][y] = new_move_dir;
6802             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6803
6804             can_clone = TRUE;
6805
6806             break;
6807           }
6808         }
6809       }
6810
6811       if (can_clone)            /* cloning and moving successful */
6812         return;
6813
6814       /* cannot clone -- try to move towards player */
6815
6816       start_pos = check_pos[MovDir[x][y] & 0x0f];
6817       check_order = (RND(2) ? -1 : +1);
6818
6819       for (i = 0; i < 3; i++)
6820       {
6821         /* first check start_pos, then previous/next or (next/previous) pos */
6822         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6823         int pos = (pos_raw + 8) % 8;
6824         int newx = x + check_xy[pos].dx;
6825         int newy = y + check_xy[pos].dy;
6826         int new_move_dir = check_xy[pos].dir;
6827
6828         if (IS_PLAYER(newx, newy))
6829           break;
6830
6831         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6832         {
6833           MovDir[x][y] = new_move_dir;
6834           MovDelay[x][y] = level.android_move_time * 8 + 1;
6835
6836           break;
6837         }
6838       }
6839     }
6840   }
6841   else if (move_pattern == MV_TURNING_LEFT ||
6842            move_pattern == MV_TURNING_RIGHT ||
6843            move_pattern == MV_TURNING_LEFT_RIGHT ||
6844            move_pattern == MV_TURNING_RIGHT_LEFT ||
6845            move_pattern == MV_TURNING_RANDOM ||
6846            move_pattern == MV_ALL_DIRECTIONS)
6847   {
6848     boolean can_turn_left =
6849       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6850     boolean can_turn_right =
6851       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6852
6853     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6854       return;
6855
6856     if (move_pattern == MV_TURNING_LEFT)
6857       MovDir[x][y] = left_dir;
6858     else if (move_pattern == MV_TURNING_RIGHT)
6859       MovDir[x][y] = right_dir;
6860     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6861       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6862     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6863       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6864     else if (move_pattern == MV_TURNING_RANDOM)
6865       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6866                       can_turn_right && !can_turn_left ? right_dir :
6867                       RND(2) ? left_dir : right_dir);
6868     else if (can_turn_left && can_turn_right)
6869       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6870     else if (can_turn_left)
6871       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6872     else if (can_turn_right)
6873       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6874     else
6875       MovDir[x][y] = back_dir;
6876
6877     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6878   }
6879   else if (move_pattern == MV_HORIZONTAL ||
6880            move_pattern == MV_VERTICAL)
6881   {
6882     if (move_pattern & old_move_dir)
6883       MovDir[x][y] = back_dir;
6884     else if (move_pattern == MV_HORIZONTAL)
6885       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6886     else if (move_pattern == MV_VERTICAL)
6887       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6888
6889     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6890   }
6891   else if (move_pattern & MV_ANY_DIRECTION)
6892   {
6893     MovDir[x][y] = move_pattern;
6894     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6895   }
6896   else if (move_pattern & MV_WIND_DIRECTION)
6897   {
6898     MovDir[x][y] = game.wind_direction;
6899     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6900   }
6901   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6902   {
6903     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6904       MovDir[x][y] = left_dir;
6905     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6906       MovDir[x][y] = right_dir;
6907
6908     if (MovDir[x][y] != old_move_dir)
6909       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6910   }
6911   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6912   {
6913     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6914       MovDir[x][y] = right_dir;
6915     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6916       MovDir[x][y] = left_dir;
6917
6918     if (MovDir[x][y] != old_move_dir)
6919       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6920   }
6921   else if (move_pattern == MV_TOWARDS_PLAYER ||
6922            move_pattern == MV_AWAY_FROM_PLAYER)
6923   {
6924     int attr_x = -1, attr_y = -1;
6925     int newx, newy;
6926     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6927
6928     if (AllPlayersGone)
6929     {
6930       attr_x = ExitX;
6931       attr_y = ExitY;
6932     }
6933     else
6934     {
6935       int i;
6936
6937       for (i = 0; i < MAX_PLAYERS; i++)
6938       {
6939         struct PlayerInfo *player = &stored_player[i];
6940         int jx = player->jx, jy = player->jy;
6941
6942         if (!player->active)
6943           continue;
6944
6945         if (attr_x == -1 ||
6946             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6947         {
6948           attr_x = jx;
6949           attr_y = jy;
6950         }
6951       }
6952     }
6953
6954     MovDir[x][y] = MV_NONE;
6955     if (attr_x < x)
6956       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6957     else if (attr_x > x)
6958       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6959     if (attr_y < y)
6960       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6961     else if (attr_y > y)
6962       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6963
6964     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6965
6966     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6967     {
6968       boolean first_horiz = RND(2);
6969       int new_move_dir = MovDir[x][y];
6970
6971       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6972       {
6973         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6974         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6975
6976         return;
6977       }
6978
6979       MovDir[x][y] =
6980         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6981       Moving2Blocked(x, y, &newx, &newy);
6982
6983       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6984         return;
6985
6986       MovDir[x][y] =
6987         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6988       Moving2Blocked(x, y, &newx, &newy);
6989
6990       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6991         return;
6992
6993       MovDir[x][y] = old_move_dir;
6994     }
6995   }
6996   else if (move_pattern == MV_WHEN_PUSHED ||
6997            move_pattern == MV_WHEN_DROPPED)
6998   {
6999     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7000       MovDir[x][y] = MV_NONE;
7001
7002     MovDelay[x][y] = 0;
7003   }
7004   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7005   {
7006     static int test_xy[7][2] =
7007     {
7008       { 0, -1 },
7009       { -1, 0 },
7010       { +1, 0 },
7011       { 0, +1 },
7012       { 0, -1 },
7013       { -1, 0 },
7014       { +1, 0 },
7015     };
7016     static int test_dir[7] =
7017     {
7018       MV_UP,
7019       MV_LEFT,
7020       MV_RIGHT,
7021       MV_DOWN,
7022       MV_UP,
7023       MV_LEFT,
7024       MV_RIGHT,
7025     };
7026     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7027     int move_preference = -1000000;     /* start with very low preference */
7028     int new_move_dir = MV_NONE;
7029     int start_test = RND(4);
7030     int i;
7031
7032     for (i = 0; i < NUM_DIRECTIONS; i++)
7033     {
7034       int move_dir = test_dir[start_test + i];
7035       int move_dir_preference;
7036
7037       xx = x + test_xy[start_test + i][0];
7038       yy = y + test_xy[start_test + i][1];
7039
7040       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7041           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7042       {
7043         new_move_dir = move_dir;
7044
7045         break;
7046       }
7047
7048       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7049         continue;
7050
7051       move_dir_preference = -1 * RunnerVisit[xx][yy];
7052       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7053         move_dir_preference = PlayerVisit[xx][yy];
7054
7055       if (move_dir_preference > move_preference)
7056       {
7057         /* prefer field that has not been visited for the longest time */
7058         move_preference = move_dir_preference;
7059         new_move_dir = move_dir;
7060       }
7061       else if (move_dir_preference == move_preference &&
7062                move_dir == old_move_dir)
7063       {
7064         /* prefer last direction when all directions are preferred equally */
7065         move_preference = move_dir_preference;
7066         new_move_dir = move_dir;
7067       }
7068     }
7069
7070     MovDir[x][y] = new_move_dir;
7071     if (old_move_dir != new_move_dir)
7072       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7073   }
7074 }
7075
7076 static void TurnRound(int x, int y)
7077 {
7078   int direction = MovDir[x][y];
7079
7080   TurnRoundExt(x, y);
7081
7082   GfxDir[x][y] = MovDir[x][y];
7083
7084   if (direction != MovDir[x][y])
7085     GfxFrame[x][y] = 0;
7086
7087   if (MovDelay[x][y])
7088     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7089
7090   ResetGfxFrame(x, y, FALSE);
7091 }
7092
7093 static boolean JustBeingPushed(int x, int y)
7094 {
7095   int i;
7096
7097   for (i = 0; i < MAX_PLAYERS; i++)
7098   {
7099     struct PlayerInfo *player = &stored_player[i];
7100
7101     if (player->active && player->is_pushing && player->MovPos)
7102     {
7103       int next_jx = player->jx + (player->jx - player->last_jx);
7104       int next_jy = player->jy + (player->jy - player->last_jy);
7105
7106       if (x == next_jx && y == next_jy)
7107         return TRUE;
7108     }
7109   }
7110
7111   return FALSE;
7112 }
7113
7114 void StartMoving(int x, int y)
7115 {
7116   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7117   int element = Feld[x][y];
7118
7119   if (Stop[x][y])
7120     return;
7121
7122   if (MovDelay[x][y] == 0)
7123     GfxAction[x][y] = ACTION_DEFAULT;
7124
7125   if (CAN_FALL(element) && y < lev_fieldy - 1)
7126   {
7127     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7128         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7129       if (JustBeingPushed(x, y))
7130         return;
7131
7132     if (element == EL_QUICKSAND_FULL)
7133     {
7134       if (IS_FREE(x, y + 1))
7135       {
7136         InitMovingField(x, y, MV_DOWN);
7137         started_moving = TRUE;
7138
7139         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7140 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7141         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7142           Store[x][y] = EL_ROCK;
7143 #else
7144         Store[x][y] = EL_ROCK;
7145 #endif
7146
7147         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7148       }
7149       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7150       {
7151         if (!MovDelay[x][y])
7152         {
7153           MovDelay[x][y] = TILEY + 1;
7154
7155           ResetGfxAnimation(x, y);
7156           ResetGfxAnimation(x, y + 1);
7157         }
7158
7159         if (MovDelay[x][y])
7160         {
7161           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7162           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7163
7164           MovDelay[x][y]--;
7165           if (MovDelay[x][y])
7166             return;
7167         }
7168
7169         Feld[x][y] = EL_QUICKSAND_EMPTY;
7170         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7171         Store[x][y + 1] = Store[x][y];
7172         Store[x][y] = 0;
7173
7174         PlayLevelSoundAction(x, y, ACTION_FILLING);
7175       }
7176       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7177       {
7178         if (!MovDelay[x][y])
7179         {
7180           MovDelay[x][y] = TILEY + 1;
7181
7182           ResetGfxAnimation(x, y);
7183           ResetGfxAnimation(x, y + 1);
7184         }
7185
7186         if (MovDelay[x][y])
7187         {
7188           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7189           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7190
7191           MovDelay[x][y]--;
7192           if (MovDelay[x][y])
7193             return;
7194         }
7195
7196         Feld[x][y] = EL_QUICKSAND_EMPTY;
7197         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7198         Store[x][y + 1] = Store[x][y];
7199         Store[x][y] = 0;
7200
7201         PlayLevelSoundAction(x, y, ACTION_FILLING);
7202       }
7203     }
7204     else if (element == EL_QUICKSAND_FAST_FULL)
7205     {
7206       if (IS_FREE(x, y + 1))
7207       {
7208         InitMovingField(x, y, MV_DOWN);
7209         started_moving = TRUE;
7210
7211         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7212 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7213         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7214           Store[x][y] = EL_ROCK;
7215 #else
7216         Store[x][y] = EL_ROCK;
7217 #endif
7218
7219         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7220       }
7221       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7222       {
7223         if (!MovDelay[x][y])
7224         {
7225           MovDelay[x][y] = TILEY + 1;
7226
7227           ResetGfxAnimation(x, y);
7228           ResetGfxAnimation(x, y + 1);
7229         }
7230
7231         if (MovDelay[x][y])
7232         {
7233           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7234           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7235
7236           MovDelay[x][y]--;
7237           if (MovDelay[x][y])
7238             return;
7239         }
7240
7241         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7242         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7243         Store[x][y + 1] = Store[x][y];
7244         Store[x][y] = 0;
7245
7246         PlayLevelSoundAction(x, y, ACTION_FILLING);
7247       }
7248       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7249       {
7250         if (!MovDelay[x][y])
7251         {
7252           MovDelay[x][y] = TILEY + 1;
7253
7254           ResetGfxAnimation(x, y);
7255           ResetGfxAnimation(x, y + 1);
7256         }
7257
7258         if (MovDelay[x][y])
7259         {
7260           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7261           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7262
7263           MovDelay[x][y]--;
7264           if (MovDelay[x][y])
7265             return;
7266         }
7267
7268         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7269         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7270         Store[x][y + 1] = Store[x][y];
7271         Store[x][y] = 0;
7272
7273         PlayLevelSoundAction(x, y, ACTION_FILLING);
7274       }
7275     }
7276     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7277              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7278     {
7279       InitMovingField(x, y, MV_DOWN);
7280       started_moving = TRUE;
7281
7282       Feld[x][y] = EL_QUICKSAND_FILLING;
7283       Store[x][y] = element;
7284
7285       PlayLevelSoundAction(x, y, ACTION_FILLING);
7286     }
7287     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7288              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7289     {
7290       InitMovingField(x, y, MV_DOWN);
7291       started_moving = TRUE;
7292
7293       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7294       Store[x][y] = element;
7295
7296       PlayLevelSoundAction(x, y, ACTION_FILLING);
7297     }
7298     else if (element == EL_MAGIC_WALL_FULL)
7299     {
7300       if (IS_FREE(x, y + 1))
7301       {
7302         InitMovingField(x, y, MV_DOWN);
7303         started_moving = TRUE;
7304
7305         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7306         Store[x][y] = EL_CHANGED(Store[x][y]);
7307       }
7308       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7309       {
7310         if (!MovDelay[x][y])
7311           MovDelay[x][y] = TILEY / 4 + 1;
7312
7313         if (MovDelay[x][y])
7314         {
7315           MovDelay[x][y]--;
7316           if (MovDelay[x][y])
7317             return;
7318         }
7319
7320         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7321         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7322         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7323         Store[x][y] = 0;
7324       }
7325     }
7326     else if (element == EL_BD_MAGIC_WALL_FULL)
7327     {
7328       if (IS_FREE(x, y + 1))
7329       {
7330         InitMovingField(x, y, MV_DOWN);
7331         started_moving = TRUE;
7332
7333         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7334         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7335       }
7336       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7337       {
7338         if (!MovDelay[x][y])
7339           MovDelay[x][y] = TILEY / 4 + 1;
7340
7341         if (MovDelay[x][y])
7342         {
7343           MovDelay[x][y]--;
7344           if (MovDelay[x][y])
7345             return;
7346         }
7347
7348         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7349         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7350         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7351         Store[x][y] = 0;
7352       }
7353     }
7354     else if (element == EL_DC_MAGIC_WALL_FULL)
7355     {
7356       if (IS_FREE(x, y + 1))
7357       {
7358         InitMovingField(x, y, MV_DOWN);
7359         started_moving = TRUE;
7360
7361         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7362         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7363       }
7364       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7365       {
7366         if (!MovDelay[x][y])
7367           MovDelay[x][y] = TILEY / 4 + 1;
7368
7369         if (MovDelay[x][y])
7370         {
7371           MovDelay[x][y]--;
7372           if (MovDelay[x][y])
7373             return;
7374         }
7375
7376         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7377         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7378         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7379         Store[x][y] = 0;
7380       }
7381     }
7382     else if ((CAN_PASS_MAGIC_WALL(element) &&
7383               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7384                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7385              (CAN_PASS_DC_MAGIC_WALL(element) &&
7386               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7387
7388     {
7389       InitMovingField(x, y, MV_DOWN);
7390       started_moving = TRUE;
7391
7392       Feld[x][y] =
7393         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7394          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7395          EL_DC_MAGIC_WALL_FILLING);
7396       Store[x][y] = element;
7397     }
7398     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7399     {
7400       SplashAcid(x, y + 1);
7401
7402       InitMovingField(x, y, MV_DOWN);
7403       started_moving = TRUE;
7404
7405       Store[x][y] = EL_ACID;
7406     }
7407     else if (
7408              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7409               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7410              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7411               CAN_FALL(element) && WasJustFalling[x][y] &&
7412               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7413
7414              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7415               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7416               (Feld[x][y + 1] == EL_BLOCKED)))
7417     {
7418       /* this is needed for a special case not covered by calling "Impact()"
7419          from "ContinueMoving()": if an element moves to a tile directly below
7420          another element which was just falling on that tile (which was empty
7421          in the previous frame), the falling element above would just stop
7422          instead of smashing the element below (in previous version, the above
7423          element was just checked for "moving" instead of "falling", resulting
7424          in incorrect smashes caused by horizontal movement of the above
7425          element; also, the case of the player being the element to smash was
7426          simply not covered here... :-/ ) */
7427
7428       CheckCollision[x][y] = 0;
7429       CheckImpact[x][y] = 0;
7430
7431       Impact(x, y);
7432     }
7433     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7434     {
7435       if (MovDir[x][y] == MV_NONE)
7436       {
7437         InitMovingField(x, y, MV_DOWN);
7438         started_moving = TRUE;
7439       }
7440     }
7441     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7442     {
7443       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7444         MovDir[x][y] = MV_DOWN;
7445
7446       InitMovingField(x, y, MV_DOWN);
7447       started_moving = TRUE;
7448     }
7449     else if (element == EL_AMOEBA_DROP)
7450     {
7451       Feld[x][y] = EL_AMOEBA_GROWING;
7452       Store[x][y] = EL_AMOEBA_WET;
7453     }
7454     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7455               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7456              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7457              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7458     {
7459       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7460                                 (IS_FREE(x - 1, y + 1) ||
7461                                  Feld[x - 1][y + 1] == EL_ACID));
7462       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7463                                 (IS_FREE(x + 1, y + 1) ||
7464                                  Feld[x + 1][y + 1] == EL_ACID));
7465       boolean can_fall_any  = (can_fall_left || can_fall_right);
7466       boolean can_fall_both = (can_fall_left && can_fall_right);
7467       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7468
7469       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7470       {
7471         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7472           can_fall_right = FALSE;
7473         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7474           can_fall_left = FALSE;
7475         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7476           can_fall_right = FALSE;
7477         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7478           can_fall_left = FALSE;
7479
7480         can_fall_any  = (can_fall_left || can_fall_right);
7481         can_fall_both = FALSE;
7482       }
7483
7484       if (can_fall_both)
7485       {
7486         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7487           can_fall_right = FALSE;       /* slip down on left side */
7488         else
7489           can_fall_left = !(can_fall_right = RND(2));
7490
7491         can_fall_both = FALSE;
7492       }
7493
7494       if (can_fall_any)
7495       {
7496         /* if not determined otherwise, prefer left side for slipping down */
7497         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7498         started_moving = TRUE;
7499       }
7500     }
7501     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7502     {
7503       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7504       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7505       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7506       int belt_dir = game.belt_dir[belt_nr];
7507
7508       if ((belt_dir == MV_LEFT  && left_is_free) ||
7509           (belt_dir == MV_RIGHT && right_is_free))
7510       {
7511         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7512
7513         InitMovingField(x, y, belt_dir);
7514         started_moving = TRUE;
7515
7516         Pushed[x][y] = TRUE;
7517         Pushed[nextx][y] = TRUE;
7518
7519         GfxAction[x][y] = ACTION_DEFAULT;
7520       }
7521       else
7522       {
7523         MovDir[x][y] = 0;       /* if element was moving, stop it */
7524       }
7525     }
7526   }
7527
7528   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7529   if (CAN_MOVE(element) && !started_moving)
7530   {
7531     int move_pattern = element_info[element].move_pattern;
7532     int newx, newy;
7533
7534     Moving2Blocked(x, y, &newx, &newy);
7535
7536     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7537       return;
7538
7539     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7540         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7541     {
7542       WasJustMoving[x][y] = 0;
7543       CheckCollision[x][y] = 0;
7544
7545       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7546
7547       if (Feld[x][y] != element)        /* element has changed */
7548         return;
7549     }
7550
7551     if (!MovDelay[x][y])        /* start new movement phase */
7552     {
7553       /* all objects that can change their move direction after each step
7554          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7555
7556       if (element != EL_YAMYAM &&
7557           element != EL_DARK_YAMYAM &&
7558           element != EL_PACMAN &&
7559           !(move_pattern & MV_ANY_DIRECTION) &&
7560           move_pattern != MV_TURNING_LEFT &&
7561           move_pattern != MV_TURNING_RIGHT &&
7562           move_pattern != MV_TURNING_LEFT_RIGHT &&
7563           move_pattern != MV_TURNING_RIGHT_LEFT &&
7564           move_pattern != MV_TURNING_RANDOM)
7565       {
7566         TurnRound(x, y);
7567
7568         if (MovDelay[x][y] && (element == EL_BUG ||
7569                                element == EL_SPACESHIP ||
7570                                element == EL_SP_SNIKSNAK ||
7571                                element == EL_SP_ELECTRON ||
7572                                element == EL_MOLE))
7573           TEST_DrawLevelField(x, y);
7574       }
7575     }
7576
7577     if (MovDelay[x][y])         /* wait some time before next movement */
7578     {
7579       MovDelay[x][y]--;
7580
7581       if (element == EL_ROBOT ||
7582           element == EL_YAMYAM ||
7583           element == EL_DARK_YAMYAM)
7584       {
7585         DrawLevelElementAnimationIfNeeded(x, y, element);
7586         PlayLevelSoundAction(x, y, ACTION_WAITING);
7587       }
7588       else if (element == EL_SP_ELECTRON)
7589         DrawLevelElementAnimationIfNeeded(x, y, element);
7590       else if (element == EL_DRAGON)
7591       {
7592         int i;
7593         int dir = MovDir[x][y];
7594         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7595         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7596         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7597                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7598                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7599                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7600         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7601
7602         GfxAction[x][y] = ACTION_ATTACKING;
7603
7604         if (IS_PLAYER(x, y))
7605           DrawPlayerField(x, y);
7606         else
7607           TEST_DrawLevelField(x, y);
7608
7609         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7610
7611         for (i = 1; i <= 3; i++)
7612         {
7613           int xx = x + i * dx;
7614           int yy = y + i * dy;
7615           int sx = SCREENX(xx);
7616           int sy = SCREENY(yy);
7617           int flame_graphic = graphic + (i - 1);
7618
7619           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7620             break;
7621
7622           if (MovDelay[x][y])
7623           {
7624             int flamed = MovingOrBlocked2Element(xx, yy);
7625
7626             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7627               Bang(xx, yy);
7628             else
7629               RemoveMovingField(xx, yy);
7630
7631             ChangeDelay[xx][yy] = 0;
7632
7633             Feld[xx][yy] = EL_FLAMES;
7634
7635             if (IN_SCR_FIELD(sx, sy))
7636             {
7637               TEST_DrawLevelFieldCrumbled(xx, yy);
7638               DrawGraphic(sx, sy, flame_graphic, frame);
7639             }
7640           }
7641           else
7642           {
7643             if (Feld[xx][yy] == EL_FLAMES)
7644               Feld[xx][yy] = EL_EMPTY;
7645             TEST_DrawLevelField(xx, yy);
7646           }
7647         }
7648       }
7649
7650       if (MovDelay[x][y])       /* element still has to wait some time */
7651       {
7652         PlayLevelSoundAction(x, y, ACTION_WAITING);
7653
7654         return;
7655       }
7656     }
7657
7658     /* now make next step */
7659
7660     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7661
7662     if (DONT_COLLIDE_WITH(element) &&
7663         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7664         !PLAYER_ENEMY_PROTECTED(newx, newy))
7665     {
7666       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7667
7668       return;
7669     }
7670
7671     else if (CAN_MOVE_INTO_ACID(element) &&
7672              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7673              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7674              (MovDir[x][y] == MV_DOWN ||
7675               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7676     {
7677       SplashAcid(newx, newy);
7678       Store[x][y] = EL_ACID;
7679     }
7680     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7681     {
7682       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7683           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7684           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7685           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7686       {
7687         RemoveField(x, y);
7688         TEST_DrawLevelField(x, y);
7689
7690         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7691         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7692           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7693
7694         local_player->friends_still_needed--;
7695         if (!local_player->friends_still_needed &&
7696             !local_player->GameOver && AllPlayersGone)
7697           PlayerWins(local_player);
7698
7699         return;
7700       }
7701       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7702       {
7703         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7704           TEST_DrawLevelField(newx, newy);
7705         else
7706           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7707       }
7708       else if (!IS_FREE(newx, newy))
7709       {
7710         GfxAction[x][y] = ACTION_WAITING;
7711
7712         if (IS_PLAYER(x, y))
7713           DrawPlayerField(x, y);
7714         else
7715           TEST_DrawLevelField(x, y);
7716
7717         return;
7718       }
7719     }
7720     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7721     {
7722       if (IS_FOOD_PIG(Feld[newx][newy]))
7723       {
7724         if (IS_MOVING(newx, newy))
7725           RemoveMovingField(newx, newy);
7726         else
7727         {
7728           Feld[newx][newy] = EL_EMPTY;
7729           TEST_DrawLevelField(newx, newy);
7730         }
7731
7732         PlayLevelSound(x, y, SND_PIG_DIGGING);
7733       }
7734       else if (!IS_FREE(newx, newy))
7735       {
7736         if (IS_PLAYER(x, y))
7737           DrawPlayerField(x, y);
7738         else
7739           TEST_DrawLevelField(x, y);
7740
7741         return;
7742       }
7743     }
7744     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7745     {
7746       if (Store[x][y] != EL_EMPTY)
7747       {
7748         boolean can_clone = FALSE;
7749         int xx, yy;
7750
7751         /* check if element to clone is still there */
7752         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7753         {
7754           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7755           {
7756             can_clone = TRUE;
7757
7758             break;
7759           }
7760         }
7761
7762         /* cannot clone or target field not free anymore -- do not clone */
7763         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7764           Store[x][y] = EL_EMPTY;
7765       }
7766
7767       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7768       {
7769         if (IS_MV_DIAGONAL(MovDir[x][y]))
7770         {
7771           int diagonal_move_dir = MovDir[x][y];
7772           int stored = Store[x][y];
7773           int change_delay = 8;
7774           int graphic;
7775
7776           /* android is moving diagonally */
7777
7778           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7779
7780           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7781           GfxElement[x][y] = EL_EMC_ANDROID;
7782           GfxAction[x][y] = ACTION_SHRINKING;
7783           GfxDir[x][y] = diagonal_move_dir;
7784           ChangeDelay[x][y] = change_delay;
7785
7786           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7787                                    GfxDir[x][y]);
7788
7789           DrawLevelGraphicAnimation(x, y, graphic);
7790           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7791
7792           if (Feld[newx][newy] == EL_ACID)
7793           {
7794             SplashAcid(newx, newy);
7795
7796             return;
7797           }
7798
7799           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7800
7801           Store[newx][newy] = EL_EMC_ANDROID;
7802           GfxElement[newx][newy] = EL_EMC_ANDROID;
7803           GfxAction[newx][newy] = ACTION_GROWING;
7804           GfxDir[newx][newy] = diagonal_move_dir;
7805           ChangeDelay[newx][newy] = change_delay;
7806
7807           graphic = el_act_dir2img(GfxElement[newx][newy],
7808                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7809
7810           DrawLevelGraphicAnimation(newx, newy, graphic);
7811           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7812
7813           return;
7814         }
7815         else
7816         {
7817           Feld[newx][newy] = EL_EMPTY;
7818           TEST_DrawLevelField(newx, newy);
7819
7820           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7821         }
7822       }
7823       else if (!IS_FREE(newx, newy))
7824       {
7825         return;
7826       }
7827     }
7828     else if (IS_CUSTOM_ELEMENT(element) &&
7829              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7830     {
7831       if (!DigFieldByCE(newx, newy, element))
7832         return;
7833
7834       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7835       {
7836         RunnerVisit[x][y] = FrameCounter;
7837         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7838       }
7839     }
7840     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7841     {
7842       if (!IS_FREE(newx, newy))
7843       {
7844         if (IS_PLAYER(x, y))
7845           DrawPlayerField(x, y);
7846         else
7847           TEST_DrawLevelField(x, y);
7848
7849         return;
7850       }
7851       else
7852       {
7853         boolean wanna_flame = !RND(10);
7854         int dx = newx - x, dy = newy - y;
7855         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7856         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7857         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7858                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7859         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7860                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7861
7862         if ((wanna_flame ||
7863              IS_CLASSIC_ENEMY(element1) ||
7864              IS_CLASSIC_ENEMY(element2)) &&
7865             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7866             element1 != EL_FLAMES && element2 != EL_FLAMES)
7867         {
7868           ResetGfxAnimation(x, y);
7869           GfxAction[x][y] = ACTION_ATTACKING;
7870
7871           if (IS_PLAYER(x, y))
7872             DrawPlayerField(x, y);
7873           else
7874             TEST_DrawLevelField(x, y);
7875
7876           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7877
7878           MovDelay[x][y] = 50;
7879
7880           Feld[newx][newy] = EL_FLAMES;
7881           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7882             Feld[newx1][newy1] = EL_FLAMES;
7883           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7884             Feld[newx2][newy2] = EL_FLAMES;
7885
7886           return;
7887         }
7888       }
7889     }
7890     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7891              Feld[newx][newy] == EL_DIAMOND)
7892     {
7893       if (IS_MOVING(newx, newy))
7894         RemoveMovingField(newx, newy);
7895       else
7896       {
7897         Feld[newx][newy] = EL_EMPTY;
7898         TEST_DrawLevelField(newx, newy);
7899       }
7900
7901       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7902     }
7903     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7904              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7905     {
7906       if (AmoebaNr[newx][newy])
7907       {
7908         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7909         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7910             Feld[newx][newy] == EL_BD_AMOEBA)
7911           AmoebaCnt[AmoebaNr[newx][newy]]--;
7912       }
7913
7914       if (IS_MOVING(newx, newy))
7915       {
7916         RemoveMovingField(newx, newy);
7917       }
7918       else
7919       {
7920         Feld[newx][newy] = EL_EMPTY;
7921         TEST_DrawLevelField(newx, newy);
7922       }
7923
7924       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7925     }
7926     else if ((element == EL_PACMAN || element == EL_MOLE)
7927              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7928     {
7929       if (AmoebaNr[newx][newy])
7930       {
7931         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7932         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7933             Feld[newx][newy] == EL_BD_AMOEBA)
7934           AmoebaCnt[AmoebaNr[newx][newy]]--;
7935       }
7936
7937       if (element == EL_MOLE)
7938       {
7939         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7940         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7941
7942         ResetGfxAnimation(x, y);
7943         GfxAction[x][y] = ACTION_DIGGING;
7944         TEST_DrawLevelField(x, y);
7945
7946         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7947
7948         return;                         /* wait for shrinking amoeba */
7949       }
7950       else      /* element == EL_PACMAN */
7951       {
7952         Feld[newx][newy] = EL_EMPTY;
7953         TEST_DrawLevelField(newx, newy);
7954         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7955       }
7956     }
7957     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7958              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7959               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7960     {
7961       /* wait for shrinking amoeba to completely disappear */
7962       return;
7963     }
7964     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7965     {
7966       /* object was running against a wall */
7967
7968       TurnRound(x, y);
7969
7970       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7971         DrawLevelElementAnimation(x, y, element);
7972
7973       if (DONT_TOUCH(element))
7974         TestIfBadThingTouchesPlayer(x, y);
7975
7976       return;
7977     }
7978
7979     InitMovingField(x, y, MovDir[x][y]);
7980
7981     PlayLevelSoundAction(x, y, ACTION_MOVING);
7982   }
7983
7984   if (MovDir[x][y])
7985     ContinueMoving(x, y);
7986 }
7987
7988 void ContinueMoving(int x, int y)
7989 {
7990   int element = Feld[x][y];
7991   struct ElementInfo *ei = &element_info[element];
7992   int direction = MovDir[x][y];
7993   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7994   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7995   int newx = x + dx, newy = y + dy;
7996   int stored = Store[x][y];
7997   int stored_new = Store[newx][newy];
7998   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7999   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8000   boolean last_line = (newy == lev_fieldy - 1);
8001
8002   MovPos[x][y] += getElementMoveStepsize(x, y);
8003
8004   if (pushed_by_player) /* special case: moving object pushed by player */
8005     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8006
8007   if (ABS(MovPos[x][y]) < TILEX)
8008   {
8009     TEST_DrawLevelField(x, y);
8010
8011     return;     /* element is still moving */
8012   }
8013
8014   /* element reached destination field */
8015
8016   Feld[x][y] = EL_EMPTY;
8017   Feld[newx][newy] = element;
8018   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8019
8020   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8021   {
8022     element = Feld[newx][newy] = EL_ACID;
8023   }
8024   else if (element == EL_MOLE)
8025   {
8026     Feld[x][y] = EL_SAND;
8027
8028     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8029   }
8030   else if (element == EL_QUICKSAND_FILLING)
8031   {
8032     element = Feld[newx][newy] = get_next_element(element);
8033     Store[newx][newy] = Store[x][y];
8034   }
8035   else if (element == EL_QUICKSAND_EMPTYING)
8036   {
8037     Feld[x][y] = get_next_element(element);
8038     element = Feld[newx][newy] = Store[x][y];
8039   }
8040   else if (element == EL_QUICKSAND_FAST_FILLING)
8041   {
8042     element = Feld[newx][newy] = get_next_element(element);
8043     Store[newx][newy] = Store[x][y];
8044   }
8045   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8046   {
8047     Feld[x][y] = get_next_element(element);
8048     element = Feld[newx][newy] = Store[x][y];
8049   }
8050   else if (element == EL_MAGIC_WALL_FILLING)
8051   {
8052     element = Feld[newx][newy] = get_next_element(element);
8053     if (!game.magic_wall_active)
8054       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8055     Store[newx][newy] = Store[x][y];
8056   }
8057   else if (element == EL_MAGIC_WALL_EMPTYING)
8058   {
8059     Feld[x][y] = get_next_element(element);
8060     if (!game.magic_wall_active)
8061       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8062     element = Feld[newx][newy] = Store[x][y];
8063
8064     InitField(newx, newy, FALSE);
8065   }
8066   else if (element == EL_BD_MAGIC_WALL_FILLING)
8067   {
8068     element = Feld[newx][newy] = get_next_element(element);
8069     if (!game.magic_wall_active)
8070       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8071     Store[newx][newy] = Store[x][y];
8072   }
8073   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8074   {
8075     Feld[x][y] = get_next_element(element);
8076     if (!game.magic_wall_active)
8077       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8078     element = Feld[newx][newy] = Store[x][y];
8079
8080     InitField(newx, newy, FALSE);
8081   }
8082   else if (element == EL_DC_MAGIC_WALL_FILLING)
8083   {
8084     element = Feld[newx][newy] = get_next_element(element);
8085     if (!game.magic_wall_active)
8086       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8087     Store[newx][newy] = Store[x][y];
8088   }
8089   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8090   {
8091     Feld[x][y] = get_next_element(element);
8092     if (!game.magic_wall_active)
8093       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8094     element = Feld[newx][newy] = Store[x][y];
8095
8096     InitField(newx, newy, FALSE);
8097   }
8098   else if (element == EL_AMOEBA_DROPPING)
8099   {
8100     Feld[x][y] = get_next_element(element);
8101     element = Feld[newx][newy] = Store[x][y];
8102   }
8103   else if (element == EL_SOKOBAN_OBJECT)
8104   {
8105     if (Back[x][y])
8106       Feld[x][y] = Back[x][y];
8107
8108     if (Back[newx][newy])
8109       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8110
8111     Back[x][y] = Back[newx][newy] = 0;
8112   }
8113
8114   Store[x][y] = EL_EMPTY;
8115   MovPos[x][y] = 0;
8116   MovDir[x][y] = 0;
8117   MovDelay[x][y] = 0;
8118
8119   MovDelay[newx][newy] = 0;
8120
8121   if (CAN_CHANGE_OR_HAS_ACTION(element))
8122   {
8123     /* copy element change control values to new field */
8124     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8125     ChangePage[newx][newy]  = ChangePage[x][y];
8126     ChangeCount[newx][newy] = ChangeCount[x][y];
8127     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8128   }
8129
8130   CustomValue[newx][newy] = CustomValue[x][y];
8131
8132   ChangeDelay[x][y] = 0;
8133   ChangePage[x][y] = -1;
8134   ChangeCount[x][y] = 0;
8135   ChangeEvent[x][y] = -1;
8136
8137   CustomValue[x][y] = 0;
8138
8139   /* copy animation control values to new field */
8140   GfxFrame[newx][newy]  = GfxFrame[x][y];
8141   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8142   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8143   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8144
8145   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8146
8147   /* some elements can leave other elements behind after moving */
8148   if (ei->move_leave_element != EL_EMPTY &&
8149       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8150       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8151   {
8152     int move_leave_element = ei->move_leave_element;
8153
8154     /* this makes it possible to leave the removed element again */
8155     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8156       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8157
8158     Feld[x][y] = move_leave_element;
8159
8160     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8161       MovDir[x][y] = direction;
8162
8163     InitField(x, y, FALSE);
8164
8165     if (GFX_CRUMBLED(Feld[x][y]))
8166       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8167
8168     if (ELEM_IS_PLAYER(move_leave_element))
8169       RelocatePlayer(x, y, move_leave_element);
8170   }
8171
8172   /* do this after checking for left-behind element */
8173   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8174
8175   if (!CAN_MOVE(element) ||
8176       (CAN_FALL(element) && direction == MV_DOWN &&
8177        (element == EL_SPRING ||
8178         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8179         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8180     GfxDir[x][y] = MovDir[newx][newy] = 0;
8181
8182   TEST_DrawLevelField(x, y);
8183   TEST_DrawLevelField(newx, newy);
8184
8185   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8186
8187   /* prevent pushed element from moving on in pushed direction */
8188   if (pushed_by_player && CAN_MOVE(element) &&
8189       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8190       !(element_info[element].move_pattern & direction))
8191     TurnRound(newx, newy);
8192
8193   /* prevent elements on conveyor belt from moving on in last direction */
8194   if (pushed_by_conveyor && CAN_FALL(element) &&
8195       direction & MV_HORIZONTAL)
8196     MovDir[newx][newy] = 0;
8197
8198   if (!pushed_by_player)
8199   {
8200     int nextx = newx + dx, nexty = newy + dy;
8201     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8202
8203     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8204
8205     if (CAN_FALL(element) && direction == MV_DOWN)
8206       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8207
8208     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8209       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8210
8211     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8212       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8213   }
8214
8215   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8216   {
8217     TestIfBadThingTouchesPlayer(newx, newy);
8218     TestIfBadThingTouchesFriend(newx, newy);
8219
8220     if (!IS_CUSTOM_ELEMENT(element))
8221       TestIfBadThingTouchesOtherBadThing(newx, newy);
8222   }
8223   else if (element == EL_PENGUIN)
8224     TestIfFriendTouchesBadThing(newx, newy);
8225
8226   if (DONT_GET_HIT_BY(element))
8227   {
8228     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8229   }
8230
8231   /* give the player one last chance (one more frame) to move away */
8232   if (CAN_FALL(element) && direction == MV_DOWN &&
8233       (last_line || (!IS_FREE(x, newy + 1) &&
8234                      (!IS_PLAYER(x, newy + 1) ||
8235                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8236     Impact(x, newy);
8237
8238   if (pushed_by_player && !game.use_change_when_pushing_bug)
8239   {
8240     int push_side = MV_DIR_OPPOSITE(direction);
8241     struct PlayerInfo *player = PLAYERINFO(x, y);
8242
8243     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8244                                player->index_bit, push_side);
8245     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8246                                         player->index_bit, push_side);
8247   }
8248
8249   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8250     MovDelay[newx][newy] = 1;
8251
8252   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8253
8254   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8255   TestIfElementHitsCustomElement(newx, newy, direction);
8256   TestIfPlayerTouchesCustomElement(newx, newy);
8257   TestIfElementTouchesCustomElement(newx, newy);
8258
8259   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8260       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8261     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8262                              MV_DIR_OPPOSITE(direction));
8263 }
8264
8265 int AmoebeNachbarNr(int ax, int ay)
8266 {
8267   int i;
8268   int element = Feld[ax][ay];
8269   int group_nr = 0;
8270   static int xy[4][2] =
8271   {
8272     { 0, -1 },
8273     { -1, 0 },
8274     { +1, 0 },
8275     { 0, +1 }
8276   };
8277
8278   for (i = 0; i < NUM_DIRECTIONS; i++)
8279   {
8280     int x = ax + xy[i][0];
8281     int y = ay + xy[i][1];
8282
8283     if (!IN_LEV_FIELD(x, y))
8284       continue;
8285
8286     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8287       group_nr = AmoebaNr[x][y];
8288   }
8289
8290   return group_nr;
8291 }
8292
8293 void AmoebenVereinigen(int ax, int ay)
8294 {
8295   int i, x, y, xx, yy;
8296   int new_group_nr = AmoebaNr[ax][ay];
8297   static int xy[4][2] =
8298   {
8299     { 0, -1 },
8300     { -1, 0 },
8301     { +1, 0 },
8302     { 0, +1 }
8303   };
8304
8305   if (new_group_nr == 0)
8306     return;
8307
8308   for (i = 0; i < NUM_DIRECTIONS; i++)
8309   {
8310     x = ax + xy[i][0];
8311     y = ay + xy[i][1];
8312
8313     if (!IN_LEV_FIELD(x, y))
8314       continue;
8315
8316     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8317          Feld[x][y] == EL_BD_AMOEBA ||
8318          Feld[x][y] == EL_AMOEBA_DEAD) &&
8319         AmoebaNr[x][y] != new_group_nr)
8320     {
8321       int old_group_nr = AmoebaNr[x][y];
8322
8323       if (old_group_nr == 0)
8324         return;
8325
8326       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8327       AmoebaCnt[old_group_nr] = 0;
8328       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8329       AmoebaCnt2[old_group_nr] = 0;
8330
8331       SCAN_PLAYFIELD(xx, yy)
8332       {
8333         if (AmoebaNr[xx][yy] == old_group_nr)
8334           AmoebaNr[xx][yy] = new_group_nr;
8335       }
8336     }
8337   }
8338 }
8339
8340 void AmoebeUmwandeln(int ax, int ay)
8341 {
8342   int i, x, y;
8343
8344   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8345   {
8346     int group_nr = AmoebaNr[ax][ay];
8347
8348 #ifdef DEBUG
8349     if (group_nr == 0)
8350     {
8351       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8352       printf("AmoebeUmwandeln(): This should never happen!\n");
8353       return;
8354     }
8355 #endif
8356
8357     SCAN_PLAYFIELD(x, y)
8358     {
8359       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8360       {
8361         AmoebaNr[x][y] = 0;
8362         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8363       }
8364     }
8365
8366     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8367                             SND_AMOEBA_TURNING_TO_GEM :
8368                             SND_AMOEBA_TURNING_TO_ROCK));
8369     Bang(ax, ay);
8370   }
8371   else
8372   {
8373     static int xy[4][2] =
8374     {
8375       { 0, -1 },
8376       { -1, 0 },
8377       { +1, 0 },
8378       { 0, +1 }
8379     };
8380
8381     for (i = 0; i < NUM_DIRECTIONS; i++)
8382     {
8383       x = ax + xy[i][0];
8384       y = ay + xy[i][1];
8385
8386       if (!IN_LEV_FIELD(x, y))
8387         continue;
8388
8389       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8390       {
8391         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8392                               SND_AMOEBA_TURNING_TO_GEM :
8393                               SND_AMOEBA_TURNING_TO_ROCK));
8394         Bang(x, y);
8395       }
8396     }
8397   }
8398 }
8399
8400 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8401 {
8402   int x, y;
8403   int group_nr = AmoebaNr[ax][ay];
8404   boolean done = FALSE;
8405
8406 #ifdef DEBUG
8407   if (group_nr == 0)
8408   {
8409     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8410     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8411     return;
8412   }
8413 #endif
8414
8415   SCAN_PLAYFIELD(x, y)
8416   {
8417     if (AmoebaNr[x][y] == group_nr &&
8418         (Feld[x][y] == EL_AMOEBA_DEAD ||
8419          Feld[x][y] == EL_BD_AMOEBA ||
8420          Feld[x][y] == EL_AMOEBA_GROWING))
8421     {
8422       AmoebaNr[x][y] = 0;
8423       Feld[x][y] = new_element;
8424       InitField(x, y, FALSE);
8425       TEST_DrawLevelField(x, y);
8426       done = TRUE;
8427     }
8428   }
8429
8430   if (done)
8431     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8432                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8433                             SND_BD_AMOEBA_TURNING_TO_GEM));
8434 }
8435
8436 void AmoebeWaechst(int x, int y)
8437 {
8438   static unsigned int sound_delay = 0;
8439   static unsigned int sound_delay_value = 0;
8440
8441   if (!MovDelay[x][y])          /* start new growing cycle */
8442   {
8443     MovDelay[x][y] = 7;
8444
8445     if (DelayReached(&sound_delay, sound_delay_value))
8446     {
8447       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8448       sound_delay_value = 30;
8449     }
8450   }
8451
8452   if (MovDelay[x][y])           /* wait some time before growing bigger */
8453   {
8454     MovDelay[x][y]--;
8455     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8456     {
8457       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8458                                            6 - MovDelay[x][y]);
8459
8460       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8461     }
8462
8463     if (!MovDelay[x][y])
8464     {
8465       Feld[x][y] = Store[x][y];
8466       Store[x][y] = 0;
8467       TEST_DrawLevelField(x, y);
8468     }
8469   }
8470 }
8471
8472 void AmoebaDisappearing(int x, int y)
8473 {
8474   static unsigned int sound_delay = 0;
8475   static unsigned int sound_delay_value = 0;
8476
8477   if (!MovDelay[x][y])          /* start new shrinking cycle */
8478   {
8479     MovDelay[x][y] = 7;
8480
8481     if (DelayReached(&sound_delay, sound_delay_value))
8482       sound_delay_value = 30;
8483   }
8484
8485   if (MovDelay[x][y])           /* wait some time before shrinking */
8486   {
8487     MovDelay[x][y]--;
8488     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8489     {
8490       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8491                                            6 - MovDelay[x][y]);
8492
8493       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8494     }
8495
8496     if (!MovDelay[x][y])
8497     {
8498       Feld[x][y] = EL_EMPTY;
8499       TEST_DrawLevelField(x, y);
8500
8501       /* don't let mole enter this field in this cycle;
8502          (give priority to objects falling to this field from above) */
8503       Stop[x][y] = TRUE;
8504     }
8505   }
8506 }
8507
8508 void AmoebeAbleger(int ax, int ay)
8509 {
8510   int i;
8511   int element = Feld[ax][ay];
8512   int graphic = el2img(element);
8513   int newax = ax, neway = ay;
8514   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8515   static int xy[4][2] =
8516   {
8517     { 0, -1 },
8518     { -1, 0 },
8519     { +1, 0 },
8520     { 0, +1 }
8521   };
8522
8523   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8524   {
8525     Feld[ax][ay] = EL_AMOEBA_DEAD;
8526     TEST_DrawLevelField(ax, ay);
8527     return;
8528   }
8529
8530   if (IS_ANIMATED(graphic))
8531     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8532
8533   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8534     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8535
8536   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8537   {
8538     MovDelay[ax][ay]--;
8539     if (MovDelay[ax][ay])
8540       return;
8541   }
8542
8543   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8544   {
8545     int start = RND(4);
8546     int x = ax + xy[start][0];
8547     int y = ay + xy[start][1];
8548
8549     if (!IN_LEV_FIELD(x, y))
8550       return;
8551
8552     if (IS_FREE(x, y) ||
8553         CAN_GROW_INTO(Feld[x][y]) ||
8554         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8555         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8556     {
8557       newax = x;
8558       neway = y;
8559     }
8560
8561     if (newax == ax && neway == ay)
8562       return;
8563   }
8564   else                          /* normal or "filled" (BD style) amoeba */
8565   {
8566     int start = RND(4);
8567     boolean waiting_for_player = FALSE;
8568
8569     for (i = 0; i < NUM_DIRECTIONS; i++)
8570     {
8571       int j = (start + i) % 4;
8572       int x = ax + xy[j][0];
8573       int y = ay + xy[j][1];
8574
8575       if (!IN_LEV_FIELD(x, y))
8576         continue;
8577
8578       if (IS_FREE(x, y) ||
8579           CAN_GROW_INTO(Feld[x][y]) ||
8580           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8581           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8582       {
8583         newax = x;
8584         neway = y;
8585         break;
8586       }
8587       else if (IS_PLAYER(x, y))
8588         waiting_for_player = TRUE;
8589     }
8590
8591     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8592     {
8593       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8594       {
8595         Feld[ax][ay] = EL_AMOEBA_DEAD;
8596         TEST_DrawLevelField(ax, ay);
8597         AmoebaCnt[AmoebaNr[ax][ay]]--;
8598
8599         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8600         {
8601           if (element == EL_AMOEBA_FULL)
8602             AmoebeUmwandeln(ax, ay);
8603           else if (element == EL_BD_AMOEBA)
8604             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8605         }
8606       }
8607       return;
8608     }
8609     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8610     {
8611       /* amoeba gets larger by growing in some direction */
8612
8613       int new_group_nr = AmoebaNr[ax][ay];
8614
8615 #ifdef DEBUG
8616   if (new_group_nr == 0)
8617   {
8618     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8619     printf("AmoebeAbleger(): This should never happen!\n");
8620     return;
8621   }
8622 #endif
8623
8624       AmoebaNr[newax][neway] = new_group_nr;
8625       AmoebaCnt[new_group_nr]++;
8626       AmoebaCnt2[new_group_nr]++;
8627
8628       /* if amoeba touches other amoeba(s) after growing, unify them */
8629       AmoebenVereinigen(newax, neway);
8630
8631       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8632       {
8633         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8634         return;
8635       }
8636     }
8637   }
8638
8639   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8640       (neway == lev_fieldy - 1 && newax != ax))
8641   {
8642     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8643     Store[newax][neway] = element;
8644   }
8645   else if (neway == ay || element == EL_EMC_DRIPPER)
8646   {
8647     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8648
8649     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8650   }
8651   else
8652   {
8653     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8654     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8655     Store[ax][ay] = EL_AMOEBA_DROP;
8656     ContinueMoving(ax, ay);
8657     return;
8658   }
8659
8660   TEST_DrawLevelField(newax, neway);
8661 }
8662
8663 void Life(int ax, int ay)
8664 {
8665   int x1, y1, x2, y2;
8666   int life_time = 40;
8667   int element = Feld[ax][ay];
8668   int graphic = el2img(element);
8669   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8670                          level.biomaze);
8671   boolean changed = FALSE;
8672
8673   if (IS_ANIMATED(graphic))
8674     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8675
8676   if (Stop[ax][ay])
8677     return;
8678
8679   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8680     MovDelay[ax][ay] = life_time;
8681
8682   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8683   {
8684     MovDelay[ax][ay]--;
8685     if (MovDelay[ax][ay])
8686       return;
8687   }
8688
8689   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8690   {
8691     int xx = ax+x1, yy = ay+y1;
8692     int nachbarn = 0;
8693
8694     if (!IN_LEV_FIELD(xx, yy))
8695       continue;
8696
8697     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8698     {
8699       int x = xx+x2, y = yy+y2;
8700
8701       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8702         continue;
8703
8704       if (((Feld[x][y] == element ||
8705             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8706            !Stop[x][y]) ||
8707           (IS_FREE(x, y) && Stop[x][y]))
8708         nachbarn++;
8709     }
8710
8711     if (xx == ax && yy == ay)           /* field in the middle */
8712     {
8713       if (nachbarn < life_parameter[0] ||
8714           nachbarn > life_parameter[1])
8715       {
8716         Feld[xx][yy] = EL_EMPTY;
8717         if (!Stop[xx][yy])
8718           TEST_DrawLevelField(xx, yy);
8719         Stop[xx][yy] = TRUE;
8720         changed = TRUE;
8721       }
8722     }
8723     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8724     {                                   /* free border field */
8725       if (nachbarn >= life_parameter[2] &&
8726           nachbarn <= life_parameter[3])
8727       {
8728         Feld[xx][yy] = element;
8729         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8730         if (!Stop[xx][yy])
8731           TEST_DrawLevelField(xx, yy);
8732         Stop[xx][yy] = TRUE;
8733         changed = TRUE;
8734       }
8735     }
8736   }
8737
8738   if (changed)
8739     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8740                    SND_GAME_OF_LIFE_GROWING);
8741 }
8742
8743 static void InitRobotWheel(int x, int y)
8744 {
8745   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8746 }
8747
8748 static void RunRobotWheel(int x, int y)
8749 {
8750   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8751 }
8752
8753 static void StopRobotWheel(int x, int y)
8754 {
8755   if (ZX == x && ZY == y)
8756   {
8757     ZX = ZY = -1;
8758
8759     game.robot_wheel_active = FALSE;
8760   }
8761 }
8762
8763 static void InitTimegateWheel(int x, int y)
8764 {
8765   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8766 }
8767
8768 static void RunTimegateWheel(int x, int y)
8769 {
8770   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8771 }
8772
8773 static void InitMagicBallDelay(int x, int y)
8774 {
8775   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8776 }
8777
8778 static void ActivateMagicBall(int bx, int by)
8779 {
8780   int x, y;
8781
8782   if (level.ball_random)
8783   {
8784     int pos_border = RND(8);    /* select one of the eight border elements */
8785     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8786     int xx = pos_content % 3;
8787     int yy = pos_content / 3;
8788
8789     x = bx - 1 + xx;
8790     y = by - 1 + yy;
8791
8792     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8793       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8794   }
8795   else
8796   {
8797     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8798     {
8799       int xx = x - bx + 1;
8800       int yy = y - by + 1;
8801
8802       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8803         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8804     }
8805   }
8806
8807   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8808 }
8809
8810 void CheckExit(int x, int y)
8811 {
8812   if (local_player->gems_still_needed > 0 ||
8813       local_player->sokobanfields_still_needed > 0 ||
8814       local_player->lights_still_needed > 0)
8815   {
8816     int element = Feld[x][y];
8817     int graphic = el2img(element);
8818
8819     if (IS_ANIMATED(graphic))
8820       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8821
8822     return;
8823   }
8824
8825   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8826     return;
8827
8828   Feld[x][y] = EL_EXIT_OPENING;
8829
8830   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8831 }
8832
8833 void CheckExitEM(int x, int y)
8834 {
8835   if (local_player->gems_still_needed > 0 ||
8836       local_player->sokobanfields_still_needed > 0 ||
8837       local_player->lights_still_needed > 0)
8838   {
8839     int element = Feld[x][y];
8840     int graphic = el2img(element);
8841
8842     if (IS_ANIMATED(graphic))
8843       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8844
8845     return;
8846   }
8847
8848   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8849     return;
8850
8851   Feld[x][y] = EL_EM_EXIT_OPENING;
8852
8853   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8854 }
8855
8856 void CheckExitSteel(int x, int y)
8857 {
8858   if (local_player->gems_still_needed > 0 ||
8859       local_player->sokobanfields_still_needed > 0 ||
8860       local_player->lights_still_needed > 0)
8861   {
8862     int element = Feld[x][y];
8863     int graphic = el2img(element);
8864
8865     if (IS_ANIMATED(graphic))
8866       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8867
8868     return;
8869   }
8870
8871   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8872     return;
8873
8874   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8875
8876   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8877 }
8878
8879 void CheckExitSteelEM(int x, int y)
8880 {
8881   if (local_player->gems_still_needed > 0 ||
8882       local_player->sokobanfields_still_needed > 0 ||
8883       local_player->lights_still_needed > 0)
8884   {
8885     int element = Feld[x][y];
8886     int graphic = el2img(element);
8887
8888     if (IS_ANIMATED(graphic))
8889       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8890
8891     return;
8892   }
8893
8894   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8895     return;
8896
8897   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8898
8899   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8900 }
8901
8902 void CheckExitSP(int x, int y)
8903 {
8904   if (local_player->gems_still_needed > 0)
8905   {
8906     int element = Feld[x][y];
8907     int graphic = el2img(element);
8908
8909     if (IS_ANIMATED(graphic))
8910       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8911
8912     return;
8913   }
8914
8915   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8916     return;
8917
8918   Feld[x][y] = EL_SP_EXIT_OPENING;
8919
8920   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8921 }
8922
8923 static void CloseAllOpenTimegates()
8924 {
8925   int x, y;
8926
8927   SCAN_PLAYFIELD(x, y)
8928   {
8929     int element = Feld[x][y];
8930
8931     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8932     {
8933       Feld[x][y] = EL_TIMEGATE_CLOSING;
8934
8935       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8936     }
8937   }
8938 }
8939
8940 void DrawTwinkleOnField(int x, int y)
8941 {
8942   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8943     return;
8944
8945   if (Feld[x][y] == EL_BD_DIAMOND)
8946     return;
8947
8948   if (MovDelay[x][y] == 0)      /* next animation frame */
8949     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8950
8951   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8952   {
8953     MovDelay[x][y]--;
8954
8955     DrawLevelElementAnimation(x, y, Feld[x][y]);
8956
8957     if (MovDelay[x][y] != 0)
8958     {
8959       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8960                                            10 - MovDelay[x][y]);
8961
8962       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8963     }
8964   }
8965 }
8966
8967 void MauerWaechst(int x, int y)
8968 {
8969   int delay = 6;
8970
8971   if (!MovDelay[x][y])          /* next animation frame */
8972     MovDelay[x][y] = 3 * delay;
8973
8974   if (MovDelay[x][y])           /* wait some time before next frame */
8975   {
8976     MovDelay[x][y]--;
8977
8978     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8979     {
8980       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8981       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8982
8983       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8984     }
8985
8986     if (!MovDelay[x][y])
8987     {
8988       if (MovDir[x][y] == MV_LEFT)
8989       {
8990         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8991           TEST_DrawLevelField(x - 1, y);
8992       }
8993       else if (MovDir[x][y] == MV_RIGHT)
8994       {
8995         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8996           TEST_DrawLevelField(x + 1, y);
8997       }
8998       else if (MovDir[x][y] == MV_UP)
8999       {
9000         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9001           TEST_DrawLevelField(x, y - 1);
9002       }
9003       else
9004       {
9005         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9006           TEST_DrawLevelField(x, y + 1);
9007       }
9008
9009       Feld[x][y] = Store[x][y];
9010       Store[x][y] = 0;
9011       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9012       TEST_DrawLevelField(x, y);
9013     }
9014   }
9015 }
9016
9017 void MauerAbleger(int ax, int ay)
9018 {
9019   int element = Feld[ax][ay];
9020   int graphic = el2img(element);
9021   boolean oben_frei = FALSE, unten_frei = FALSE;
9022   boolean links_frei = FALSE, rechts_frei = FALSE;
9023   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9024   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9025   boolean new_wall = FALSE;
9026
9027   if (IS_ANIMATED(graphic))
9028     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9029
9030   if (!MovDelay[ax][ay])        /* start building new wall */
9031     MovDelay[ax][ay] = 6;
9032
9033   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9034   {
9035     MovDelay[ax][ay]--;
9036     if (MovDelay[ax][ay])
9037       return;
9038   }
9039
9040   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9041     oben_frei = TRUE;
9042   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9043     unten_frei = TRUE;
9044   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9045     links_frei = TRUE;
9046   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9047     rechts_frei = TRUE;
9048
9049   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9050       element == EL_EXPANDABLE_WALL_ANY)
9051   {
9052     if (oben_frei)
9053     {
9054       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9055       Store[ax][ay-1] = element;
9056       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9057       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9058         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9059                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9060       new_wall = TRUE;
9061     }
9062     if (unten_frei)
9063     {
9064       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9065       Store[ax][ay+1] = element;
9066       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9067       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9068         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9069                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9070       new_wall = TRUE;
9071     }
9072   }
9073
9074   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9075       element == EL_EXPANDABLE_WALL_ANY ||
9076       element == EL_EXPANDABLE_WALL ||
9077       element == EL_BD_EXPANDABLE_WALL)
9078   {
9079     if (links_frei)
9080     {
9081       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9082       Store[ax-1][ay] = element;
9083       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9084       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9085         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9086                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9087       new_wall = TRUE;
9088     }
9089
9090     if (rechts_frei)
9091     {
9092       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9093       Store[ax+1][ay] = element;
9094       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9095       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9096         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9097                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9098       new_wall = TRUE;
9099     }
9100   }
9101
9102   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9103     TEST_DrawLevelField(ax, ay);
9104
9105   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9106     oben_massiv = TRUE;
9107   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9108     unten_massiv = TRUE;
9109   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9110     links_massiv = TRUE;
9111   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9112     rechts_massiv = TRUE;
9113
9114   if (((oben_massiv && unten_massiv) ||
9115        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9116        element == EL_EXPANDABLE_WALL) &&
9117       ((links_massiv && rechts_massiv) ||
9118        element == EL_EXPANDABLE_WALL_VERTICAL))
9119     Feld[ax][ay] = EL_WALL;
9120
9121   if (new_wall)
9122     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9123 }
9124
9125 void MauerAblegerStahl(int ax, int ay)
9126 {
9127   int element = Feld[ax][ay];
9128   int graphic = el2img(element);
9129   boolean oben_frei = FALSE, unten_frei = FALSE;
9130   boolean links_frei = FALSE, rechts_frei = FALSE;
9131   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9132   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9133   boolean new_wall = FALSE;
9134
9135   if (IS_ANIMATED(graphic))
9136     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9137
9138   if (!MovDelay[ax][ay])        /* start building new wall */
9139     MovDelay[ax][ay] = 6;
9140
9141   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9142   {
9143     MovDelay[ax][ay]--;
9144     if (MovDelay[ax][ay])
9145       return;
9146   }
9147
9148   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9149     oben_frei = TRUE;
9150   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9151     unten_frei = TRUE;
9152   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9153     links_frei = TRUE;
9154   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9155     rechts_frei = TRUE;
9156
9157   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9158       element == EL_EXPANDABLE_STEELWALL_ANY)
9159   {
9160     if (oben_frei)
9161     {
9162       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9163       Store[ax][ay-1] = element;
9164       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9165       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9166         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9167                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9168       new_wall = TRUE;
9169     }
9170     if (unten_frei)
9171     {
9172       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9173       Store[ax][ay+1] = element;
9174       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9175       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9176         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9177                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9178       new_wall = TRUE;
9179     }
9180   }
9181
9182   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9183       element == EL_EXPANDABLE_STEELWALL_ANY)
9184   {
9185     if (links_frei)
9186     {
9187       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9188       Store[ax-1][ay] = element;
9189       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9190       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9191         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9192                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9193       new_wall = TRUE;
9194     }
9195
9196     if (rechts_frei)
9197     {
9198       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9199       Store[ax+1][ay] = element;
9200       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9201       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9202         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9203                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9204       new_wall = TRUE;
9205     }
9206   }
9207
9208   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9209     oben_massiv = TRUE;
9210   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9211     unten_massiv = TRUE;
9212   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9213     links_massiv = TRUE;
9214   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9215     rechts_massiv = TRUE;
9216
9217   if (((oben_massiv && unten_massiv) ||
9218        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9219       ((links_massiv && rechts_massiv) ||
9220        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9221     Feld[ax][ay] = EL_STEELWALL;
9222
9223   if (new_wall)
9224     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9225 }
9226
9227 void CheckForDragon(int x, int y)
9228 {
9229   int i, j;
9230   boolean dragon_found = FALSE;
9231   static int xy[4][2] =
9232   {
9233     { 0, -1 },
9234     { -1, 0 },
9235     { +1, 0 },
9236     { 0, +1 }
9237   };
9238
9239   for (i = 0; i < NUM_DIRECTIONS; i++)
9240   {
9241     for (j = 0; j < 4; j++)
9242     {
9243       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9244
9245       if (IN_LEV_FIELD(xx, yy) &&
9246           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9247       {
9248         if (Feld[xx][yy] == EL_DRAGON)
9249           dragon_found = TRUE;
9250       }
9251       else
9252         break;
9253     }
9254   }
9255
9256   if (!dragon_found)
9257   {
9258     for (i = 0; i < NUM_DIRECTIONS; i++)
9259     {
9260       for (j = 0; j < 3; j++)
9261       {
9262         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9263   
9264         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9265         {
9266           Feld[xx][yy] = EL_EMPTY;
9267           TEST_DrawLevelField(xx, yy);
9268         }
9269         else
9270           break;
9271       }
9272     }
9273   }
9274 }
9275
9276 static void InitBuggyBase(int x, int y)
9277 {
9278   int element = Feld[x][y];
9279   int activating_delay = FRAMES_PER_SECOND / 4;
9280
9281   ChangeDelay[x][y] =
9282     (element == EL_SP_BUGGY_BASE ?
9283      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9284      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9285      activating_delay :
9286      element == EL_SP_BUGGY_BASE_ACTIVE ?
9287      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9288 }
9289
9290 static void WarnBuggyBase(int x, int y)
9291 {
9292   int i;
9293   static int xy[4][2] =
9294   {
9295     { 0, -1 },
9296     { -1, 0 },
9297     { +1, 0 },
9298     { 0, +1 }
9299   };
9300
9301   for (i = 0; i < NUM_DIRECTIONS; i++)
9302   {
9303     int xx = x + xy[i][0];
9304     int yy = y + xy[i][1];
9305
9306     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9307     {
9308       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9309
9310       break;
9311     }
9312   }
9313 }
9314
9315 static void InitTrap(int x, int y)
9316 {
9317   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9318 }
9319
9320 static void ActivateTrap(int x, int y)
9321 {
9322   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9323 }
9324
9325 static void ChangeActiveTrap(int x, int y)
9326 {
9327   int graphic = IMG_TRAP_ACTIVE;
9328
9329   /* if new animation frame was drawn, correct crumbled sand border */
9330   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9331     TEST_DrawLevelFieldCrumbled(x, y);
9332 }
9333
9334 static int getSpecialActionElement(int element, int number, int base_element)
9335 {
9336   return (element != EL_EMPTY ? element :
9337           number != -1 ? base_element + number - 1 :
9338           EL_EMPTY);
9339 }
9340
9341 static int getModifiedActionNumber(int value_old, int operator, int operand,
9342                                    int value_min, int value_max)
9343 {
9344   int value_new = (operator == CA_MODE_SET      ? operand :
9345                    operator == CA_MODE_ADD      ? value_old + operand :
9346                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9347                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9348                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9349                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9350                    value_old);
9351
9352   return (value_new < value_min ? value_min :
9353           value_new > value_max ? value_max :
9354           value_new);
9355 }
9356
9357 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9358 {
9359   struct ElementInfo *ei = &element_info[element];
9360   struct ElementChangeInfo *change = &ei->change_page[page];
9361   int target_element = change->target_element;
9362   int action_type = change->action_type;
9363   int action_mode = change->action_mode;
9364   int action_arg = change->action_arg;
9365   int action_element = change->action_element;
9366   int i;
9367
9368   if (!change->has_action)
9369     return;
9370
9371   /* ---------- determine action paramater values -------------------------- */
9372
9373   int level_time_value =
9374     (level.time > 0 ? TimeLeft :
9375      TimePlayed);
9376
9377   int action_arg_element_raw =
9378     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9379      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9380      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9381      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9382      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9383      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9384      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9385      EL_EMPTY);
9386   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9387
9388   int action_arg_direction =
9389     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9390      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9391      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9392      change->actual_trigger_side :
9393      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9394      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9395      MV_NONE);
9396
9397   int action_arg_number_min =
9398     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9399      CA_ARG_MIN);
9400
9401   int action_arg_number_max =
9402     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9403      action_type == CA_SET_LEVEL_GEMS ? 999 :
9404      action_type == CA_SET_LEVEL_TIME ? 9999 :
9405      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9406      action_type == CA_SET_CE_VALUE ? 9999 :
9407      action_type == CA_SET_CE_SCORE ? 9999 :
9408      CA_ARG_MAX);
9409
9410   int action_arg_number_reset =
9411     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9412      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9413      action_type == CA_SET_LEVEL_TIME ? level.time :
9414      action_type == CA_SET_LEVEL_SCORE ? 0 :
9415      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9416      action_type == CA_SET_CE_SCORE ? 0 :
9417      0);
9418
9419   int action_arg_number =
9420     (action_arg <= CA_ARG_MAX ? action_arg :
9421      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9422      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9423      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9424      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9425      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9426      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9427      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9428      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9429      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9430      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9431      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9432      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9433      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9434      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9435      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9436      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9437      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9438      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9439      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9440      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9441      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9442      -1);
9443
9444   int action_arg_number_old =
9445     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9446      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9447      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9448      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9449      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9450      0);
9451
9452   int action_arg_number_new =
9453     getModifiedActionNumber(action_arg_number_old,
9454                             action_mode, action_arg_number,
9455                             action_arg_number_min, action_arg_number_max);
9456
9457   int trigger_player_bits =
9458     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9459      change->actual_trigger_player_bits : change->trigger_player);
9460
9461   int action_arg_player_bits =
9462     (action_arg >= CA_ARG_PLAYER_1 &&
9463      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9464      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9465      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9466      PLAYER_BITS_ANY);
9467
9468   /* ---------- execute action  -------------------------------------------- */
9469
9470   switch (action_type)
9471   {
9472     case CA_NO_ACTION:
9473     {
9474       return;
9475     }
9476
9477     /* ---------- level actions  ------------------------------------------- */
9478
9479     case CA_RESTART_LEVEL:
9480     {
9481       game.restart_level = TRUE;
9482
9483       break;
9484     }
9485
9486     case CA_SHOW_ENVELOPE:
9487     {
9488       int element = getSpecialActionElement(action_arg_element,
9489                                             action_arg_number, EL_ENVELOPE_1);
9490
9491       if (IS_ENVELOPE(element))
9492         local_player->show_envelope = element;
9493
9494       break;
9495     }
9496
9497     case CA_SET_LEVEL_TIME:
9498     {
9499       if (level.time > 0)       /* only modify limited time value */
9500       {
9501         TimeLeft = action_arg_number_new;
9502
9503         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9504
9505         DisplayGameControlValues();
9506
9507         if (!TimeLeft && setup.time_limit)
9508           for (i = 0; i < MAX_PLAYERS; i++)
9509             KillPlayer(&stored_player[i]);
9510       }
9511
9512       break;
9513     }
9514
9515     case CA_SET_LEVEL_SCORE:
9516     {
9517       local_player->score = action_arg_number_new;
9518
9519       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9520
9521       DisplayGameControlValues();
9522
9523       break;
9524     }
9525
9526     case CA_SET_LEVEL_GEMS:
9527     {
9528       local_player->gems_still_needed = action_arg_number_new;
9529
9530       game_panel_controls[GAME_PANEL_GEMS].value =
9531         local_player->gems_still_needed;
9532
9533       DisplayGameControlValues();
9534
9535       break;
9536     }
9537
9538     case CA_SET_LEVEL_WIND:
9539     {
9540       game.wind_direction = action_arg_direction;
9541
9542       break;
9543     }
9544
9545     case CA_SET_LEVEL_RANDOM_SEED:
9546     {
9547       /* ensure that setting a new random seed while playing is predictable */
9548       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9549
9550       break;
9551     }
9552
9553     /* ---------- player actions  ------------------------------------------ */
9554
9555     case CA_MOVE_PLAYER:
9556     {
9557       /* automatically move to the next field in specified direction */
9558       for (i = 0; i < MAX_PLAYERS; i++)
9559         if (trigger_player_bits & (1 << i))
9560           stored_player[i].programmed_action = action_arg_direction;
9561
9562       break;
9563     }
9564
9565     case CA_EXIT_PLAYER:
9566     {
9567       for (i = 0; i < MAX_PLAYERS; i++)
9568         if (action_arg_player_bits & (1 << i))
9569           PlayerWins(&stored_player[i]);
9570
9571       break;
9572     }
9573
9574     case CA_KILL_PLAYER:
9575     {
9576       for (i = 0; i < MAX_PLAYERS; i++)
9577         if (action_arg_player_bits & (1 << i))
9578           KillPlayer(&stored_player[i]);
9579
9580       break;
9581     }
9582
9583     case CA_SET_PLAYER_KEYS:
9584     {
9585       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9586       int element = getSpecialActionElement(action_arg_element,
9587                                             action_arg_number, EL_KEY_1);
9588
9589       if (IS_KEY(element))
9590       {
9591         for (i = 0; i < MAX_PLAYERS; i++)
9592         {
9593           if (trigger_player_bits & (1 << i))
9594           {
9595             stored_player[i].key[KEY_NR(element)] = key_state;
9596
9597             DrawGameDoorValues();
9598           }
9599         }
9600       }
9601
9602       break;
9603     }
9604
9605     case CA_SET_PLAYER_SPEED:
9606     {
9607       for (i = 0; i < MAX_PLAYERS; i++)
9608       {
9609         if (trigger_player_bits & (1 << i))
9610         {
9611           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9612
9613           if (action_arg == CA_ARG_SPEED_FASTER &&
9614               stored_player[i].cannot_move)
9615           {
9616             action_arg_number = STEPSIZE_VERY_SLOW;
9617           }
9618           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9619                    action_arg == CA_ARG_SPEED_FASTER)
9620           {
9621             action_arg_number = 2;
9622             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9623                            CA_MODE_MULTIPLY);
9624           }
9625           else if (action_arg == CA_ARG_NUMBER_RESET)
9626           {
9627             action_arg_number = level.initial_player_stepsize[i];
9628           }
9629
9630           move_stepsize =
9631             getModifiedActionNumber(move_stepsize,
9632                                     action_mode,
9633                                     action_arg_number,
9634                                     action_arg_number_min,
9635                                     action_arg_number_max);
9636
9637           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9638         }
9639       }
9640
9641       break;
9642     }
9643
9644     case CA_SET_PLAYER_SHIELD:
9645     {
9646       for (i = 0; i < MAX_PLAYERS; i++)
9647       {
9648         if (trigger_player_bits & (1 << i))
9649         {
9650           if (action_arg == CA_ARG_SHIELD_OFF)
9651           {
9652             stored_player[i].shield_normal_time_left = 0;
9653             stored_player[i].shield_deadly_time_left = 0;
9654           }
9655           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9656           {
9657             stored_player[i].shield_normal_time_left = 999999;
9658           }
9659           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9660           {
9661             stored_player[i].shield_normal_time_left = 999999;
9662             stored_player[i].shield_deadly_time_left = 999999;
9663           }
9664         }
9665       }
9666
9667       break;
9668     }
9669
9670     case CA_SET_PLAYER_GRAVITY:
9671     {
9672       for (i = 0; i < MAX_PLAYERS; i++)
9673       {
9674         if (trigger_player_bits & (1 << i))
9675         {
9676           stored_player[i].gravity =
9677             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9678              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9679              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9680              stored_player[i].gravity);
9681         }
9682       }
9683
9684       break;
9685     }
9686
9687     case CA_SET_PLAYER_ARTWORK:
9688     {
9689       for (i = 0; i < MAX_PLAYERS; i++)
9690       {
9691         if (trigger_player_bits & (1 << i))
9692         {
9693           int artwork_element = action_arg_element;
9694
9695           if (action_arg == CA_ARG_ELEMENT_RESET)
9696             artwork_element =
9697               (level.use_artwork_element[i] ? level.artwork_element[i] :
9698                stored_player[i].element_nr);
9699
9700           if (stored_player[i].artwork_element != artwork_element)
9701             stored_player[i].Frame = 0;
9702
9703           stored_player[i].artwork_element = artwork_element;
9704
9705           SetPlayerWaiting(&stored_player[i], FALSE);
9706
9707           /* set number of special actions for bored and sleeping animation */
9708           stored_player[i].num_special_action_bored =
9709             get_num_special_action(artwork_element,
9710                                    ACTION_BORING_1, ACTION_BORING_LAST);
9711           stored_player[i].num_special_action_sleeping =
9712             get_num_special_action(artwork_element,
9713                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9714         }
9715       }
9716
9717       break;
9718     }
9719
9720     case CA_SET_PLAYER_INVENTORY:
9721     {
9722       for (i = 0; i < MAX_PLAYERS; i++)
9723       {
9724         struct PlayerInfo *player = &stored_player[i];
9725         int j, k;
9726
9727         if (trigger_player_bits & (1 << i))
9728         {
9729           int inventory_element = action_arg_element;
9730
9731           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9732               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9733               action_arg == CA_ARG_ELEMENT_ACTION)
9734           {
9735             int element = inventory_element;
9736             int collect_count = element_info[element].collect_count_initial;
9737
9738             if (!IS_CUSTOM_ELEMENT(element))
9739               collect_count = 1;
9740
9741             if (collect_count == 0)
9742               player->inventory_infinite_element = element;
9743             else
9744               for (k = 0; k < collect_count; k++)
9745                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9746                   player->inventory_element[player->inventory_size++] =
9747                     element;
9748           }
9749           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9750                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9751                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9752           {
9753             if (player->inventory_infinite_element != EL_UNDEFINED &&
9754                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9755                                      action_arg_element_raw))
9756               player->inventory_infinite_element = EL_UNDEFINED;
9757
9758             for (k = 0, j = 0; j < player->inventory_size; j++)
9759             {
9760               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9761                                         action_arg_element_raw))
9762                 player->inventory_element[k++] = player->inventory_element[j];
9763             }
9764
9765             player->inventory_size = k;
9766           }
9767           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9768           {
9769             if (player->inventory_size > 0)
9770             {
9771               for (j = 0; j < player->inventory_size - 1; j++)
9772                 player->inventory_element[j] = player->inventory_element[j + 1];
9773
9774               player->inventory_size--;
9775             }
9776           }
9777           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9778           {
9779             if (player->inventory_size > 0)
9780               player->inventory_size--;
9781           }
9782           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9783           {
9784             player->inventory_infinite_element = EL_UNDEFINED;
9785             player->inventory_size = 0;
9786           }
9787           else if (action_arg == CA_ARG_INVENTORY_RESET)
9788           {
9789             player->inventory_infinite_element = EL_UNDEFINED;
9790             player->inventory_size = 0;
9791
9792             if (level.use_initial_inventory[i])
9793             {
9794               for (j = 0; j < level.initial_inventory_size[i]; j++)
9795               {
9796                 int element = level.initial_inventory_content[i][j];
9797                 int collect_count = element_info[element].collect_count_initial;
9798
9799                 if (!IS_CUSTOM_ELEMENT(element))
9800                   collect_count = 1;
9801
9802                 if (collect_count == 0)
9803                   player->inventory_infinite_element = element;
9804                 else
9805                   for (k = 0; k < collect_count; k++)
9806                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9807                       player->inventory_element[player->inventory_size++] =
9808                         element;
9809               }
9810             }
9811           }
9812         }
9813       }
9814
9815       break;
9816     }
9817
9818     /* ---------- CE actions  ---------------------------------------------- */
9819
9820     case CA_SET_CE_VALUE:
9821     {
9822       int last_ce_value = CustomValue[x][y];
9823
9824       CustomValue[x][y] = action_arg_number_new;
9825
9826       if (CustomValue[x][y] != last_ce_value)
9827       {
9828         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9829         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9830
9831         if (CustomValue[x][y] == 0)
9832         {
9833           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9834           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9835         }
9836       }
9837
9838       break;
9839     }
9840
9841     case CA_SET_CE_SCORE:
9842     {
9843       int last_ce_score = ei->collect_score;
9844
9845       ei->collect_score = action_arg_number_new;
9846
9847       if (ei->collect_score != last_ce_score)
9848       {
9849         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9850         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9851
9852         if (ei->collect_score == 0)
9853         {
9854           int xx, yy;
9855
9856           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9857           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9858
9859           /*
9860             This is a very special case that seems to be a mixture between
9861             CheckElementChange() and CheckTriggeredElementChange(): while
9862             the first one only affects single elements that are triggered
9863             directly, the second one affects multiple elements in the playfield
9864             that are triggered indirectly by another element. This is a third
9865             case: Changing the CE score always affects multiple identical CEs,
9866             so every affected CE must be checked, not only the single CE for
9867             which the CE score was changed in the first place (as every instance
9868             of that CE shares the same CE score, and therefore also can change)!
9869           */
9870           SCAN_PLAYFIELD(xx, yy)
9871           {
9872             if (Feld[xx][yy] == element)
9873               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9874                                  CE_SCORE_GETS_ZERO);
9875           }
9876         }
9877       }
9878
9879       break;
9880     }
9881
9882     case CA_SET_CE_ARTWORK:
9883     {
9884       int artwork_element = action_arg_element;
9885       boolean reset_frame = FALSE;
9886       int xx, yy;
9887
9888       if (action_arg == CA_ARG_ELEMENT_RESET)
9889         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9890                            element);
9891
9892       if (ei->gfx_element != artwork_element)
9893         reset_frame = TRUE;
9894
9895       ei->gfx_element = artwork_element;
9896
9897       SCAN_PLAYFIELD(xx, yy)
9898       {
9899         if (Feld[xx][yy] == element)
9900         {
9901           if (reset_frame)
9902           {
9903             ResetGfxAnimation(xx, yy);
9904             ResetRandomAnimationValue(xx, yy);
9905           }
9906
9907           TEST_DrawLevelField(xx, yy);
9908         }
9909       }
9910
9911       break;
9912     }
9913
9914     /* ---------- engine actions  ------------------------------------------ */
9915
9916     case CA_SET_ENGINE_SCAN_MODE:
9917     {
9918       InitPlayfieldScanMode(action_arg);
9919
9920       break;
9921     }
9922
9923     default:
9924       break;
9925   }
9926 }
9927
9928 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9929 {
9930   int old_element = Feld[x][y];
9931   int new_element = GetElementFromGroupElement(element);
9932   int previous_move_direction = MovDir[x][y];
9933   int last_ce_value = CustomValue[x][y];
9934   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9935   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9936   boolean add_player_onto_element = (new_element_is_player &&
9937                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9938                                      IS_WALKABLE(old_element));
9939
9940   if (!add_player_onto_element)
9941   {
9942     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9943       RemoveMovingField(x, y);
9944     else
9945       RemoveField(x, y);
9946
9947     Feld[x][y] = new_element;
9948
9949     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9950       MovDir[x][y] = previous_move_direction;
9951
9952     if (element_info[new_element].use_last_ce_value)
9953       CustomValue[x][y] = last_ce_value;
9954
9955     InitField_WithBug1(x, y, FALSE);
9956
9957     new_element = Feld[x][y];   /* element may have changed */
9958
9959     ResetGfxAnimation(x, y);
9960     ResetRandomAnimationValue(x, y);
9961
9962     TEST_DrawLevelField(x, y);
9963
9964     if (GFX_CRUMBLED(new_element))
9965       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9966   }
9967
9968   /* check if element under the player changes from accessible to unaccessible
9969      (needed for special case of dropping element which then changes) */
9970   /* (must be checked after creating new element for walkable group elements) */
9971   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9972       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9973   {
9974     Bang(x, y);
9975
9976     return;
9977   }
9978
9979   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9980   if (new_element_is_player)
9981     RelocatePlayer(x, y, new_element);
9982
9983   if (is_change)
9984     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9985
9986   TestIfBadThingTouchesPlayer(x, y);
9987   TestIfPlayerTouchesCustomElement(x, y);
9988   TestIfElementTouchesCustomElement(x, y);
9989 }
9990
9991 static void CreateField(int x, int y, int element)
9992 {
9993   CreateFieldExt(x, y, element, FALSE);
9994 }
9995
9996 static void CreateElementFromChange(int x, int y, int element)
9997 {
9998   element = GET_VALID_RUNTIME_ELEMENT(element);
9999
10000   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10001   {
10002     int old_element = Feld[x][y];
10003
10004     /* prevent changed element from moving in same engine frame
10005        unless both old and new element can either fall or move */
10006     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10007         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10008       Stop[x][y] = TRUE;
10009   }
10010
10011   CreateFieldExt(x, y, element, TRUE);
10012 }
10013
10014 static boolean ChangeElement(int x, int y, int element, int page)
10015 {
10016   struct ElementInfo *ei = &element_info[element];
10017   struct ElementChangeInfo *change = &ei->change_page[page];
10018   int ce_value = CustomValue[x][y];
10019   int ce_score = ei->collect_score;
10020   int target_element;
10021   int old_element = Feld[x][y];
10022
10023   /* always use default change event to prevent running into a loop */
10024   if (ChangeEvent[x][y] == -1)
10025     ChangeEvent[x][y] = CE_DELAY;
10026
10027   if (ChangeEvent[x][y] == CE_DELAY)
10028   {
10029     /* reset actual trigger element, trigger player and action element */
10030     change->actual_trigger_element = EL_EMPTY;
10031     change->actual_trigger_player = EL_EMPTY;
10032     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10033     change->actual_trigger_side = CH_SIDE_NONE;
10034     change->actual_trigger_ce_value = 0;
10035     change->actual_trigger_ce_score = 0;
10036   }
10037
10038   /* do not change elements more than a specified maximum number of changes */
10039   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10040     return FALSE;
10041
10042   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10043
10044   if (change->explode)
10045   {
10046     Bang(x, y);
10047
10048     return TRUE;
10049   }
10050
10051   if (change->use_target_content)
10052   {
10053     boolean complete_replace = TRUE;
10054     boolean can_replace[3][3];
10055     int xx, yy;
10056
10057     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10058     {
10059       boolean is_empty;
10060       boolean is_walkable;
10061       boolean is_diggable;
10062       boolean is_collectible;
10063       boolean is_removable;
10064       boolean is_destructible;
10065       int ex = x + xx - 1;
10066       int ey = y + yy - 1;
10067       int content_element = change->target_content.e[xx][yy];
10068       int e;
10069
10070       can_replace[xx][yy] = TRUE;
10071
10072       if (ex == x && ey == y)   /* do not check changing element itself */
10073         continue;
10074
10075       if (content_element == EL_EMPTY_SPACE)
10076       {
10077         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10078
10079         continue;
10080       }
10081
10082       if (!IN_LEV_FIELD(ex, ey))
10083       {
10084         can_replace[xx][yy] = FALSE;
10085         complete_replace = FALSE;
10086
10087         continue;
10088       }
10089
10090       e = Feld[ex][ey];
10091
10092       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10093         e = MovingOrBlocked2Element(ex, ey);
10094
10095       is_empty = (IS_FREE(ex, ey) ||
10096                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10097
10098       is_walkable     = (is_empty || IS_WALKABLE(e));
10099       is_diggable     = (is_empty || IS_DIGGABLE(e));
10100       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10101       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10102       is_removable    = (is_diggable || is_collectible);
10103
10104       can_replace[xx][yy] =
10105         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10106           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10107           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10108           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10109           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10110           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10111          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10112
10113       if (!can_replace[xx][yy])
10114         complete_replace = FALSE;
10115     }
10116
10117     if (!change->only_if_complete || complete_replace)
10118     {
10119       boolean something_has_changed = FALSE;
10120
10121       if (change->only_if_complete && change->use_random_replace &&
10122           RND(100) < change->random_percentage)
10123         return FALSE;
10124
10125       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10126       {
10127         int ex = x + xx - 1;
10128         int ey = y + yy - 1;
10129         int content_element;
10130
10131         if (can_replace[xx][yy] && (!change->use_random_replace ||
10132                                     RND(100) < change->random_percentage))
10133         {
10134           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10135             RemoveMovingField(ex, ey);
10136
10137           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10138
10139           content_element = change->target_content.e[xx][yy];
10140           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10141                                               ce_value, ce_score);
10142
10143           CreateElementFromChange(ex, ey, target_element);
10144
10145           something_has_changed = TRUE;
10146
10147           /* for symmetry reasons, freeze newly created border elements */
10148           if (ex != x || ey != y)
10149             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10150         }
10151       }
10152
10153       if (something_has_changed)
10154       {
10155         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10156         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10157       }
10158     }
10159   }
10160   else
10161   {
10162     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10163                                         ce_value, ce_score);
10164
10165     if (element == EL_DIAGONAL_GROWING ||
10166         element == EL_DIAGONAL_SHRINKING)
10167     {
10168       target_element = Store[x][y];
10169
10170       Store[x][y] = EL_EMPTY;
10171     }
10172
10173     CreateElementFromChange(x, y, target_element);
10174
10175     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10176     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10177   }
10178
10179   /* this uses direct change before indirect change */
10180   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10181
10182   return TRUE;
10183 }
10184
10185 static void HandleElementChange(int x, int y, int page)
10186 {
10187   int element = MovingOrBlocked2Element(x, y);
10188   struct ElementInfo *ei = &element_info[element];
10189   struct ElementChangeInfo *change = &ei->change_page[page];
10190   boolean handle_action_before_change = FALSE;
10191
10192 #ifdef DEBUG
10193   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10194       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10195   {
10196     printf("\n\n");
10197     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10198            x, y, element, element_info[element].token_name);
10199     printf("HandleElementChange(): This should never happen!\n");
10200     printf("\n\n");
10201   }
10202 #endif
10203
10204   /* this can happen with classic bombs on walkable, changing elements */
10205   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10206   {
10207     return;
10208   }
10209
10210   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10211   {
10212     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10213
10214     if (change->can_change)
10215     {
10216       /* !!! not clear why graphic animation should be reset at all here !!! */
10217       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10218       /* when a custom element is about to change (for example by change delay),
10219          do not reset graphic animation when the custom element is moving */
10220       if (!IS_MOVING(x, y))
10221       {
10222         ResetGfxAnimation(x, y);
10223         ResetRandomAnimationValue(x, y);
10224       }
10225
10226       if (change->pre_change_function)
10227         change->pre_change_function(x, y);
10228     }
10229   }
10230
10231   ChangeDelay[x][y]--;
10232
10233   if (ChangeDelay[x][y] != 0)           /* continue element change */
10234   {
10235     if (change->can_change)
10236     {
10237       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10238
10239       if (IS_ANIMATED(graphic))
10240         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10241
10242       if (change->change_function)
10243         change->change_function(x, y);
10244     }
10245   }
10246   else                                  /* finish element change */
10247   {
10248     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10249     {
10250       page = ChangePage[x][y];
10251       ChangePage[x][y] = -1;
10252
10253       change = &ei->change_page[page];
10254     }
10255
10256     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10257     {
10258       ChangeDelay[x][y] = 1;            /* try change after next move step */
10259       ChangePage[x][y] = page;          /* remember page to use for change */
10260
10261       return;
10262     }
10263
10264     /* special case: set new level random seed before changing element */
10265     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10266       handle_action_before_change = TRUE;
10267
10268     if (change->has_action && handle_action_before_change)
10269       ExecuteCustomElementAction(x, y, element, page);
10270
10271     if (change->can_change)
10272     {
10273       if (ChangeElement(x, y, element, page))
10274       {
10275         if (change->post_change_function)
10276           change->post_change_function(x, y);
10277       }
10278     }
10279
10280     if (change->has_action && !handle_action_before_change)
10281       ExecuteCustomElementAction(x, y, element, page);
10282   }
10283 }
10284
10285 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10286                                               int trigger_element,
10287                                               int trigger_event,
10288                                               int trigger_player,
10289                                               int trigger_side,
10290                                               int trigger_page)
10291 {
10292   boolean change_done_any = FALSE;
10293   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10294   int i;
10295
10296   if (!(trigger_events[trigger_element][trigger_event]))
10297     return FALSE;
10298
10299   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10300
10301   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10302   {
10303     int element = EL_CUSTOM_START + i;
10304     boolean change_done = FALSE;
10305     int p;
10306
10307     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10308         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10309       continue;
10310
10311     for (p = 0; p < element_info[element].num_change_pages; p++)
10312     {
10313       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10314
10315       if (change->can_change_or_has_action &&
10316           change->has_event[trigger_event] &&
10317           change->trigger_side & trigger_side &&
10318           change->trigger_player & trigger_player &&
10319           change->trigger_page & trigger_page_bits &&
10320           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10321       {
10322         change->actual_trigger_element = trigger_element;
10323         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10324         change->actual_trigger_player_bits = trigger_player;
10325         change->actual_trigger_side = trigger_side;
10326         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10327         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10328
10329         if ((change->can_change && !change_done) || change->has_action)
10330         {
10331           int x, y;
10332
10333           SCAN_PLAYFIELD(x, y)
10334           {
10335             if (Feld[x][y] == element)
10336             {
10337               if (change->can_change && !change_done)
10338               {
10339                 /* if element already changed in this frame, not only prevent
10340                    another element change (checked in ChangeElement()), but
10341                    also prevent additional element actions for this element */
10342
10343                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10344                     !level.use_action_after_change_bug)
10345                   continue;
10346
10347                 ChangeDelay[x][y] = 1;
10348                 ChangeEvent[x][y] = trigger_event;
10349
10350                 HandleElementChange(x, y, p);
10351               }
10352               else if (change->has_action)
10353               {
10354                 /* if element already changed in this frame, not only prevent
10355                    another element change (checked in ChangeElement()), but
10356                    also prevent additional element actions for this element */
10357
10358                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10359                     !level.use_action_after_change_bug)
10360                   continue;
10361
10362                 ExecuteCustomElementAction(x, y, element, p);
10363                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10364               }
10365             }
10366           }
10367
10368           if (change->can_change)
10369           {
10370             change_done = TRUE;
10371             change_done_any = TRUE;
10372           }
10373         }
10374       }
10375     }
10376   }
10377
10378   RECURSION_LOOP_DETECTION_END();
10379
10380   return change_done_any;
10381 }
10382
10383 static boolean CheckElementChangeExt(int x, int y,
10384                                      int element,
10385                                      int trigger_element,
10386                                      int trigger_event,
10387                                      int trigger_player,
10388                                      int trigger_side)
10389 {
10390   boolean change_done = FALSE;
10391   int p;
10392
10393   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10394       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10395     return FALSE;
10396
10397   if (Feld[x][y] == EL_BLOCKED)
10398   {
10399     Blocked2Moving(x, y, &x, &y);
10400     element = Feld[x][y];
10401   }
10402
10403   /* check if element has already changed or is about to change after moving */
10404   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10405        Feld[x][y] != element) ||
10406
10407       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10408        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10409         ChangePage[x][y] != -1)))
10410     return FALSE;
10411
10412   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10413
10414   for (p = 0; p < element_info[element].num_change_pages; p++)
10415   {
10416     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10417
10418     /* check trigger element for all events where the element that is checked
10419        for changing interacts with a directly adjacent element -- this is
10420        different to element changes that affect other elements to change on the
10421        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10422     boolean check_trigger_element =
10423       (trigger_event == CE_TOUCHING_X ||
10424        trigger_event == CE_HITTING_X ||
10425        trigger_event == CE_HIT_BY_X ||
10426        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10427
10428     if (change->can_change_or_has_action &&
10429         change->has_event[trigger_event] &&
10430         change->trigger_side & trigger_side &&
10431         change->trigger_player & trigger_player &&
10432         (!check_trigger_element ||
10433          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10434     {
10435       change->actual_trigger_element = trigger_element;
10436       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10437       change->actual_trigger_player_bits = trigger_player;
10438       change->actual_trigger_side = trigger_side;
10439       change->actual_trigger_ce_value = CustomValue[x][y];
10440       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10441
10442       /* special case: trigger element not at (x,y) position for some events */
10443       if (check_trigger_element)
10444       {
10445         static struct
10446         {
10447           int dx, dy;
10448         } move_xy[] =
10449           {
10450             {  0,  0 },
10451             { -1,  0 },
10452             { +1,  0 },
10453             {  0,  0 },
10454             {  0, -1 },
10455             {  0,  0 }, { 0, 0 }, { 0, 0 },
10456             {  0, +1 }
10457           };
10458
10459         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10460         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10461
10462         change->actual_trigger_ce_value = CustomValue[xx][yy];
10463         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10464       }
10465
10466       if (change->can_change && !change_done)
10467       {
10468         ChangeDelay[x][y] = 1;
10469         ChangeEvent[x][y] = trigger_event;
10470
10471         HandleElementChange(x, y, p);
10472
10473         change_done = TRUE;
10474       }
10475       else if (change->has_action)
10476       {
10477         ExecuteCustomElementAction(x, y, element, p);
10478         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10479       }
10480     }
10481   }
10482
10483   RECURSION_LOOP_DETECTION_END();
10484
10485   return change_done;
10486 }
10487
10488 static void PlayPlayerSound(struct PlayerInfo *player)
10489 {
10490   int jx = player->jx, jy = player->jy;
10491   int sound_element = player->artwork_element;
10492   int last_action = player->last_action_waiting;
10493   int action = player->action_waiting;
10494
10495   if (player->is_waiting)
10496   {
10497     if (action != last_action)
10498       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10499     else
10500       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10501   }
10502   else
10503   {
10504     if (action != last_action)
10505       StopSound(element_info[sound_element].sound[last_action]);
10506
10507     if (last_action == ACTION_SLEEPING)
10508       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10509   }
10510 }
10511
10512 static void PlayAllPlayersSound()
10513 {
10514   int i;
10515
10516   for (i = 0; i < MAX_PLAYERS; i++)
10517     if (stored_player[i].active)
10518       PlayPlayerSound(&stored_player[i]);
10519 }
10520
10521 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10522 {
10523   boolean last_waiting = player->is_waiting;
10524   int move_dir = player->MovDir;
10525
10526   player->dir_waiting = move_dir;
10527   player->last_action_waiting = player->action_waiting;
10528
10529   if (is_waiting)
10530   {
10531     if (!last_waiting)          /* not waiting -> waiting */
10532     {
10533       player->is_waiting = TRUE;
10534
10535       player->frame_counter_bored =
10536         FrameCounter +
10537         game.player_boring_delay_fixed +
10538         GetSimpleRandom(game.player_boring_delay_random);
10539       player->frame_counter_sleeping =
10540         FrameCounter +
10541         game.player_sleeping_delay_fixed +
10542         GetSimpleRandom(game.player_sleeping_delay_random);
10543
10544       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10545     }
10546
10547     if (game.player_sleeping_delay_fixed +
10548         game.player_sleeping_delay_random > 0 &&
10549         player->anim_delay_counter == 0 &&
10550         player->post_delay_counter == 0 &&
10551         FrameCounter >= player->frame_counter_sleeping)
10552       player->is_sleeping = TRUE;
10553     else if (game.player_boring_delay_fixed +
10554              game.player_boring_delay_random > 0 &&
10555              FrameCounter >= player->frame_counter_bored)
10556       player->is_bored = TRUE;
10557
10558     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10559                               player->is_bored ? ACTION_BORING :
10560                               ACTION_WAITING);
10561
10562     if (player->is_sleeping && player->use_murphy)
10563     {
10564       /* special case for sleeping Murphy when leaning against non-free tile */
10565
10566       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10567           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10568            !IS_MOVING(player->jx - 1, player->jy)))
10569         move_dir = MV_LEFT;
10570       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10571                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10572                 !IS_MOVING(player->jx + 1, player->jy)))
10573         move_dir = MV_RIGHT;
10574       else
10575         player->is_sleeping = FALSE;
10576
10577       player->dir_waiting = move_dir;
10578     }
10579
10580     if (player->is_sleeping)
10581     {
10582       if (player->num_special_action_sleeping > 0)
10583       {
10584         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10585         {
10586           int last_special_action = player->special_action_sleeping;
10587           int num_special_action = player->num_special_action_sleeping;
10588           int special_action =
10589             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10590              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10591              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10592              last_special_action + 1 : ACTION_SLEEPING);
10593           int special_graphic =
10594             el_act_dir2img(player->artwork_element, special_action, move_dir);
10595
10596           player->anim_delay_counter =
10597             graphic_info[special_graphic].anim_delay_fixed +
10598             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10599           player->post_delay_counter =
10600             graphic_info[special_graphic].post_delay_fixed +
10601             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10602
10603           player->special_action_sleeping = special_action;
10604         }
10605
10606         if (player->anim_delay_counter > 0)
10607         {
10608           player->action_waiting = player->special_action_sleeping;
10609           player->anim_delay_counter--;
10610         }
10611         else if (player->post_delay_counter > 0)
10612         {
10613           player->post_delay_counter--;
10614         }
10615       }
10616     }
10617     else if (player->is_bored)
10618     {
10619       if (player->num_special_action_bored > 0)
10620       {
10621         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10622         {
10623           int special_action =
10624             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10625           int special_graphic =
10626             el_act_dir2img(player->artwork_element, special_action, move_dir);
10627
10628           player->anim_delay_counter =
10629             graphic_info[special_graphic].anim_delay_fixed +
10630             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10631           player->post_delay_counter =
10632             graphic_info[special_graphic].post_delay_fixed +
10633             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10634
10635           player->special_action_bored = special_action;
10636         }
10637
10638         if (player->anim_delay_counter > 0)
10639         {
10640           player->action_waiting = player->special_action_bored;
10641           player->anim_delay_counter--;
10642         }
10643         else if (player->post_delay_counter > 0)
10644         {
10645           player->post_delay_counter--;
10646         }
10647       }
10648     }
10649   }
10650   else if (last_waiting)        /* waiting -> not waiting */
10651   {
10652     player->is_waiting = FALSE;
10653     player->is_bored = FALSE;
10654     player->is_sleeping = FALSE;
10655
10656     player->frame_counter_bored = -1;
10657     player->frame_counter_sleeping = -1;
10658
10659     player->anim_delay_counter = 0;
10660     player->post_delay_counter = 0;
10661
10662     player->dir_waiting = player->MovDir;
10663     player->action_waiting = ACTION_DEFAULT;
10664
10665     player->special_action_bored = ACTION_DEFAULT;
10666     player->special_action_sleeping = ACTION_DEFAULT;
10667   }
10668 }
10669
10670 static void CheckSingleStepMode(struct PlayerInfo *player)
10671 {
10672   if (tape.single_step && tape.recording && !tape.pausing)
10673   {
10674     /* as it is called "single step mode", just return to pause mode when the
10675        player stopped moving after one tile (or never starts moving at all) */
10676     if (!player->is_moving && !player->is_pushing)
10677     {
10678       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10679       SnapField(player, 0, 0);                  /* stop snapping */
10680     }
10681   }
10682 }
10683
10684 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10685 {
10686   int left      = player_action & JOY_LEFT;
10687   int right     = player_action & JOY_RIGHT;
10688   int up        = player_action & JOY_UP;
10689   int down      = player_action & JOY_DOWN;
10690   int button1   = player_action & JOY_BUTTON_1;
10691   int button2   = player_action & JOY_BUTTON_2;
10692   int dx        = (left ? -1 : right ? 1 : 0);
10693   int dy        = (up   ? -1 : down  ? 1 : 0);
10694
10695   if (!player->active || tape.pausing)
10696     return 0;
10697
10698   if (player_action)
10699   {
10700     if (button1)
10701       SnapField(player, dx, dy);
10702     else
10703     {
10704       if (button2)
10705         DropElement(player);
10706
10707       MovePlayer(player, dx, dy);
10708     }
10709
10710     CheckSingleStepMode(player);
10711
10712     SetPlayerWaiting(player, FALSE);
10713
10714     return player_action;
10715   }
10716   else
10717   {
10718     /* no actions for this player (no input at player's configured device) */
10719
10720     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10721     SnapField(player, 0, 0);
10722     CheckGravityMovementWhenNotMoving(player);
10723
10724     if (player->MovPos == 0)
10725       SetPlayerWaiting(player, TRUE);
10726
10727     if (player->MovPos == 0)    /* needed for tape.playing */
10728       player->is_moving = FALSE;
10729
10730     player->is_dropping = FALSE;
10731     player->is_dropping_pressed = FALSE;
10732     player->drop_pressed_delay = 0;
10733
10734     CheckSingleStepMode(player);
10735
10736     return 0;
10737   }
10738 }
10739
10740 static void CheckLevelTime()
10741 {
10742   int i;
10743
10744   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10745   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10746   {
10747     if (level.native_em_level->lev->home == 0)  /* all players at home */
10748     {
10749       PlayerWins(local_player);
10750
10751       AllPlayersGone = TRUE;
10752
10753       level.native_em_level->lev->home = -1;
10754     }
10755
10756     if (level.native_em_level->ply[0]->alive == 0 &&
10757         level.native_em_level->ply[1]->alive == 0 &&
10758         level.native_em_level->ply[2]->alive == 0 &&
10759         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10760       AllPlayersGone = TRUE;
10761   }
10762   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10763   {
10764     if (game_sp.LevelSolved &&
10765         !game_sp.GameOver)                              /* game won */
10766     {
10767       PlayerWins(local_player);
10768
10769       game_sp.GameOver = TRUE;
10770
10771       AllPlayersGone = TRUE;
10772     }
10773
10774     if (game_sp.GameOver)                               /* game lost */
10775       AllPlayersGone = TRUE;
10776   }
10777
10778   if (TimeFrames >= FRAMES_PER_SECOND)
10779   {
10780     TimeFrames = 0;
10781     TapeTime++;
10782
10783     for (i = 0; i < MAX_PLAYERS; i++)
10784     {
10785       struct PlayerInfo *player = &stored_player[i];
10786
10787       if (SHIELD_ON(player))
10788       {
10789         player->shield_normal_time_left--;
10790
10791         if (player->shield_deadly_time_left > 0)
10792           player->shield_deadly_time_left--;
10793       }
10794     }
10795
10796     if (!local_player->LevelSolved && !level.use_step_counter)
10797     {
10798       TimePlayed++;
10799
10800       if (TimeLeft > 0)
10801       {
10802         TimeLeft--;
10803
10804         if (TimeLeft <= 10 && setup.time_limit)
10805           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10806
10807         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10808            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10809
10810         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10811
10812         if (!TimeLeft && setup.time_limit)
10813         {
10814           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10815             level.native_em_level->lev->killed_out_of_time = TRUE;
10816           else
10817             for (i = 0; i < MAX_PLAYERS; i++)
10818               KillPlayer(&stored_player[i]);
10819         }
10820       }
10821       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10822       {
10823         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10824       }
10825
10826       level.native_em_level->lev->time =
10827         (game.no_time_limit ? TimePlayed : TimeLeft);
10828     }
10829
10830     if (tape.recording || tape.playing)
10831       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10832   }
10833
10834   UpdateAndDisplayGameControlValues();
10835 }
10836
10837 void AdvanceFrameAndPlayerCounters(int player_nr)
10838 {
10839   int i;
10840
10841   /* advance frame counters (global frame counter and time frame counter) */
10842   FrameCounter++;
10843   TimeFrames++;
10844
10845   /* advance player counters (counters for move delay, move animation etc.) */
10846   for (i = 0; i < MAX_PLAYERS; i++)
10847   {
10848     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10849     int move_delay_value = stored_player[i].move_delay_value;
10850     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10851
10852     if (!advance_player_counters)       /* not all players may be affected */
10853       continue;
10854
10855     if (move_frames == 0)       /* less than one move per game frame */
10856     {
10857       int stepsize = TILEX / move_delay_value;
10858       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10859       int count = (stored_player[i].is_moving ?
10860                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10861
10862       if (count % delay == 0)
10863         move_frames = 1;
10864     }
10865
10866     stored_player[i].Frame += move_frames;
10867
10868     if (stored_player[i].MovPos != 0)
10869       stored_player[i].StepFrame += move_frames;
10870
10871     if (stored_player[i].move_delay > 0)
10872       stored_player[i].move_delay--;
10873
10874     /* due to bugs in previous versions, counter must count up, not down */
10875     if (stored_player[i].push_delay != -1)
10876       stored_player[i].push_delay++;
10877
10878     if (stored_player[i].drop_delay > 0)
10879       stored_player[i].drop_delay--;
10880
10881     if (stored_player[i].is_dropping_pressed)
10882       stored_player[i].drop_pressed_delay++;
10883   }
10884 }
10885
10886 void StartGameActions(boolean init_network_game, boolean record_tape,
10887                       int random_seed)
10888 {
10889   unsigned int new_random_seed = InitRND(random_seed);
10890
10891   if (record_tape)
10892     TapeStartRecording(new_random_seed);
10893
10894 #if defined(NETWORK_AVALIABLE)
10895   if (init_network_game)
10896   {
10897     SendToServer_StartPlaying();
10898
10899     return;
10900   }
10901 #endif
10902
10903   InitGame();
10904 }
10905
10906 void GameActions()
10907 {
10908   static unsigned int game_frame_delay = 0;
10909   unsigned int game_frame_delay_value;
10910   byte *recorded_player_action;
10911   byte summarized_player_action = 0;
10912   byte tape_action[MAX_PLAYERS];
10913   int i;
10914
10915   /* detect endless loops, caused by custom element programming */
10916   if (recursion_loop_detected && recursion_loop_depth == 0)
10917   {
10918     char *message = getStringCat3("Internal Error! Element ",
10919                                   EL_NAME(recursion_loop_element),
10920                                   " caused endless loop! Quit the game?");
10921
10922     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10923           EL_NAME(recursion_loop_element));
10924
10925     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10926
10927     recursion_loop_detected = FALSE;    /* if game should be continued */
10928
10929     free(message);
10930
10931     return;
10932   }
10933
10934   if (game.restart_level)
10935     StartGameActions(options.network, setup.autorecord, level.random_seed);
10936
10937   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10938   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10939   {
10940     if (level.native_em_level->lev->home == 0)  /* all players at home */
10941     {
10942       PlayerWins(local_player);
10943
10944       AllPlayersGone = TRUE;
10945
10946       level.native_em_level->lev->home = -1;
10947     }
10948
10949     if (level.native_em_level->ply[0]->alive == 0 &&
10950         level.native_em_level->ply[1]->alive == 0 &&
10951         level.native_em_level->ply[2]->alive == 0 &&
10952         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10953       AllPlayersGone = TRUE;
10954   }
10955   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10956   {
10957     if (game_sp.LevelSolved &&
10958         !game_sp.GameOver)                              /* game won */
10959     {
10960       PlayerWins(local_player);
10961
10962       game_sp.GameOver = TRUE;
10963
10964       AllPlayersGone = TRUE;
10965     }
10966
10967     if (game_sp.GameOver)                               /* game lost */
10968       AllPlayersGone = TRUE;
10969   }
10970
10971   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10972     GameWon();
10973
10974   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10975     TapeStop();
10976
10977   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10978     return;
10979
10980   game_frame_delay_value =
10981     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10982
10983   if (tape.playing && tape.warp_forward && !tape.pausing)
10984     game_frame_delay_value = 0;
10985
10986   /* ---------- main game synchronization point ---------- */
10987
10988   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10989
10990   if (network_playing && !network_player_action_received)
10991   {
10992     /* try to get network player actions in time */
10993
10994 #if defined(NETWORK_AVALIABLE)
10995     /* last chance to get network player actions without main loop delay */
10996     HandleNetworking();
10997 #endif
10998
10999     /* game was quit by network peer */
11000     if (game_status != GAME_MODE_PLAYING)
11001       return;
11002
11003     if (!network_player_action_received)
11004       return;           /* failed to get network player actions in time */
11005
11006     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11007   }
11008
11009   if (tape.pausing)
11010     return;
11011
11012   /* at this point we know that we really continue executing the game */
11013
11014   network_player_action_received = FALSE;
11015
11016   /* when playing tape, read previously recorded player input from tape data */
11017   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11018
11019   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11020   if (tape.pausing)
11021     return;
11022
11023   if (tape.set_centered_player)
11024   {
11025     game.centered_player_nr_next = tape.centered_player_nr_next;
11026     game.set_centered_player = TRUE;
11027   }
11028
11029   for (i = 0; i < MAX_PLAYERS; i++)
11030   {
11031     summarized_player_action |= stored_player[i].action;
11032
11033     if (!network_playing && (game.team_mode || tape.playing))
11034       stored_player[i].effective_action = stored_player[i].action;
11035   }
11036
11037 #if defined(NETWORK_AVALIABLE)
11038   if (network_playing)
11039     SendToServer_MovePlayer(summarized_player_action);
11040 #endif
11041
11042   if (!options.network && !game.team_mode)
11043     local_player->effective_action = summarized_player_action;
11044
11045   if (tape.recording &&
11046       setup.team_mode &&
11047       setup.input_on_focus &&
11048       game.centered_player_nr != -1)
11049   {
11050     for (i = 0; i < MAX_PLAYERS; i++)
11051       stored_player[i].effective_action =
11052         (i == game.centered_player_nr ? summarized_player_action : 0);
11053   }
11054
11055   if (recorded_player_action != NULL)
11056     for (i = 0; i < MAX_PLAYERS; i++)
11057       stored_player[i].effective_action = recorded_player_action[i];
11058
11059   for (i = 0; i < MAX_PLAYERS; i++)
11060   {
11061     tape_action[i] = stored_player[i].effective_action;
11062
11063     /* (this may happen in the RND game engine if a player was not present on
11064        the playfield on level start, but appeared later from a custom element */
11065     if (setup.team_mode &&
11066         tape.recording &&
11067         tape_action[i] &&
11068         !tape.player_participates[i])
11069       tape.player_participates[i] = TRUE;
11070   }
11071
11072   /* only record actions from input devices, but not programmed actions */
11073   if (tape.recording)
11074     TapeRecordAction(tape_action);
11075
11076 #if USE_NEW_PLAYER_ASSIGNMENTS
11077   // !!! also map player actions in single player mode !!!
11078   // if (game.team_mode)
11079   {
11080     byte mapped_action[MAX_PLAYERS];
11081
11082 #if DEBUG_PLAYER_ACTIONS
11083     printf(":::");
11084     for (i = 0; i < MAX_PLAYERS; i++)
11085       printf(" %d, ", stored_player[i].effective_action);
11086 #endif
11087
11088     for (i = 0; i < MAX_PLAYERS; i++)
11089       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11090
11091     for (i = 0; i < MAX_PLAYERS; i++)
11092       stored_player[i].effective_action = mapped_action[i];
11093
11094 #if DEBUG_PLAYER_ACTIONS
11095     printf(" =>");
11096     for (i = 0; i < MAX_PLAYERS; i++)
11097       printf(" %d, ", stored_player[i].effective_action);
11098     printf("\n");
11099 #endif
11100   }
11101 #if DEBUG_PLAYER_ACTIONS
11102   else
11103   {
11104     printf(":::");
11105     for (i = 0; i < MAX_PLAYERS; i++)
11106       printf(" %d, ", stored_player[i].effective_action);
11107     printf("\n");
11108   }
11109 #endif
11110 #endif
11111
11112   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11113   {
11114     GameActions_EM_Main();
11115   }
11116   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11117   {
11118     GameActions_SP_Main();
11119   }
11120   else
11121   {
11122     GameActions_RND();
11123   }
11124 }
11125
11126 void GameActions_EM_Main()
11127 {
11128   byte effective_action[MAX_PLAYERS];
11129   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11130   int i;
11131
11132   for (i = 0; i < MAX_PLAYERS; i++)
11133     effective_action[i] = stored_player[i].effective_action;
11134
11135   GameActions_EM(effective_action, warp_mode);
11136
11137   CheckLevelTime();
11138
11139   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11140 }
11141
11142 void GameActions_SP_Main()
11143 {
11144   byte effective_action[MAX_PLAYERS];
11145   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11146   int i;
11147
11148   for (i = 0; i < MAX_PLAYERS; i++)
11149     effective_action[i] = stored_player[i].effective_action;
11150
11151   GameActions_SP(effective_action, warp_mode);
11152
11153   CheckLevelTime();
11154
11155   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11156 }
11157
11158 void GameActions_RND()
11159 {
11160   int magic_wall_x = 0, magic_wall_y = 0;
11161   int i, x, y, element, graphic;
11162
11163   InitPlayfieldScanModeVars();
11164
11165   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11166   {
11167     SCAN_PLAYFIELD(x, y)
11168     {
11169       ChangeCount[x][y] = 0;
11170       ChangeEvent[x][y] = -1;
11171     }
11172   }
11173
11174   if (game.set_centered_player)
11175   {
11176     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11177
11178     /* switching to "all players" only possible if all players fit to screen */
11179     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11180     {
11181       game.centered_player_nr_next = game.centered_player_nr;
11182       game.set_centered_player = FALSE;
11183     }
11184
11185     /* do not switch focus to non-existing (or non-active) player */
11186     if (game.centered_player_nr_next >= 0 &&
11187         !stored_player[game.centered_player_nr_next].active)
11188     {
11189       game.centered_player_nr_next = game.centered_player_nr;
11190       game.set_centered_player = FALSE;
11191     }
11192   }
11193
11194   if (game.set_centered_player &&
11195       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11196   {
11197     int sx, sy;
11198
11199     if (game.centered_player_nr_next == -1)
11200     {
11201       setScreenCenteredToAllPlayers(&sx, &sy);
11202     }
11203     else
11204     {
11205       sx = stored_player[game.centered_player_nr_next].jx;
11206       sy = stored_player[game.centered_player_nr_next].jy;
11207     }
11208
11209     game.centered_player_nr = game.centered_player_nr_next;
11210     game.set_centered_player = FALSE;
11211
11212     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11213     DrawGameDoorValues();
11214   }
11215
11216   for (i = 0; i < MAX_PLAYERS; i++)
11217   {
11218     int actual_player_action = stored_player[i].effective_action;
11219
11220 #if 1
11221     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11222        - rnd_equinox_tetrachloride 048
11223        - rnd_equinox_tetrachloride_ii 096
11224        - rnd_emanuel_schmieg 002
11225        - doctor_sloan_ww 001, 020
11226     */
11227     if (stored_player[i].MovPos == 0)
11228       CheckGravityMovement(&stored_player[i]);
11229 #endif
11230
11231     /* overwrite programmed action with tape action */
11232     if (stored_player[i].programmed_action)
11233       actual_player_action = stored_player[i].programmed_action;
11234
11235     PlayerActions(&stored_player[i], actual_player_action);
11236
11237     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11238   }
11239
11240   ScrollScreen(NULL, SCROLL_GO_ON);
11241
11242   /* for backwards compatibility, the following code emulates a fixed bug that
11243      occured when pushing elements (causing elements that just made their last
11244      pushing step to already (if possible) make their first falling step in the
11245      same game frame, which is bad); this code is also needed to use the famous
11246      "spring push bug" which is used in older levels and might be wanted to be
11247      used also in newer levels, but in this case the buggy pushing code is only
11248      affecting the "spring" element and no other elements */
11249
11250   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11251   {
11252     for (i = 0; i < MAX_PLAYERS; i++)
11253     {
11254       struct PlayerInfo *player = &stored_player[i];
11255       int x = player->jx;
11256       int y = player->jy;
11257
11258       if (player->active && player->is_pushing && player->is_moving &&
11259           IS_MOVING(x, y) &&
11260           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11261            Feld[x][y] == EL_SPRING))
11262       {
11263         ContinueMoving(x, y);
11264
11265         /* continue moving after pushing (this is actually a bug) */
11266         if (!IS_MOVING(x, y))
11267           Stop[x][y] = FALSE;
11268       }
11269     }
11270   }
11271
11272   SCAN_PLAYFIELD(x, y)
11273   {
11274     ChangeCount[x][y] = 0;
11275     ChangeEvent[x][y] = -1;
11276
11277     /* this must be handled before main playfield loop */
11278     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11279     {
11280       MovDelay[x][y]--;
11281       if (MovDelay[x][y] <= 0)
11282         RemoveField(x, y);
11283     }
11284
11285     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11286     {
11287       MovDelay[x][y]--;
11288       if (MovDelay[x][y] <= 0)
11289       {
11290         RemoveField(x, y);
11291         TEST_DrawLevelField(x, y);
11292
11293         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11294       }
11295     }
11296
11297 #if DEBUG
11298     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11299     {
11300       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11301       printf("GameActions(): This should never happen!\n");
11302
11303       ChangePage[x][y] = -1;
11304     }
11305 #endif
11306
11307     Stop[x][y] = FALSE;
11308     if (WasJustMoving[x][y] > 0)
11309       WasJustMoving[x][y]--;
11310     if (WasJustFalling[x][y] > 0)
11311       WasJustFalling[x][y]--;
11312     if (CheckCollision[x][y] > 0)
11313       CheckCollision[x][y]--;
11314     if (CheckImpact[x][y] > 0)
11315       CheckImpact[x][y]--;
11316
11317     GfxFrame[x][y]++;
11318
11319     /* reset finished pushing action (not done in ContinueMoving() to allow
11320        continuous pushing animation for elements with zero push delay) */
11321     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11322     {
11323       ResetGfxAnimation(x, y);
11324       TEST_DrawLevelField(x, y);
11325     }
11326
11327 #if DEBUG
11328     if (IS_BLOCKED(x, y))
11329     {
11330       int oldx, oldy;
11331
11332       Blocked2Moving(x, y, &oldx, &oldy);
11333       if (!IS_MOVING(oldx, oldy))
11334       {
11335         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11336         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11337         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11338         printf("GameActions(): This should never happen!\n");
11339       }
11340     }
11341 #endif
11342   }
11343
11344   SCAN_PLAYFIELD(x, y)
11345   {
11346     element = Feld[x][y];
11347     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11348
11349     ResetGfxFrame(x, y, TRUE);
11350
11351     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11352         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11353       ResetRandomAnimationValue(x, y);
11354
11355     SetRandomAnimationValue(x, y);
11356
11357     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11358
11359     if (IS_INACTIVE(element))
11360     {
11361       if (IS_ANIMATED(graphic))
11362         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11363
11364       continue;
11365     }
11366
11367     /* this may take place after moving, so 'element' may have changed */
11368     if (IS_CHANGING(x, y) &&
11369         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11370     {
11371       int page = element_info[element].event_page_nr[CE_DELAY];
11372
11373       HandleElementChange(x, y, page);
11374
11375       element = Feld[x][y];
11376       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11377     }
11378
11379     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11380     {
11381       StartMoving(x, y);
11382
11383       element = Feld[x][y];
11384       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11385
11386       if (IS_ANIMATED(graphic) &&
11387           !IS_MOVING(x, y) &&
11388           !Stop[x][y])
11389         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11390
11391       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11392         TEST_DrawTwinkleOnField(x, y);
11393     }
11394     else if ((element == EL_ACID ||
11395               element == EL_EXIT_OPEN ||
11396               element == EL_EM_EXIT_OPEN ||
11397               element == EL_SP_EXIT_OPEN ||
11398               element == EL_STEEL_EXIT_OPEN ||
11399               element == EL_EM_STEEL_EXIT_OPEN ||
11400               element == EL_SP_TERMINAL ||
11401               element == EL_SP_TERMINAL_ACTIVE ||
11402               element == EL_EXTRA_TIME ||
11403               element == EL_SHIELD_NORMAL ||
11404               element == EL_SHIELD_DEADLY) &&
11405              IS_ANIMATED(graphic))
11406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11407     else if (IS_MOVING(x, y))
11408       ContinueMoving(x, y);
11409     else if (IS_ACTIVE_BOMB(element))
11410       CheckDynamite(x, y);
11411     else if (element == EL_AMOEBA_GROWING)
11412       AmoebeWaechst(x, y);
11413     else if (element == EL_AMOEBA_SHRINKING)
11414       AmoebaDisappearing(x, y);
11415
11416 #if !USE_NEW_AMOEBA_CODE
11417     else if (IS_AMOEBALIVE(element))
11418       AmoebeAbleger(x, y);
11419 #endif
11420
11421     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11422       Life(x, y);
11423     else if (element == EL_EXIT_CLOSED)
11424       CheckExit(x, y);
11425     else if (element == EL_EM_EXIT_CLOSED)
11426       CheckExitEM(x, y);
11427     else if (element == EL_STEEL_EXIT_CLOSED)
11428       CheckExitSteel(x, y);
11429     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11430       CheckExitSteelEM(x, y);
11431     else if (element == EL_SP_EXIT_CLOSED)
11432       CheckExitSP(x, y);
11433     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11434              element == EL_EXPANDABLE_STEELWALL_GROWING)
11435       MauerWaechst(x, y);
11436     else if (element == EL_EXPANDABLE_WALL ||
11437              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11438              element == EL_EXPANDABLE_WALL_VERTICAL ||
11439              element == EL_EXPANDABLE_WALL_ANY ||
11440              element == EL_BD_EXPANDABLE_WALL)
11441       MauerAbleger(x, y);
11442     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11443              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11444              element == EL_EXPANDABLE_STEELWALL_ANY)
11445       MauerAblegerStahl(x, y);
11446     else if (element == EL_FLAMES)
11447       CheckForDragon(x, y);
11448     else if (element == EL_EXPLOSION)
11449       ; /* drawing of correct explosion animation is handled separately */
11450     else if (element == EL_ELEMENT_SNAPPING ||
11451              element == EL_DIAGONAL_SHRINKING ||
11452              element == EL_DIAGONAL_GROWING)
11453     {
11454       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11455
11456       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11457     }
11458     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11459       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11460
11461     if (IS_BELT_ACTIVE(element))
11462       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11463
11464     if (game.magic_wall_active)
11465     {
11466       int jx = local_player->jx, jy = local_player->jy;
11467
11468       /* play the element sound at the position nearest to the player */
11469       if ((element == EL_MAGIC_WALL_FULL ||
11470            element == EL_MAGIC_WALL_ACTIVE ||
11471            element == EL_MAGIC_WALL_EMPTYING ||
11472            element == EL_BD_MAGIC_WALL_FULL ||
11473            element == EL_BD_MAGIC_WALL_ACTIVE ||
11474            element == EL_BD_MAGIC_WALL_EMPTYING ||
11475            element == EL_DC_MAGIC_WALL_FULL ||
11476            element == EL_DC_MAGIC_WALL_ACTIVE ||
11477            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11478           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11479       {
11480         magic_wall_x = x;
11481         magic_wall_y = y;
11482       }
11483     }
11484   }
11485
11486 #if USE_NEW_AMOEBA_CODE
11487   /* new experimental amoeba growth stuff */
11488   if (!(FrameCounter % 8))
11489   {
11490     static unsigned int random = 1684108901;
11491
11492     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11493     {
11494       x = RND(lev_fieldx);
11495       y = RND(lev_fieldy);
11496       element = Feld[x][y];
11497
11498       if (!IS_PLAYER(x,y) &&
11499           (element == EL_EMPTY ||
11500            CAN_GROW_INTO(element) ||
11501            element == EL_QUICKSAND_EMPTY ||
11502            element == EL_QUICKSAND_FAST_EMPTY ||
11503            element == EL_ACID_SPLASH_LEFT ||
11504            element == EL_ACID_SPLASH_RIGHT))
11505       {
11506         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11507             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11508             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11509             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11510           Feld[x][y] = EL_AMOEBA_DROP;
11511       }
11512
11513       random = random * 129 + 1;
11514     }
11515   }
11516 #endif
11517
11518   game.explosions_delayed = FALSE;
11519
11520   SCAN_PLAYFIELD(x, y)
11521   {
11522     element = Feld[x][y];
11523
11524     if (ExplodeField[x][y])
11525       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11526     else if (element == EL_EXPLOSION)
11527       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11528
11529     ExplodeField[x][y] = EX_TYPE_NONE;
11530   }
11531
11532   game.explosions_delayed = TRUE;
11533
11534   if (game.magic_wall_active)
11535   {
11536     if (!(game.magic_wall_time_left % 4))
11537     {
11538       int element = Feld[magic_wall_x][magic_wall_y];
11539
11540       if (element == EL_BD_MAGIC_WALL_FULL ||
11541           element == EL_BD_MAGIC_WALL_ACTIVE ||
11542           element == EL_BD_MAGIC_WALL_EMPTYING)
11543         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11544       else if (element == EL_DC_MAGIC_WALL_FULL ||
11545                element == EL_DC_MAGIC_WALL_ACTIVE ||
11546                element == EL_DC_MAGIC_WALL_EMPTYING)
11547         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11548       else
11549         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11550     }
11551
11552     if (game.magic_wall_time_left > 0)
11553     {
11554       game.magic_wall_time_left--;
11555
11556       if (!game.magic_wall_time_left)
11557       {
11558         SCAN_PLAYFIELD(x, y)
11559         {
11560           element = Feld[x][y];
11561
11562           if (element == EL_MAGIC_WALL_ACTIVE ||
11563               element == EL_MAGIC_WALL_FULL)
11564           {
11565             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11566             TEST_DrawLevelField(x, y);
11567           }
11568           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11569                    element == EL_BD_MAGIC_WALL_FULL)
11570           {
11571             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11572             TEST_DrawLevelField(x, y);
11573           }
11574           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11575                    element == EL_DC_MAGIC_WALL_FULL)
11576           {
11577             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11578             TEST_DrawLevelField(x, y);
11579           }
11580         }
11581
11582         game.magic_wall_active = FALSE;
11583       }
11584     }
11585   }
11586
11587   if (game.light_time_left > 0)
11588   {
11589     game.light_time_left--;
11590
11591     if (game.light_time_left == 0)
11592       RedrawAllLightSwitchesAndInvisibleElements();
11593   }
11594
11595   if (game.timegate_time_left > 0)
11596   {
11597     game.timegate_time_left--;
11598
11599     if (game.timegate_time_left == 0)
11600       CloseAllOpenTimegates();
11601   }
11602
11603   if (game.lenses_time_left > 0)
11604   {
11605     game.lenses_time_left--;
11606
11607     if (game.lenses_time_left == 0)
11608       RedrawAllInvisibleElementsForLenses();
11609   }
11610
11611   if (game.magnify_time_left > 0)
11612   {
11613     game.magnify_time_left--;
11614
11615     if (game.magnify_time_left == 0)
11616       RedrawAllInvisibleElementsForMagnifier();
11617   }
11618
11619   for (i = 0; i < MAX_PLAYERS; i++)
11620   {
11621     struct PlayerInfo *player = &stored_player[i];
11622
11623     if (SHIELD_ON(player))
11624     {
11625       if (player->shield_deadly_time_left)
11626         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11627       else if (player->shield_normal_time_left)
11628         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11629     }
11630   }
11631
11632 #if USE_DELAYED_GFX_REDRAW
11633   SCAN_PLAYFIELD(x, y)
11634   {
11635     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11636     {
11637       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11638          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11639
11640       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11641         DrawLevelField(x, y);
11642
11643       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11644         DrawLevelFieldCrumbled(x, y);
11645
11646       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11647         DrawLevelFieldCrumbledNeighbours(x, y);
11648
11649       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11650         DrawTwinkleOnField(x, y);
11651     }
11652
11653     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11654   }
11655 #endif
11656
11657   CheckLevelTime();
11658
11659   DrawAllPlayers();
11660   PlayAllPlayersSound();
11661
11662   if (options.debug)                    /* calculate frames per second */
11663   {
11664     static unsigned int fps_counter = 0;
11665     static int fps_frames = 0;
11666     unsigned int fps_delay_ms = Counter() - fps_counter;
11667
11668     fps_frames++;
11669
11670     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11671     {
11672       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11673
11674       fps_frames = 0;
11675       fps_counter = Counter();
11676     }
11677
11678     redraw_mask |= REDRAW_FPS;
11679   }
11680
11681   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11682
11683   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11684   {
11685     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11686
11687     local_player->show_envelope = 0;
11688   }
11689
11690   /* use random number generator in every frame to make it less predictable */
11691   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11692     RND(1);
11693 }
11694
11695 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11696 {
11697   int min_x = x, min_y = y, max_x = x, max_y = y;
11698   int i;
11699
11700   for (i = 0; i < MAX_PLAYERS; i++)
11701   {
11702     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11703
11704     if (!stored_player[i].active || &stored_player[i] == player)
11705       continue;
11706
11707     min_x = MIN(min_x, jx);
11708     min_y = MIN(min_y, jy);
11709     max_x = MAX(max_x, jx);
11710     max_y = MAX(max_y, jy);
11711   }
11712
11713   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11714 }
11715
11716 static boolean AllPlayersInVisibleScreen()
11717 {
11718   int i;
11719
11720   for (i = 0; i < MAX_PLAYERS; i++)
11721   {
11722     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11723
11724     if (!stored_player[i].active)
11725       continue;
11726
11727     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11728       return FALSE;
11729   }
11730
11731   return TRUE;
11732 }
11733
11734 void ScrollLevel(int dx, int dy)
11735 {
11736   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11737   int x, y;
11738
11739   BlitBitmap(drawto_field, drawto_field,
11740              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11741              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11742              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11743              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11744              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11745              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11746
11747   if (dx != 0)
11748   {
11749     x = (dx == 1 ? BX1 : BX2);
11750     for (y = BY1; y <= BY2; y++)
11751       DrawScreenField(x, y);
11752   }
11753
11754   if (dy != 0)
11755   {
11756     y = (dy == 1 ? BY1 : BY2);
11757     for (x = BX1; x <= BX2; x++)
11758       DrawScreenField(x, y);
11759   }
11760
11761   redraw_mask |= REDRAW_FIELD;
11762 }
11763
11764 static boolean canFallDown(struct PlayerInfo *player)
11765 {
11766   int jx = player->jx, jy = player->jy;
11767
11768   return (IN_LEV_FIELD(jx, jy + 1) &&
11769           (IS_FREE(jx, jy + 1) ||
11770            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11771           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11772           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11773 }
11774
11775 static boolean canPassField(int x, int y, int move_dir)
11776 {
11777   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11778   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11779   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11780   int nextx = x + dx;
11781   int nexty = y + dy;
11782   int element = Feld[x][y];
11783
11784   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11785           !CAN_MOVE(element) &&
11786           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11787           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11788           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11789 }
11790
11791 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11792 {
11793   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11794   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11795   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11796   int newx = x + dx;
11797   int newy = y + dy;
11798
11799   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11800           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11801           (IS_DIGGABLE(Feld[newx][newy]) ||
11802            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11803            canPassField(newx, newy, move_dir)));
11804 }
11805
11806 static void CheckGravityMovement(struct PlayerInfo *player)
11807 {
11808   if (player->gravity && !player->programmed_action)
11809   {
11810     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11811     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11812     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11813     int jx = player->jx, jy = player->jy;
11814     boolean player_is_moving_to_valid_field =
11815       (!player_is_snapping &&
11816        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11817         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11818     boolean player_can_fall_down = canFallDown(player);
11819
11820     if (player_can_fall_down &&
11821         !player_is_moving_to_valid_field)
11822       player->programmed_action = MV_DOWN;
11823   }
11824 }
11825
11826 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11827 {
11828   return CheckGravityMovement(player);
11829
11830   if (player->gravity && !player->programmed_action)
11831   {
11832     int jx = player->jx, jy = player->jy;
11833     boolean field_under_player_is_free =
11834       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11835     boolean player_is_standing_on_valid_field =
11836       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11837        (IS_WALKABLE(Feld[jx][jy]) &&
11838         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11839
11840     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11841       player->programmed_action = MV_DOWN;
11842   }
11843 }
11844
11845 /*
11846   MovePlayerOneStep()
11847   -----------------------------------------------------------------------------
11848   dx, dy:               direction (non-diagonal) to try to move the player to
11849   real_dx, real_dy:     direction as read from input device (can be diagonal)
11850 */
11851
11852 boolean MovePlayerOneStep(struct PlayerInfo *player,
11853                           int dx, int dy, int real_dx, int real_dy)
11854 {
11855   int jx = player->jx, jy = player->jy;
11856   int new_jx = jx + dx, new_jy = jy + dy;
11857   int can_move;
11858   boolean player_can_move = !player->cannot_move;
11859
11860   if (!player->active || (!dx && !dy))
11861     return MP_NO_ACTION;
11862
11863   player->MovDir = (dx < 0 ? MV_LEFT :
11864                     dx > 0 ? MV_RIGHT :
11865                     dy < 0 ? MV_UP :
11866                     dy > 0 ? MV_DOWN :  MV_NONE);
11867
11868   if (!IN_LEV_FIELD(new_jx, new_jy))
11869     return MP_NO_ACTION;
11870
11871   if (!player_can_move)
11872   {
11873     if (player->MovPos == 0)
11874     {
11875       player->is_moving = FALSE;
11876       player->is_digging = FALSE;
11877       player->is_collecting = FALSE;
11878       player->is_snapping = FALSE;
11879       player->is_pushing = FALSE;
11880     }
11881   }
11882
11883   if (!options.network && game.centered_player_nr == -1 &&
11884       !AllPlayersInSight(player, new_jx, new_jy))
11885     return MP_NO_ACTION;
11886
11887   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11888   if (can_move != MP_MOVING)
11889     return can_move;
11890
11891   /* check if DigField() has caused relocation of the player */
11892   if (player->jx != jx || player->jy != jy)
11893     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11894
11895   StorePlayer[jx][jy] = 0;
11896   player->last_jx = jx;
11897   player->last_jy = jy;
11898   player->jx = new_jx;
11899   player->jy = new_jy;
11900   StorePlayer[new_jx][new_jy] = player->element_nr;
11901
11902   if (player->move_delay_value_next != -1)
11903   {
11904     player->move_delay_value = player->move_delay_value_next;
11905     player->move_delay_value_next = -1;
11906   }
11907
11908   player->MovPos =
11909     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11910
11911   player->step_counter++;
11912
11913   PlayerVisit[jx][jy] = FrameCounter;
11914
11915   player->is_moving = TRUE;
11916
11917 #if 1
11918   /* should better be called in MovePlayer(), but this breaks some tapes */
11919   ScrollPlayer(player, SCROLL_INIT);
11920 #endif
11921
11922   return MP_MOVING;
11923 }
11924
11925 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11926 {
11927   int jx = player->jx, jy = player->jy;
11928   int old_jx = jx, old_jy = jy;
11929   int moved = MP_NO_ACTION;
11930
11931   if (!player->active)
11932     return FALSE;
11933
11934   if (!dx && !dy)
11935   {
11936     if (player->MovPos == 0)
11937     {
11938       player->is_moving = FALSE;
11939       player->is_digging = FALSE;
11940       player->is_collecting = FALSE;
11941       player->is_snapping = FALSE;
11942       player->is_pushing = FALSE;
11943     }
11944
11945     return FALSE;
11946   }
11947
11948   if (player->move_delay > 0)
11949     return FALSE;
11950
11951   player->move_delay = -1;              /* set to "uninitialized" value */
11952
11953   /* store if player is automatically moved to next field */
11954   player->is_auto_moving = (player->programmed_action != MV_NONE);
11955
11956   /* remove the last programmed player action */
11957   player->programmed_action = 0;
11958
11959   if (player->MovPos)
11960   {
11961     /* should only happen if pre-1.2 tape recordings are played */
11962     /* this is only for backward compatibility */
11963
11964     int original_move_delay_value = player->move_delay_value;
11965
11966 #if DEBUG
11967     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
11968            tape.counter);
11969 #endif
11970
11971     /* scroll remaining steps with finest movement resolution */
11972     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11973
11974     while (player->MovPos)
11975     {
11976       ScrollPlayer(player, SCROLL_GO_ON);
11977       ScrollScreen(NULL, SCROLL_GO_ON);
11978
11979       AdvanceFrameAndPlayerCounters(player->index_nr);
11980
11981       DrawAllPlayers();
11982       BackToFront();
11983     }
11984
11985     player->move_delay_value = original_move_delay_value;
11986   }
11987
11988   player->is_active = FALSE;
11989
11990   if (player->last_move_dir & MV_HORIZONTAL)
11991   {
11992     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11993       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11994   }
11995   else
11996   {
11997     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11998       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11999   }
12000
12001   if (!moved && !player->is_active)
12002   {
12003     player->is_moving = FALSE;
12004     player->is_digging = FALSE;
12005     player->is_collecting = FALSE;
12006     player->is_snapping = FALSE;
12007     player->is_pushing = FALSE;
12008   }
12009
12010   jx = player->jx;
12011   jy = player->jy;
12012
12013   if (moved & MP_MOVING && !ScreenMovPos &&
12014       (player->index_nr == game.centered_player_nr ||
12015        game.centered_player_nr == -1))
12016   {
12017     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12018     int offset = game.scroll_delay_value;
12019
12020     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12021     {
12022       /* actual player has left the screen -- scroll in that direction */
12023       if (jx != old_jx)         /* player has moved horizontally */
12024         scroll_x += (jx - old_jx);
12025       else                      /* player has moved vertically */
12026         scroll_y += (jy - old_jy);
12027     }
12028     else
12029     {
12030       if (jx != old_jx)         /* player has moved horizontally */
12031       {
12032         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12033             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12034           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12035
12036         /* don't scroll over playfield boundaries */
12037         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12038           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12039
12040         /* don't scroll more than one field at a time */
12041         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12042
12043         /* don't scroll against the player's moving direction */
12044         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12045             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12046           scroll_x = old_scroll_x;
12047       }
12048       else                      /* player has moved vertically */
12049       {
12050         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12051             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12052           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12053
12054         /* don't scroll over playfield boundaries */
12055         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12056           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12057
12058         /* don't scroll more than one field at a time */
12059         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12060
12061         /* don't scroll against the player's moving direction */
12062         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12063             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12064           scroll_y = old_scroll_y;
12065       }
12066     }
12067
12068     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12069     {
12070       if (!options.network && game.centered_player_nr == -1 &&
12071           !AllPlayersInVisibleScreen())
12072       {
12073         scroll_x = old_scroll_x;
12074         scroll_y = old_scroll_y;
12075       }
12076       else
12077       {
12078         ScrollScreen(player, SCROLL_INIT);
12079         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12080       }
12081     }
12082   }
12083
12084   player->StepFrame = 0;
12085
12086   if (moved & MP_MOVING)
12087   {
12088     if (old_jx != jx && old_jy == jy)
12089       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12090     else if (old_jx == jx && old_jy != jy)
12091       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12092
12093     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12094
12095     player->last_move_dir = player->MovDir;
12096     player->is_moving = TRUE;
12097     player->is_snapping = FALSE;
12098     player->is_switching = FALSE;
12099     player->is_dropping = FALSE;
12100     player->is_dropping_pressed = FALSE;
12101     player->drop_pressed_delay = 0;
12102
12103 #if 0
12104     /* should better be called here than above, but this breaks some tapes */
12105     ScrollPlayer(player, SCROLL_INIT);
12106 #endif
12107   }
12108   else
12109   {
12110     CheckGravityMovementWhenNotMoving(player);
12111
12112     player->is_moving = FALSE;
12113
12114     /* at this point, the player is allowed to move, but cannot move right now
12115        (e.g. because of something blocking the way) -- ensure that the player
12116        is also allowed to move in the next frame (in old versions before 3.1.1,
12117        the player was forced to wait again for eight frames before next try) */
12118
12119     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12120       player->move_delay = 0;   /* allow direct movement in the next frame */
12121   }
12122
12123   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12124     player->move_delay = player->move_delay_value;
12125
12126   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12127   {
12128     TestIfPlayerTouchesBadThing(jx, jy);
12129     TestIfPlayerTouchesCustomElement(jx, jy);
12130   }
12131
12132   if (!player->active)
12133     RemovePlayer(player);
12134
12135   return moved;
12136 }
12137
12138 void ScrollPlayer(struct PlayerInfo *player, int mode)
12139 {
12140   int jx = player->jx, jy = player->jy;
12141   int last_jx = player->last_jx, last_jy = player->last_jy;
12142   int move_stepsize = TILEX / player->move_delay_value;
12143
12144   if (!player->active)
12145     return;
12146
12147   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12148     return;
12149
12150   if (mode == SCROLL_INIT)
12151   {
12152     player->actual_frame_counter = FrameCounter;
12153     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12154
12155     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12156         Feld[last_jx][last_jy] == EL_EMPTY)
12157     {
12158       int last_field_block_delay = 0;   /* start with no blocking at all */
12159       int block_delay_adjustment = player->block_delay_adjustment;
12160
12161       /* if player blocks last field, add delay for exactly one move */
12162       if (player->block_last_field)
12163       {
12164         last_field_block_delay += player->move_delay_value;
12165
12166         /* when blocking enabled, prevent moving up despite gravity */
12167         if (player->gravity && player->MovDir == MV_UP)
12168           block_delay_adjustment = -1;
12169       }
12170
12171       /* add block delay adjustment (also possible when not blocking) */
12172       last_field_block_delay += block_delay_adjustment;
12173
12174       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12175       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12176     }
12177
12178     if (player->MovPos != 0)    /* player has not yet reached destination */
12179       return;
12180   }
12181   else if (!FrameReached(&player->actual_frame_counter, 1))
12182     return;
12183
12184   if (player->MovPos != 0)
12185   {
12186     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12187     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12188
12189     /* before DrawPlayer() to draw correct player graphic for this case */
12190     if (player->MovPos == 0)
12191       CheckGravityMovement(player);
12192   }
12193
12194   if (player->MovPos == 0)      /* player reached destination field */
12195   {
12196     if (player->move_delay_reset_counter > 0)
12197     {
12198       player->move_delay_reset_counter--;
12199
12200       if (player->move_delay_reset_counter == 0)
12201       {
12202         /* continue with normal speed after quickly moving through gate */
12203         HALVE_PLAYER_SPEED(player);
12204
12205         /* be able to make the next move without delay */
12206         player->move_delay = 0;
12207       }
12208     }
12209
12210     player->last_jx = jx;
12211     player->last_jy = jy;
12212
12213     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12214         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12215         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12216         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12217         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12218         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12219         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12220         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12221     {
12222       DrawPlayer(player);       /* needed here only to cleanup last field */
12223       RemovePlayer(player);
12224
12225       if (local_player->friends_still_needed == 0 ||
12226           IS_SP_ELEMENT(Feld[jx][jy]))
12227         PlayerWins(player);
12228     }
12229
12230     /* this breaks one level: "machine", level 000 */
12231     {
12232       int move_direction = player->MovDir;
12233       int enter_side = MV_DIR_OPPOSITE(move_direction);
12234       int leave_side = move_direction;
12235       int old_jx = last_jx;
12236       int old_jy = last_jy;
12237       int old_element = Feld[old_jx][old_jy];
12238       int new_element = Feld[jx][jy];
12239
12240       if (IS_CUSTOM_ELEMENT(old_element))
12241         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12242                                    CE_LEFT_BY_PLAYER,
12243                                    player->index_bit, leave_side);
12244
12245       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12246                                           CE_PLAYER_LEAVES_X,
12247                                           player->index_bit, leave_side);
12248
12249       if (IS_CUSTOM_ELEMENT(new_element))
12250         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12251                                    player->index_bit, enter_side);
12252
12253       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12254                                           CE_PLAYER_ENTERS_X,
12255                                           player->index_bit, enter_side);
12256
12257       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12258                                         CE_MOVE_OF_X, move_direction);
12259     }
12260
12261     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12262     {
12263       TestIfPlayerTouchesBadThing(jx, jy);
12264       TestIfPlayerTouchesCustomElement(jx, jy);
12265
12266       /* needed because pushed element has not yet reached its destination,
12267          so it would trigger a change event at its previous field location */
12268       if (!player->is_pushing)
12269         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12270
12271       if (!player->active)
12272         RemovePlayer(player);
12273     }
12274
12275     if (!local_player->LevelSolved && level.use_step_counter)
12276     {
12277       int i;
12278
12279       TimePlayed++;
12280
12281       if (TimeLeft > 0)
12282       {
12283         TimeLeft--;
12284
12285         if (TimeLeft <= 10 && setup.time_limit)
12286           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12287
12288         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12289
12290         DisplayGameControlValues();
12291
12292         if (!TimeLeft && setup.time_limit)
12293           for (i = 0; i < MAX_PLAYERS; i++)
12294             KillPlayer(&stored_player[i]);
12295       }
12296       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12297       {
12298         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12299
12300         DisplayGameControlValues();
12301       }
12302     }
12303
12304     if (tape.single_step && tape.recording && !tape.pausing &&
12305         !player->programmed_action)
12306       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12307   }
12308 }
12309
12310 void ScrollScreen(struct PlayerInfo *player, int mode)
12311 {
12312   static unsigned int screen_frame_counter = 0;
12313
12314   if (mode == SCROLL_INIT)
12315   {
12316     /* set scrolling step size according to actual player's moving speed */
12317     ScrollStepSize = TILEX / player->move_delay_value;
12318
12319     screen_frame_counter = FrameCounter;
12320     ScreenMovDir = player->MovDir;
12321     ScreenMovPos = player->MovPos;
12322     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12323     return;
12324   }
12325   else if (!FrameReached(&screen_frame_counter, 1))
12326     return;
12327
12328   if (ScreenMovPos)
12329   {
12330     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12331     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12332     redraw_mask |= REDRAW_FIELD;
12333   }
12334   else
12335     ScreenMovDir = MV_NONE;
12336 }
12337
12338 void TestIfPlayerTouchesCustomElement(int x, int y)
12339 {
12340   static int xy[4][2] =
12341   {
12342     { 0, -1 },
12343     { -1, 0 },
12344     { +1, 0 },
12345     { 0, +1 }
12346   };
12347   static int trigger_sides[4][2] =
12348   {
12349     /* center side       border side */
12350     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12351     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12352     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12353     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12354   };
12355   static int touch_dir[4] =
12356   {
12357     MV_LEFT | MV_RIGHT,
12358     MV_UP   | MV_DOWN,
12359     MV_UP   | MV_DOWN,
12360     MV_LEFT | MV_RIGHT
12361   };
12362   int center_element = Feld[x][y];      /* should always be non-moving! */
12363   int i;
12364
12365   for (i = 0; i < NUM_DIRECTIONS; i++)
12366   {
12367     int xx = x + xy[i][0];
12368     int yy = y + xy[i][1];
12369     int center_side = trigger_sides[i][0];
12370     int border_side = trigger_sides[i][1];
12371     int border_element;
12372
12373     if (!IN_LEV_FIELD(xx, yy))
12374       continue;
12375
12376     if (IS_PLAYER(x, y))                /* player found at center element */
12377     {
12378       struct PlayerInfo *player = PLAYERINFO(x, y);
12379
12380       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12381         border_element = Feld[xx][yy];          /* may be moving! */
12382       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12383         border_element = Feld[xx][yy];
12384       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12385         border_element = MovingOrBlocked2Element(xx, yy);
12386       else
12387         continue;               /* center and border element do not touch */
12388
12389       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12390                                  player->index_bit, border_side);
12391       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12392                                           CE_PLAYER_TOUCHES_X,
12393                                           player->index_bit, border_side);
12394
12395       {
12396         /* use player element that is initially defined in the level playfield,
12397            not the player element that corresponds to the runtime player number
12398            (example: a level that contains EL_PLAYER_3 as the only player would
12399            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12400         int player_element = PLAYERINFO(x, y)->initial_element;
12401
12402         CheckElementChangeBySide(xx, yy, border_element, player_element,
12403                                  CE_TOUCHING_X, border_side);
12404       }
12405     }
12406     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12407     {
12408       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12409
12410       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12411       {
12412         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12413           continue;             /* center and border element do not touch */
12414       }
12415
12416       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12417                                  player->index_bit, center_side);
12418       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12419                                           CE_PLAYER_TOUCHES_X,
12420                                           player->index_bit, center_side);
12421
12422       {
12423         /* use player element that is initially defined in the level playfield,
12424            not the player element that corresponds to the runtime player number
12425            (example: a level that contains EL_PLAYER_3 as the only player would
12426            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12427         int player_element = PLAYERINFO(xx, yy)->initial_element;
12428
12429         CheckElementChangeBySide(x, y, center_element, player_element,
12430                                  CE_TOUCHING_X, center_side);
12431       }
12432
12433       break;
12434     }
12435   }
12436 }
12437
12438 void TestIfElementTouchesCustomElement(int x, int y)
12439 {
12440   static int xy[4][2] =
12441   {
12442     { 0, -1 },
12443     { -1, 0 },
12444     { +1, 0 },
12445     { 0, +1 }
12446   };
12447   static int trigger_sides[4][2] =
12448   {
12449     /* center side      border side */
12450     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12451     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12452     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12453     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12454   };
12455   static int touch_dir[4] =
12456   {
12457     MV_LEFT | MV_RIGHT,
12458     MV_UP   | MV_DOWN,
12459     MV_UP   | MV_DOWN,
12460     MV_LEFT | MV_RIGHT
12461   };
12462   boolean change_center_element = FALSE;
12463   int center_element = Feld[x][y];      /* should always be non-moving! */
12464   int border_element_old[NUM_DIRECTIONS];
12465   int i;
12466
12467   for (i = 0; i < NUM_DIRECTIONS; i++)
12468   {
12469     int xx = x + xy[i][0];
12470     int yy = y + xy[i][1];
12471     int border_element;
12472
12473     border_element_old[i] = -1;
12474
12475     if (!IN_LEV_FIELD(xx, yy))
12476       continue;
12477
12478     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12479       border_element = Feld[xx][yy];    /* may be moving! */
12480     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12481       border_element = Feld[xx][yy];
12482     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12483       border_element = MovingOrBlocked2Element(xx, yy);
12484     else
12485       continue;                 /* center and border element do not touch */
12486
12487     border_element_old[i] = border_element;
12488   }
12489
12490   for (i = 0; i < NUM_DIRECTIONS; i++)
12491   {
12492     int xx = x + xy[i][0];
12493     int yy = y + xy[i][1];
12494     int center_side = trigger_sides[i][0];
12495     int border_element = border_element_old[i];
12496
12497     if (border_element == -1)
12498       continue;
12499
12500     /* check for change of border element */
12501     CheckElementChangeBySide(xx, yy, border_element, center_element,
12502                              CE_TOUCHING_X, center_side);
12503
12504     /* (center element cannot be player, so we dont have to check this here) */
12505   }
12506
12507   for (i = 0; i < NUM_DIRECTIONS; i++)
12508   {
12509     int xx = x + xy[i][0];
12510     int yy = y + xy[i][1];
12511     int border_side = trigger_sides[i][1];
12512     int border_element = border_element_old[i];
12513
12514     if (border_element == -1)
12515       continue;
12516
12517     /* check for change of center element (but change it only once) */
12518     if (!change_center_element)
12519       change_center_element =
12520         CheckElementChangeBySide(x, y, center_element, border_element,
12521                                  CE_TOUCHING_X, border_side);
12522
12523     if (IS_PLAYER(xx, yy))
12524     {
12525       /* use player element that is initially defined in the level playfield,
12526          not the player element that corresponds to the runtime player number
12527          (example: a level that contains EL_PLAYER_3 as the only player would
12528          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12529       int player_element = PLAYERINFO(xx, yy)->initial_element;
12530
12531       CheckElementChangeBySide(x, y, center_element, player_element,
12532                                CE_TOUCHING_X, border_side);
12533     }
12534   }
12535 }
12536
12537 void TestIfElementHitsCustomElement(int x, int y, int direction)
12538 {
12539   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12540   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12541   int hitx = x + dx, hity = y + dy;
12542   int hitting_element = Feld[x][y];
12543   int touched_element;
12544
12545   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12546     return;
12547
12548   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12549                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12550
12551   if (IN_LEV_FIELD(hitx, hity))
12552   {
12553     int opposite_direction = MV_DIR_OPPOSITE(direction);
12554     int hitting_side = direction;
12555     int touched_side = opposite_direction;
12556     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12557                           MovDir[hitx][hity] != direction ||
12558                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12559
12560     object_hit = TRUE;
12561
12562     if (object_hit)
12563     {
12564       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12565                                CE_HITTING_X, touched_side);
12566
12567       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12568                                CE_HIT_BY_X, hitting_side);
12569
12570       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12571                                CE_HIT_BY_SOMETHING, opposite_direction);
12572
12573       if (IS_PLAYER(hitx, hity))
12574       {
12575         /* use player element that is initially defined in the level playfield,
12576            not the player element that corresponds to the runtime player number
12577            (example: a level that contains EL_PLAYER_3 as the only player would
12578            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12579         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12580
12581         CheckElementChangeBySide(x, y, hitting_element, player_element,
12582                                  CE_HITTING_X, touched_side);
12583       }
12584     }
12585   }
12586
12587   /* "hitting something" is also true when hitting the playfield border */
12588   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12589                            CE_HITTING_SOMETHING, direction);
12590 }
12591
12592 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12593 {
12594   int i, kill_x = -1, kill_y = -1;
12595
12596   int bad_element = -1;
12597   static int test_xy[4][2] =
12598   {
12599     { 0, -1 },
12600     { -1, 0 },
12601     { +1, 0 },
12602     { 0, +1 }
12603   };
12604   static int test_dir[4] =
12605   {
12606     MV_UP,
12607     MV_LEFT,
12608     MV_RIGHT,
12609     MV_DOWN
12610   };
12611
12612   for (i = 0; i < NUM_DIRECTIONS; i++)
12613   {
12614     int test_x, test_y, test_move_dir, test_element;
12615
12616     test_x = good_x + test_xy[i][0];
12617     test_y = good_y + test_xy[i][1];
12618
12619     if (!IN_LEV_FIELD(test_x, test_y))
12620       continue;
12621
12622     test_move_dir =
12623       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12624
12625     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12626
12627     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12628        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12629     */
12630     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12631         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12632     {
12633       kill_x = test_x;
12634       kill_y = test_y;
12635       bad_element = test_element;
12636
12637       break;
12638     }
12639   }
12640
12641   if (kill_x != -1 || kill_y != -1)
12642   {
12643     if (IS_PLAYER(good_x, good_y))
12644     {
12645       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12646
12647       if (player->shield_deadly_time_left > 0 &&
12648           !IS_INDESTRUCTIBLE(bad_element))
12649         Bang(kill_x, kill_y);
12650       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12651         KillPlayer(player);
12652     }
12653     else
12654       Bang(good_x, good_y);
12655   }
12656 }
12657
12658 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12659 {
12660   int i, kill_x = -1, kill_y = -1;
12661   int bad_element = Feld[bad_x][bad_y];
12662   static int test_xy[4][2] =
12663   {
12664     { 0, -1 },
12665     { -1, 0 },
12666     { +1, 0 },
12667     { 0, +1 }
12668   };
12669   static int touch_dir[4] =
12670   {
12671     MV_LEFT | MV_RIGHT,
12672     MV_UP   | MV_DOWN,
12673     MV_UP   | MV_DOWN,
12674     MV_LEFT | MV_RIGHT
12675   };
12676   static int test_dir[4] =
12677   {
12678     MV_UP,
12679     MV_LEFT,
12680     MV_RIGHT,
12681     MV_DOWN
12682   };
12683
12684   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12685     return;
12686
12687   for (i = 0; i < NUM_DIRECTIONS; i++)
12688   {
12689     int test_x, test_y, test_move_dir, test_element;
12690
12691     test_x = bad_x + test_xy[i][0];
12692     test_y = bad_y + test_xy[i][1];
12693
12694     if (!IN_LEV_FIELD(test_x, test_y))
12695       continue;
12696
12697     test_move_dir =
12698       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12699
12700     test_element = Feld[test_x][test_y];
12701
12702     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12703        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12704     */
12705     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12706         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12707     {
12708       /* good thing is player or penguin that does not move away */
12709       if (IS_PLAYER(test_x, test_y))
12710       {
12711         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12712
12713         if (bad_element == EL_ROBOT && player->is_moving)
12714           continue;     /* robot does not kill player if he is moving */
12715
12716         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12717         {
12718           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12719             continue;           /* center and border element do not touch */
12720         }
12721
12722         kill_x = test_x;
12723         kill_y = test_y;
12724
12725         break;
12726       }
12727       else if (test_element == EL_PENGUIN)
12728       {
12729         kill_x = test_x;
12730         kill_y = test_y;
12731
12732         break;
12733       }
12734     }
12735   }
12736
12737   if (kill_x != -1 || kill_y != -1)
12738   {
12739     if (IS_PLAYER(kill_x, kill_y))
12740     {
12741       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12742
12743       if (player->shield_deadly_time_left > 0 &&
12744           !IS_INDESTRUCTIBLE(bad_element))
12745         Bang(bad_x, bad_y);
12746       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12747         KillPlayer(player);
12748     }
12749     else
12750       Bang(kill_x, kill_y);
12751   }
12752 }
12753
12754 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12755 {
12756   int bad_element = Feld[bad_x][bad_y];
12757   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12758   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12759   int test_x = bad_x + dx, test_y = bad_y + dy;
12760   int test_move_dir, test_element;
12761   int kill_x = -1, kill_y = -1;
12762
12763   if (!IN_LEV_FIELD(test_x, test_y))
12764     return;
12765
12766   test_move_dir =
12767     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12768
12769   test_element = Feld[test_x][test_y];
12770
12771   if (test_move_dir != bad_move_dir)
12772   {
12773     /* good thing can be player or penguin that does not move away */
12774     if (IS_PLAYER(test_x, test_y))
12775     {
12776       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12777
12778       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12779          player as being hit when he is moving towards the bad thing, because
12780          the "get hit by" condition would be lost after the player stops) */
12781       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12782         return;         /* player moves away from bad thing */
12783
12784       kill_x = test_x;
12785       kill_y = test_y;
12786     }
12787     else if (test_element == EL_PENGUIN)
12788     {
12789       kill_x = test_x;
12790       kill_y = test_y;
12791     }
12792   }
12793
12794   if (kill_x != -1 || kill_y != -1)
12795   {
12796     if (IS_PLAYER(kill_x, kill_y))
12797     {
12798       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12799
12800       if (player->shield_deadly_time_left > 0 &&
12801           !IS_INDESTRUCTIBLE(bad_element))
12802         Bang(bad_x, bad_y);
12803       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12804         KillPlayer(player);
12805     }
12806     else
12807       Bang(kill_x, kill_y);
12808   }
12809 }
12810
12811 void TestIfPlayerTouchesBadThing(int x, int y)
12812 {
12813   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12814 }
12815
12816 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12817 {
12818   TestIfGoodThingHitsBadThing(x, y, move_dir);
12819 }
12820
12821 void TestIfBadThingTouchesPlayer(int x, int y)
12822 {
12823   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12824 }
12825
12826 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12827 {
12828   TestIfBadThingHitsGoodThing(x, y, move_dir);
12829 }
12830
12831 void TestIfFriendTouchesBadThing(int x, int y)
12832 {
12833   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12834 }
12835
12836 void TestIfBadThingTouchesFriend(int x, int y)
12837 {
12838   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12839 }
12840
12841 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12842 {
12843   int i, kill_x = bad_x, kill_y = bad_y;
12844   static int xy[4][2] =
12845   {
12846     { 0, -1 },
12847     { -1, 0 },
12848     { +1, 0 },
12849     { 0, +1 }
12850   };
12851
12852   for (i = 0; i < NUM_DIRECTIONS; i++)
12853   {
12854     int x, y, element;
12855
12856     x = bad_x + xy[i][0];
12857     y = bad_y + xy[i][1];
12858     if (!IN_LEV_FIELD(x, y))
12859       continue;
12860
12861     element = Feld[x][y];
12862     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12863         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12864     {
12865       kill_x = x;
12866       kill_y = y;
12867       break;
12868     }
12869   }
12870
12871   if (kill_x != bad_x || kill_y != bad_y)
12872     Bang(bad_x, bad_y);
12873 }
12874
12875 void KillPlayer(struct PlayerInfo *player)
12876 {
12877   int jx = player->jx, jy = player->jy;
12878
12879   if (!player->active)
12880     return;
12881
12882 #if 0
12883   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12884          player->killed, player->active, player->reanimated);
12885 #endif
12886
12887   /* the following code was introduced to prevent an infinite loop when calling
12888      -> Bang()
12889      -> CheckTriggeredElementChangeExt()
12890      -> ExecuteCustomElementAction()
12891      -> KillPlayer()
12892      -> (infinitely repeating the above sequence of function calls)
12893      which occurs when killing the player while having a CE with the setting
12894      "kill player X when explosion of <player X>"; the solution using a new
12895      field "player->killed" was chosen for backwards compatibility, although
12896      clever use of the fields "player->active" etc. would probably also work */
12897 #if 1
12898   if (player->killed)
12899     return;
12900 #endif
12901
12902   player->killed = TRUE;
12903
12904   /* remove accessible field at the player's position */
12905   Feld[jx][jy] = EL_EMPTY;
12906
12907   /* deactivate shield (else Bang()/Explode() would not work right) */
12908   player->shield_normal_time_left = 0;
12909   player->shield_deadly_time_left = 0;
12910
12911 #if 0
12912   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12913          player->killed, player->active, player->reanimated);
12914 #endif
12915
12916   Bang(jx, jy);
12917
12918 #if 0
12919   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12920          player->killed, player->active, player->reanimated);
12921 #endif
12922
12923   if (player->reanimated)       /* killed player may have been reanimated */
12924     player->killed = player->reanimated = FALSE;
12925   else
12926     BuryPlayer(player);
12927 }
12928
12929 static void KillPlayerUnlessEnemyProtected(int x, int y)
12930 {
12931   if (!PLAYER_ENEMY_PROTECTED(x, y))
12932     KillPlayer(PLAYERINFO(x, y));
12933 }
12934
12935 static void KillPlayerUnlessExplosionProtected(int x, int y)
12936 {
12937   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12938     KillPlayer(PLAYERINFO(x, y));
12939 }
12940
12941 void BuryPlayer(struct PlayerInfo *player)
12942 {
12943   int jx = player->jx, jy = player->jy;
12944
12945   if (!player->active)
12946     return;
12947
12948   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12949   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12950
12951   player->GameOver = TRUE;
12952   RemovePlayer(player);
12953 }
12954
12955 void RemovePlayer(struct PlayerInfo *player)
12956 {
12957   int jx = player->jx, jy = player->jy;
12958   int i, found = FALSE;
12959
12960   player->present = FALSE;
12961   player->active = FALSE;
12962
12963   if (!ExplodeField[jx][jy])
12964     StorePlayer[jx][jy] = 0;
12965
12966   if (player->is_moving)
12967     TEST_DrawLevelField(player->last_jx, player->last_jy);
12968
12969   for (i = 0; i < MAX_PLAYERS; i++)
12970     if (stored_player[i].active)
12971       found = TRUE;
12972
12973   if (!found)
12974     AllPlayersGone = TRUE;
12975
12976   ExitX = ZX = jx;
12977   ExitY = ZY = jy;
12978 }
12979
12980 static void setFieldForSnapping(int x, int y, int element, int direction)
12981 {
12982   struct ElementInfo *ei = &element_info[element];
12983   int direction_bit = MV_DIR_TO_BIT(direction);
12984   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12985   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12986                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12987
12988   Feld[x][y] = EL_ELEMENT_SNAPPING;
12989   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12990
12991   ResetGfxAnimation(x, y);
12992
12993   GfxElement[x][y] = element;
12994   GfxAction[x][y] = action;
12995   GfxDir[x][y] = direction;
12996   GfxFrame[x][y] = -1;
12997 }
12998
12999 /*
13000   =============================================================================
13001   checkDiagonalPushing()
13002   -----------------------------------------------------------------------------
13003   check if diagonal input device direction results in pushing of object
13004   (by checking if the alternative direction is walkable, diggable, ...)
13005   =============================================================================
13006 */
13007
13008 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13009                                     int x, int y, int real_dx, int real_dy)
13010 {
13011   int jx, jy, dx, dy, xx, yy;
13012
13013   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13014     return TRUE;
13015
13016   /* diagonal direction: check alternative direction */
13017   jx = player->jx;
13018   jy = player->jy;
13019   dx = x - jx;
13020   dy = y - jy;
13021   xx = jx + (dx == 0 ? real_dx : 0);
13022   yy = jy + (dy == 0 ? real_dy : 0);
13023
13024   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13025 }
13026
13027 /*
13028   =============================================================================
13029   DigField()
13030   -----------------------------------------------------------------------------
13031   x, y:                 field next to player (non-diagonal) to try to dig to
13032   real_dx, real_dy:     direction as read from input device (can be diagonal)
13033   =============================================================================
13034 */
13035
13036 static int DigField(struct PlayerInfo *player,
13037                     int oldx, int oldy, int x, int y,
13038                     int real_dx, int real_dy, int mode)
13039 {
13040   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13041   boolean player_was_pushing = player->is_pushing;
13042   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13043   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13044   int jx = oldx, jy = oldy;
13045   int dx = x - jx, dy = y - jy;
13046   int nextx = x + dx, nexty = y + dy;
13047   int move_direction = (dx == -1 ? MV_LEFT  :
13048                         dx == +1 ? MV_RIGHT :
13049                         dy == -1 ? MV_UP    :
13050                         dy == +1 ? MV_DOWN  : MV_NONE);
13051   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13052   int dig_side = MV_DIR_OPPOSITE(move_direction);
13053   int old_element = Feld[jx][jy];
13054   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13055   int collect_count;
13056
13057   if (is_player)                /* function can also be called by EL_PENGUIN */
13058   {
13059     if (player->MovPos == 0)
13060     {
13061       player->is_digging = FALSE;
13062       player->is_collecting = FALSE;
13063     }
13064
13065     if (player->MovPos == 0)    /* last pushing move finished */
13066       player->is_pushing = FALSE;
13067
13068     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13069     {
13070       player->is_switching = FALSE;
13071       player->push_delay = -1;
13072
13073       return MP_NO_ACTION;
13074     }
13075   }
13076
13077   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13078     old_element = Back[jx][jy];
13079
13080   /* in case of element dropped at player position, check background */
13081   else if (Back[jx][jy] != EL_EMPTY &&
13082            game.engine_version >= VERSION_IDENT(2,2,0,0))
13083     old_element = Back[jx][jy];
13084
13085   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13086     return MP_NO_ACTION;        /* field has no opening in this direction */
13087
13088   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13089     return MP_NO_ACTION;        /* field has no opening in this direction */
13090
13091   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13092   {
13093     SplashAcid(x, y);
13094
13095     Feld[jx][jy] = player->artwork_element;
13096     InitMovingField(jx, jy, MV_DOWN);
13097     Store[jx][jy] = EL_ACID;
13098     ContinueMoving(jx, jy);
13099     BuryPlayer(player);
13100
13101     return MP_DONT_RUN_INTO;
13102   }
13103
13104   if (player_can_move && DONT_RUN_INTO(element))
13105   {
13106     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13107
13108     return MP_DONT_RUN_INTO;
13109   }
13110
13111   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13112     return MP_NO_ACTION;
13113
13114   collect_count = element_info[element].collect_count_initial;
13115
13116   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13117     return MP_NO_ACTION;
13118
13119   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13120     player_can_move = player_can_move_or_snap;
13121
13122   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13123       game.engine_version >= VERSION_IDENT(2,2,0,0))
13124   {
13125     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13126                                player->index_bit, dig_side);
13127     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13128                                         player->index_bit, dig_side);
13129
13130     if (element == EL_DC_LANDMINE)
13131       Bang(x, y);
13132
13133     if (Feld[x][y] != element)          /* field changed by snapping */
13134       return MP_ACTION;
13135
13136     return MP_NO_ACTION;
13137   }
13138
13139   if (player->gravity && is_player && !player->is_auto_moving &&
13140       canFallDown(player) && move_direction != MV_DOWN &&
13141       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13142     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13143
13144   if (player_can_move &&
13145       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13146   {
13147     int sound_element = SND_ELEMENT(element);
13148     int sound_action = ACTION_WALKING;
13149
13150     if (IS_RND_GATE(element))
13151     {
13152       if (!player->key[RND_GATE_NR(element)])
13153         return MP_NO_ACTION;
13154     }
13155     else if (IS_RND_GATE_GRAY(element))
13156     {
13157       if (!player->key[RND_GATE_GRAY_NR(element)])
13158         return MP_NO_ACTION;
13159     }
13160     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13161     {
13162       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13163         return MP_NO_ACTION;
13164     }
13165     else if (element == EL_EXIT_OPEN ||
13166              element == EL_EM_EXIT_OPEN ||
13167              element == EL_EM_EXIT_OPENING ||
13168              element == EL_STEEL_EXIT_OPEN ||
13169              element == EL_EM_STEEL_EXIT_OPEN ||
13170              element == EL_EM_STEEL_EXIT_OPENING ||
13171              element == EL_SP_EXIT_OPEN ||
13172              element == EL_SP_EXIT_OPENING)
13173     {
13174       sound_action = ACTION_PASSING;    /* player is passing exit */
13175     }
13176     else if (element == EL_EMPTY)
13177     {
13178       sound_action = ACTION_MOVING;             /* nothing to walk on */
13179     }
13180
13181     /* play sound from background or player, whatever is available */
13182     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13183       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13184     else
13185       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13186   }
13187   else if (player_can_move &&
13188            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13189   {
13190     if (!ACCESS_FROM(element, opposite_direction))
13191       return MP_NO_ACTION;      /* field not accessible from this direction */
13192
13193     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13194       return MP_NO_ACTION;
13195
13196     if (IS_EM_GATE(element))
13197     {
13198       if (!player->key[EM_GATE_NR(element)])
13199         return MP_NO_ACTION;
13200     }
13201     else if (IS_EM_GATE_GRAY(element))
13202     {
13203       if (!player->key[EM_GATE_GRAY_NR(element)])
13204         return MP_NO_ACTION;
13205     }
13206     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13207     {
13208       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13209         return MP_NO_ACTION;
13210     }
13211     else if (IS_EMC_GATE(element))
13212     {
13213       if (!player->key[EMC_GATE_NR(element)])
13214         return MP_NO_ACTION;
13215     }
13216     else if (IS_EMC_GATE_GRAY(element))
13217     {
13218       if (!player->key[EMC_GATE_GRAY_NR(element)])
13219         return MP_NO_ACTION;
13220     }
13221     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13222     {
13223       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13224         return MP_NO_ACTION;
13225     }
13226     else if (element == EL_DC_GATE_WHITE ||
13227              element == EL_DC_GATE_WHITE_GRAY ||
13228              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13229     {
13230       if (player->num_white_keys == 0)
13231         return MP_NO_ACTION;
13232
13233       player->num_white_keys--;
13234     }
13235     else if (IS_SP_PORT(element))
13236     {
13237       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13238           element == EL_SP_GRAVITY_PORT_RIGHT ||
13239           element == EL_SP_GRAVITY_PORT_UP ||
13240           element == EL_SP_GRAVITY_PORT_DOWN)
13241         player->gravity = !player->gravity;
13242       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13243                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13244                element == EL_SP_GRAVITY_ON_PORT_UP ||
13245                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13246         player->gravity = TRUE;
13247       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13248                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13249                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13250                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13251         player->gravity = FALSE;
13252     }
13253
13254     /* automatically move to the next field with double speed */
13255     player->programmed_action = move_direction;
13256
13257     if (player->move_delay_reset_counter == 0)
13258     {
13259       player->move_delay_reset_counter = 2;     /* two double speed steps */
13260
13261       DOUBLE_PLAYER_SPEED(player);
13262     }
13263
13264     PlayLevelSoundAction(x, y, ACTION_PASSING);
13265   }
13266   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13267   {
13268     RemoveField(x, y);
13269
13270     if (mode != DF_SNAP)
13271     {
13272       GfxElement[x][y] = GFX_ELEMENT(element);
13273       player->is_digging = TRUE;
13274     }
13275
13276     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13277
13278     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13279                                         player->index_bit, dig_side);
13280
13281     if (mode == DF_SNAP)
13282     {
13283       if (level.block_snap_field)
13284         setFieldForSnapping(x, y, element, move_direction);
13285       else
13286         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13287
13288       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13289                                           player->index_bit, dig_side);
13290     }
13291   }
13292   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13293   {
13294     RemoveField(x, y);
13295
13296     if (is_player && mode != DF_SNAP)
13297     {
13298       GfxElement[x][y] = element;
13299       player->is_collecting = TRUE;
13300     }
13301
13302     if (element == EL_SPEED_PILL)
13303     {
13304       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13305     }
13306     else if (element == EL_EXTRA_TIME && level.time > 0)
13307     {
13308       TimeLeft += level.extra_time;
13309
13310       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13311
13312       DisplayGameControlValues();
13313     }
13314     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13315     {
13316       player->shield_normal_time_left += level.shield_normal_time;
13317       if (element == EL_SHIELD_DEADLY)
13318         player->shield_deadly_time_left += level.shield_deadly_time;
13319     }
13320     else if (element == EL_DYNAMITE ||
13321              element == EL_EM_DYNAMITE ||
13322              element == EL_SP_DISK_RED)
13323     {
13324       if (player->inventory_size < MAX_INVENTORY_SIZE)
13325         player->inventory_element[player->inventory_size++] = element;
13326
13327       DrawGameDoorValues();
13328     }
13329     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13330     {
13331       player->dynabomb_count++;
13332       player->dynabombs_left++;
13333     }
13334     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13335     {
13336       player->dynabomb_size++;
13337     }
13338     else if (element == EL_DYNABOMB_INCREASE_POWER)
13339     {
13340       player->dynabomb_xl = TRUE;
13341     }
13342     else if (IS_KEY(element))
13343     {
13344       player->key[KEY_NR(element)] = TRUE;
13345
13346       DrawGameDoorValues();
13347     }
13348     else if (element == EL_DC_KEY_WHITE)
13349     {
13350       player->num_white_keys++;
13351
13352       /* display white keys? */
13353       /* DrawGameDoorValues(); */
13354     }
13355     else if (IS_ENVELOPE(element))
13356     {
13357       player->show_envelope = element;
13358     }
13359     else if (element == EL_EMC_LENSES)
13360     {
13361       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13362
13363       RedrawAllInvisibleElementsForLenses();
13364     }
13365     else if (element == EL_EMC_MAGNIFIER)
13366     {
13367       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13368
13369       RedrawAllInvisibleElementsForMagnifier();
13370     }
13371     else if (IS_DROPPABLE(element) ||
13372              IS_THROWABLE(element))     /* can be collected and dropped */
13373     {
13374       int i;
13375
13376       if (collect_count == 0)
13377         player->inventory_infinite_element = element;
13378       else
13379         for (i = 0; i < collect_count; i++)
13380           if (player->inventory_size < MAX_INVENTORY_SIZE)
13381             player->inventory_element[player->inventory_size++] = element;
13382
13383       DrawGameDoorValues();
13384     }
13385     else if (collect_count > 0)
13386     {
13387       local_player->gems_still_needed -= collect_count;
13388       if (local_player->gems_still_needed < 0)
13389         local_player->gems_still_needed = 0;
13390
13391       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13392
13393       DisplayGameControlValues();
13394     }
13395
13396     RaiseScoreElement(element);
13397     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13398
13399     if (is_player)
13400       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13401                                           player->index_bit, dig_side);
13402
13403     if (mode == DF_SNAP)
13404     {
13405       if (level.block_snap_field)
13406         setFieldForSnapping(x, y, element, move_direction);
13407       else
13408         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13409
13410       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13411                                           player->index_bit, dig_side);
13412     }
13413   }
13414   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13415   {
13416     if (mode == DF_SNAP && element != EL_BD_ROCK)
13417       return MP_NO_ACTION;
13418
13419     if (CAN_FALL(element) && dy)
13420       return MP_NO_ACTION;
13421
13422     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13423         !(element == EL_SPRING && level.use_spring_bug))
13424       return MP_NO_ACTION;
13425
13426     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13427         ((move_direction & MV_VERTICAL &&
13428           ((element_info[element].move_pattern & MV_LEFT &&
13429             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13430            (element_info[element].move_pattern & MV_RIGHT &&
13431             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13432          (move_direction & MV_HORIZONTAL &&
13433           ((element_info[element].move_pattern & MV_UP &&
13434             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13435            (element_info[element].move_pattern & MV_DOWN &&
13436             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13437       return MP_NO_ACTION;
13438
13439     /* do not push elements already moving away faster than player */
13440     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13441         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13442       return MP_NO_ACTION;
13443
13444     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13445     {
13446       if (player->push_delay_value == -1 || !player_was_pushing)
13447         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13448     }
13449     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13450     {
13451       if (player->push_delay_value == -1)
13452         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13453     }
13454     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13455     {
13456       if (!player->is_pushing)
13457         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13458     }
13459
13460     player->is_pushing = TRUE;
13461     player->is_active = TRUE;
13462
13463     if (!(IN_LEV_FIELD(nextx, nexty) &&
13464           (IS_FREE(nextx, nexty) ||
13465            (IS_SB_ELEMENT(element) &&
13466             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13467            (IS_CUSTOM_ELEMENT(element) &&
13468             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13469       return MP_NO_ACTION;
13470
13471     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13472       return MP_NO_ACTION;
13473
13474     if (player->push_delay == -1)       /* new pushing; restart delay */
13475       player->push_delay = 0;
13476
13477     if (player->push_delay < player->push_delay_value &&
13478         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13479         element != EL_SPRING && element != EL_BALLOON)
13480     {
13481       /* make sure that there is no move delay before next try to push */
13482       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13483         player->move_delay = 0;
13484
13485       return MP_NO_ACTION;
13486     }
13487
13488     if (IS_CUSTOM_ELEMENT(element) &&
13489         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13490     {
13491       if (!DigFieldByCE(nextx, nexty, element))
13492         return MP_NO_ACTION;
13493     }
13494
13495     if (IS_SB_ELEMENT(element))
13496     {
13497       if (element == EL_SOKOBAN_FIELD_FULL)
13498       {
13499         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13500         local_player->sokobanfields_still_needed++;
13501       }
13502
13503       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13504       {
13505         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13506         local_player->sokobanfields_still_needed--;
13507       }
13508
13509       Feld[x][y] = EL_SOKOBAN_OBJECT;
13510
13511       if (Back[x][y] == Back[nextx][nexty])
13512         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13513       else if (Back[x][y] != 0)
13514         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13515                                     ACTION_EMPTYING);
13516       else
13517         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13518                                     ACTION_FILLING);
13519
13520       if (local_player->sokobanfields_still_needed == 0 &&
13521           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13522       {
13523         PlayerWins(player);
13524
13525         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13526       }
13527     }
13528     else
13529       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13530
13531     InitMovingField(x, y, move_direction);
13532     GfxAction[x][y] = ACTION_PUSHING;
13533
13534     if (mode == DF_SNAP)
13535       ContinueMoving(x, y);
13536     else
13537       MovPos[x][y] = (dx != 0 ? dx : dy);
13538
13539     Pushed[x][y] = TRUE;
13540     Pushed[nextx][nexty] = TRUE;
13541
13542     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13543       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13544     else
13545       player->push_delay_value = -1;    /* get new value later */
13546
13547     /* check for element change _after_ element has been pushed */
13548     if (game.use_change_when_pushing_bug)
13549     {
13550       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13551                                  player->index_bit, dig_side);
13552       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13553                                           player->index_bit, dig_side);
13554     }
13555   }
13556   else if (IS_SWITCHABLE(element))
13557   {
13558     if (PLAYER_SWITCHING(player, x, y))
13559     {
13560       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13561                                           player->index_bit, dig_side);
13562
13563       return MP_ACTION;
13564     }
13565
13566     player->is_switching = TRUE;
13567     player->switch_x = x;
13568     player->switch_y = y;
13569
13570     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13571
13572     if (element == EL_ROBOT_WHEEL)
13573     {
13574       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13575       ZX = x;
13576       ZY = y;
13577
13578       game.robot_wheel_active = TRUE;
13579
13580       TEST_DrawLevelField(x, y);
13581     }
13582     else if (element == EL_SP_TERMINAL)
13583     {
13584       int xx, yy;
13585
13586       SCAN_PLAYFIELD(xx, yy)
13587       {
13588         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13589           Bang(xx, yy);
13590         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13591           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13592       }
13593     }
13594     else if (IS_BELT_SWITCH(element))
13595     {
13596       ToggleBeltSwitch(x, y);
13597     }
13598     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13599              element == EL_SWITCHGATE_SWITCH_DOWN ||
13600              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13601              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13602     {
13603       ToggleSwitchgateSwitch(x, y);
13604     }
13605     else if (element == EL_LIGHT_SWITCH ||
13606              element == EL_LIGHT_SWITCH_ACTIVE)
13607     {
13608       ToggleLightSwitch(x, y);
13609     }
13610     else if (element == EL_TIMEGATE_SWITCH ||
13611              element == EL_DC_TIMEGATE_SWITCH)
13612     {
13613       ActivateTimegateSwitch(x, y);
13614     }
13615     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13616              element == EL_BALLOON_SWITCH_RIGHT ||
13617              element == EL_BALLOON_SWITCH_UP    ||
13618              element == EL_BALLOON_SWITCH_DOWN  ||
13619              element == EL_BALLOON_SWITCH_NONE  ||
13620              element == EL_BALLOON_SWITCH_ANY)
13621     {
13622       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13623                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13624                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13625                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13626                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13627                              move_direction);
13628     }
13629     else if (element == EL_LAMP)
13630     {
13631       Feld[x][y] = EL_LAMP_ACTIVE;
13632       local_player->lights_still_needed--;
13633
13634       ResetGfxAnimation(x, y);
13635       TEST_DrawLevelField(x, y);
13636     }
13637     else if (element == EL_TIME_ORB_FULL)
13638     {
13639       Feld[x][y] = EL_TIME_ORB_EMPTY;
13640
13641       if (level.time > 0 || level.use_time_orb_bug)
13642       {
13643         TimeLeft += level.time_orb_time;
13644         game.no_time_limit = FALSE;
13645
13646         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13647
13648         DisplayGameControlValues();
13649       }
13650
13651       ResetGfxAnimation(x, y);
13652       TEST_DrawLevelField(x, y);
13653     }
13654     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13655              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13656     {
13657       int xx, yy;
13658
13659       game.ball_state = !game.ball_state;
13660
13661       SCAN_PLAYFIELD(xx, yy)
13662       {
13663         int e = Feld[xx][yy];
13664
13665         if (game.ball_state)
13666         {
13667           if (e == EL_EMC_MAGIC_BALL)
13668             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13669           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13670             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13671         }
13672         else
13673         {
13674           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13675             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13676           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13677             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13678         }
13679       }
13680     }
13681
13682     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13683                                         player->index_bit, dig_side);
13684
13685     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13686                                         player->index_bit, dig_side);
13687
13688     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13689                                         player->index_bit, dig_side);
13690
13691     return MP_ACTION;
13692   }
13693   else
13694   {
13695     if (!PLAYER_SWITCHING(player, x, y))
13696     {
13697       player->is_switching = TRUE;
13698       player->switch_x = x;
13699       player->switch_y = y;
13700
13701       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13702                                  player->index_bit, dig_side);
13703       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13704                                           player->index_bit, dig_side);
13705
13706       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13707                                  player->index_bit, dig_side);
13708       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13709                                           player->index_bit, dig_side);
13710     }
13711
13712     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13713                                player->index_bit, dig_side);
13714     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13715                                         player->index_bit, dig_side);
13716
13717     return MP_NO_ACTION;
13718   }
13719
13720   player->push_delay = -1;
13721
13722   if (is_player)                /* function can also be called by EL_PENGUIN */
13723   {
13724     if (Feld[x][y] != element)          /* really digged/collected something */
13725     {
13726       player->is_collecting = !player->is_digging;
13727       player->is_active = TRUE;
13728     }
13729   }
13730
13731   return MP_MOVING;
13732 }
13733
13734 static boolean DigFieldByCE(int x, int y, int digging_element)
13735 {
13736   int element = Feld[x][y];
13737
13738   if (!IS_FREE(x, y))
13739   {
13740     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13741                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13742                   ACTION_BREAKING);
13743
13744     /* no element can dig solid indestructible elements */
13745     if (IS_INDESTRUCTIBLE(element) &&
13746         !IS_DIGGABLE(element) &&
13747         !IS_COLLECTIBLE(element))
13748       return FALSE;
13749
13750     if (AmoebaNr[x][y] &&
13751         (element == EL_AMOEBA_FULL ||
13752          element == EL_BD_AMOEBA ||
13753          element == EL_AMOEBA_GROWING))
13754     {
13755       AmoebaCnt[AmoebaNr[x][y]]--;
13756       AmoebaCnt2[AmoebaNr[x][y]]--;
13757     }
13758
13759     if (IS_MOVING(x, y))
13760       RemoveMovingField(x, y);
13761     else
13762     {
13763       RemoveField(x, y);
13764       TEST_DrawLevelField(x, y);
13765     }
13766
13767     /* if digged element was about to explode, prevent the explosion */
13768     ExplodeField[x][y] = EX_TYPE_NONE;
13769
13770     PlayLevelSoundAction(x, y, action);
13771   }
13772
13773   Store[x][y] = EL_EMPTY;
13774
13775   /* this makes it possible to leave the removed element again */
13776   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13777     Store[x][y] = element;
13778
13779   return TRUE;
13780 }
13781
13782 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13783 {
13784   int jx = player->jx, jy = player->jy;
13785   int x = jx + dx, y = jy + dy;
13786   int snap_direction = (dx == -1 ? MV_LEFT  :
13787                         dx == +1 ? MV_RIGHT :
13788                         dy == -1 ? MV_UP    :
13789                         dy == +1 ? MV_DOWN  : MV_NONE);
13790   boolean can_continue_snapping = (level.continuous_snapping &&
13791                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13792
13793   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13794     return FALSE;
13795
13796   if (!player->active || !IN_LEV_FIELD(x, y))
13797     return FALSE;
13798
13799   if (dx && dy)
13800     return FALSE;
13801
13802   if (!dx && !dy)
13803   {
13804     if (player->MovPos == 0)
13805       player->is_pushing = FALSE;
13806
13807     player->is_snapping = FALSE;
13808
13809     if (player->MovPos == 0)
13810     {
13811       player->is_moving = FALSE;
13812       player->is_digging = FALSE;
13813       player->is_collecting = FALSE;
13814     }
13815
13816     return FALSE;
13817   }
13818
13819   /* prevent snapping with already pressed snap key when not allowed */
13820   if (player->is_snapping && !can_continue_snapping)
13821     return FALSE;
13822
13823   player->MovDir = snap_direction;
13824
13825   if (player->MovPos == 0)
13826   {
13827     player->is_moving = FALSE;
13828     player->is_digging = FALSE;
13829     player->is_collecting = FALSE;
13830   }
13831
13832   player->is_dropping = FALSE;
13833   player->is_dropping_pressed = FALSE;
13834   player->drop_pressed_delay = 0;
13835
13836   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13837     return FALSE;
13838
13839   player->is_snapping = TRUE;
13840   player->is_active = TRUE;
13841
13842   if (player->MovPos == 0)
13843   {
13844     player->is_moving = FALSE;
13845     player->is_digging = FALSE;
13846     player->is_collecting = FALSE;
13847   }
13848
13849   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13850     TEST_DrawLevelField(player->last_jx, player->last_jy);
13851
13852   TEST_DrawLevelField(x, y);
13853
13854   return TRUE;
13855 }
13856
13857 static boolean DropElement(struct PlayerInfo *player)
13858 {
13859   int old_element, new_element;
13860   int dropx = player->jx, dropy = player->jy;
13861   int drop_direction = player->MovDir;
13862   int drop_side = drop_direction;
13863   int drop_element = get_next_dropped_element(player);
13864
13865   player->is_dropping_pressed = TRUE;
13866
13867   /* do not drop an element on top of another element; when holding drop key
13868      pressed without moving, dropped element must move away before the next
13869      element can be dropped (this is especially important if the next element
13870      is dynamite, which can be placed on background for historical reasons) */
13871   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13872     return MP_ACTION;
13873
13874   if (IS_THROWABLE(drop_element))
13875   {
13876     dropx += GET_DX_FROM_DIR(drop_direction);
13877     dropy += GET_DY_FROM_DIR(drop_direction);
13878
13879     if (!IN_LEV_FIELD(dropx, dropy))
13880       return FALSE;
13881   }
13882
13883   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13884   new_element = drop_element;           /* default: no change when dropping */
13885
13886   /* check if player is active, not moving and ready to drop */
13887   if (!player->active || player->MovPos || player->drop_delay > 0)
13888     return FALSE;
13889
13890   /* check if player has anything that can be dropped */
13891   if (new_element == EL_UNDEFINED)
13892     return FALSE;
13893
13894   /* check if drop key was pressed long enough for EM style dynamite */
13895   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13896     return FALSE;
13897
13898   /* check if anything can be dropped at the current position */
13899   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13900     return FALSE;
13901
13902   /* collected custom elements can only be dropped on empty fields */
13903   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13904     return FALSE;
13905
13906   if (old_element != EL_EMPTY)
13907     Back[dropx][dropy] = old_element;   /* store old element on this field */
13908
13909   ResetGfxAnimation(dropx, dropy);
13910   ResetRandomAnimationValue(dropx, dropy);
13911
13912   if (player->inventory_size > 0 ||
13913       player->inventory_infinite_element != EL_UNDEFINED)
13914   {
13915     if (player->inventory_size > 0)
13916     {
13917       player->inventory_size--;
13918
13919       DrawGameDoorValues();
13920
13921       if (new_element == EL_DYNAMITE)
13922         new_element = EL_DYNAMITE_ACTIVE;
13923       else if (new_element == EL_EM_DYNAMITE)
13924         new_element = EL_EM_DYNAMITE_ACTIVE;
13925       else if (new_element == EL_SP_DISK_RED)
13926         new_element = EL_SP_DISK_RED_ACTIVE;
13927     }
13928
13929     Feld[dropx][dropy] = new_element;
13930
13931     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13932       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13933                           el2img(Feld[dropx][dropy]), 0);
13934
13935     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13936
13937     /* needed if previous element just changed to "empty" in the last frame */
13938     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13939
13940     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13941                                player->index_bit, drop_side);
13942     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13943                                         CE_PLAYER_DROPS_X,
13944                                         player->index_bit, drop_side);
13945
13946     TestIfElementTouchesCustomElement(dropx, dropy);
13947   }
13948   else          /* player is dropping a dyna bomb */
13949   {
13950     player->dynabombs_left--;
13951
13952     Feld[dropx][dropy] = new_element;
13953
13954     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13955       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13956                           el2img(Feld[dropx][dropy]), 0);
13957
13958     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13959   }
13960
13961   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13962     InitField_WithBug1(dropx, dropy, FALSE);
13963
13964   new_element = Feld[dropx][dropy];     /* element might have changed */
13965
13966   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13967       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13968   {
13969     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13970       MovDir[dropx][dropy] = drop_direction;
13971
13972     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13973
13974     /* do not cause impact style collision by dropping elements that can fall */
13975     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13976   }
13977
13978   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13979   player->is_dropping = TRUE;
13980
13981   player->drop_pressed_delay = 0;
13982   player->is_dropping_pressed = FALSE;
13983
13984   player->drop_x = dropx;
13985   player->drop_y = dropy;
13986
13987   return TRUE;
13988 }
13989
13990 /* ------------------------------------------------------------------------- */
13991 /* game sound playing functions                                              */
13992 /* ------------------------------------------------------------------------- */
13993
13994 static int *loop_sound_frame = NULL;
13995 static int *loop_sound_volume = NULL;
13996
13997 void InitPlayLevelSound()
13998 {
13999   int num_sounds = getSoundListSize();
14000
14001   checked_free(loop_sound_frame);
14002   checked_free(loop_sound_volume);
14003
14004   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14005   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14006 }
14007
14008 static void PlayLevelSound(int x, int y, int nr)
14009 {
14010   int sx = SCREENX(x), sy = SCREENY(y);
14011   int volume, stereo_position;
14012   int max_distance = 8;
14013   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14014
14015   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14016       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14017     return;
14018
14019   if (!IN_LEV_FIELD(x, y) ||
14020       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14021       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14022     return;
14023
14024   volume = SOUND_MAX_VOLUME;
14025
14026   if (!IN_SCR_FIELD(sx, sy))
14027   {
14028     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14029     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14030
14031     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14032   }
14033
14034   stereo_position = (SOUND_MAX_LEFT +
14035                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14036                      (SCR_FIELDX + 2 * max_distance));
14037
14038   if (IS_LOOP_SOUND(nr))
14039   {
14040     /* This assures that quieter loop sounds do not overwrite louder ones,
14041        while restarting sound volume comparison with each new game frame. */
14042
14043     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14044       return;
14045
14046     loop_sound_volume[nr] = volume;
14047     loop_sound_frame[nr] = FrameCounter;
14048   }
14049
14050   PlaySoundExt(nr, volume, stereo_position, type);
14051 }
14052
14053 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14054 {
14055   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14056                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14057                  y < LEVELY(BY1) ? LEVELY(BY1) :
14058                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14059                  sound_action);
14060 }
14061
14062 static void PlayLevelSoundAction(int x, int y, int action)
14063 {
14064   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14065 }
14066
14067 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14068 {
14069   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14070
14071   if (sound_effect != SND_UNDEFINED)
14072     PlayLevelSound(x, y, sound_effect);
14073 }
14074
14075 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14076                                               int action)
14077 {
14078   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14079
14080   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14081     PlayLevelSound(x, y, sound_effect);
14082 }
14083
14084 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14085 {
14086   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14087
14088   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14089     PlayLevelSound(x, y, sound_effect);
14090 }
14091
14092 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14093 {
14094   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14095
14096   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14097     StopSound(sound_effect);
14098 }
14099
14100 static void PlayLevelMusic()
14101 {
14102   if (levelset.music[level_nr] != MUS_UNDEFINED)
14103     PlayMusic(levelset.music[level_nr]);        /* from config file */
14104   else
14105     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14106 }
14107
14108 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14109 {
14110   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14111   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14112   int x = xx - 1 - offset;
14113   int y = yy - 1 - offset;
14114
14115   switch (sample)
14116   {
14117     case SAMPLE_blank:
14118       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14119       break;
14120
14121     case SAMPLE_roll:
14122       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14123       break;
14124
14125     case SAMPLE_stone:
14126       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14127       break;
14128
14129     case SAMPLE_nut:
14130       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14131       break;
14132
14133     case SAMPLE_crack:
14134       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14135       break;
14136
14137     case SAMPLE_bug:
14138       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14139       break;
14140
14141     case SAMPLE_tank:
14142       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14143       break;
14144
14145     case SAMPLE_android_clone:
14146       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14147       break;
14148
14149     case SAMPLE_android_move:
14150       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14151       break;
14152
14153     case SAMPLE_spring:
14154       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14155       break;
14156
14157     case SAMPLE_slurp:
14158       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14159       break;
14160
14161     case SAMPLE_eater:
14162       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14163       break;
14164
14165     case SAMPLE_eater_eat:
14166       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14167       break;
14168
14169     case SAMPLE_alien:
14170       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14171       break;
14172
14173     case SAMPLE_collect:
14174       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14175       break;
14176
14177     case SAMPLE_diamond:
14178       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14179       break;
14180
14181     case SAMPLE_squash:
14182       /* !!! CHECK THIS !!! */
14183 #if 1
14184       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14185 #else
14186       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14187 #endif
14188       break;
14189
14190     case SAMPLE_wonderfall:
14191       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14192       break;
14193
14194     case SAMPLE_drip:
14195       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14196       break;
14197
14198     case SAMPLE_push:
14199       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14200       break;
14201
14202     case SAMPLE_dirt:
14203       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14204       break;
14205
14206     case SAMPLE_acid:
14207       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14208       break;
14209
14210     case SAMPLE_ball:
14211       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14212       break;
14213
14214     case SAMPLE_grow:
14215       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14216       break;
14217
14218     case SAMPLE_wonder:
14219       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14220       break;
14221
14222     case SAMPLE_door:
14223       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14224       break;
14225
14226     case SAMPLE_exit_open:
14227       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14228       break;
14229
14230     case SAMPLE_exit_leave:
14231       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14232       break;
14233
14234     case SAMPLE_dynamite:
14235       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14236       break;
14237
14238     case SAMPLE_tick:
14239       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14240       break;
14241
14242     case SAMPLE_press:
14243       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14244       break;
14245
14246     case SAMPLE_wheel:
14247       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14248       break;
14249
14250     case SAMPLE_boom:
14251       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14252       break;
14253
14254     case SAMPLE_die:
14255       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14256       break;
14257
14258     case SAMPLE_time:
14259       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14260       break;
14261
14262     default:
14263       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14264       break;
14265   }
14266 }
14267
14268 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14269 {
14270   int element = map_element_SP_to_RND(element_sp);
14271   int action = map_action_SP_to_RND(action_sp);
14272   int offset = (setup.sp_show_border_elements ? 0 : 1);
14273   int x = xx - offset;
14274   int y = yy - offset;
14275
14276   PlayLevelSoundElementAction(x, y, element, action);
14277 }
14278
14279 void RaiseScore(int value)
14280 {
14281   local_player->score += value;
14282
14283   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14284
14285   DisplayGameControlValues();
14286 }
14287
14288 void RaiseScoreElement(int element)
14289 {
14290   switch (element)
14291   {
14292     case EL_EMERALD:
14293     case EL_BD_DIAMOND:
14294     case EL_EMERALD_YELLOW:
14295     case EL_EMERALD_RED:
14296     case EL_EMERALD_PURPLE:
14297     case EL_SP_INFOTRON:
14298       RaiseScore(level.score[SC_EMERALD]);
14299       break;
14300     case EL_DIAMOND:
14301       RaiseScore(level.score[SC_DIAMOND]);
14302       break;
14303     case EL_CRYSTAL:
14304       RaiseScore(level.score[SC_CRYSTAL]);
14305       break;
14306     case EL_PEARL:
14307       RaiseScore(level.score[SC_PEARL]);
14308       break;
14309     case EL_BUG:
14310     case EL_BD_BUTTERFLY:
14311     case EL_SP_ELECTRON:
14312       RaiseScore(level.score[SC_BUG]);
14313       break;
14314     case EL_SPACESHIP:
14315     case EL_BD_FIREFLY:
14316     case EL_SP_SNIKSNAK:
14317       RaiseScore(level.score[SC_SPACESHIP]);
14318       break;
14319     case EL_YAMYAM:
14320     case EL_DARK_YAMYAM:
14321       RaiseScore(level.score[SC_YAMYAM]);
14322       break;
14323     case EL_ROBOT:
14324       RaiseScore(level.score[SC_ROBOT]);
14325       break;
14326     case EL_PACMAN:
14327       RaiseScore(level.score[SC_PACMAN]);
14328       break;
14329     case EL_NUT:
14330       RaiseScore(level.score[SC_NUT]);
14331       break;
14332     case EL_DYNAMITE:
14333     case EL_EM_DYNAMITE:
14334     case EL_SP_DISK_RED:
14335     case EL_DYNABOMB_INCREASE_NUMBER:
14336     case EL_DYNABOMB_INCREASE_SIZE:
14337     case EL_DYNABOMB_INCREASE_POWER:
14338       RaiseScore(level.score[SC_DYNAMITE]);
14339       break;
14340     case EL_SHIELD_NORMAL:
14341     case EL_SHIELD_DEADLY:
14342       RaiseScore(level.score[SC_SHIELD]);
14343       break;
14344     case EL_EXTRA_TIME:
14345       RaiseScore(level.extra_time_score);
14346       break;
14347     case EL_KEY_1:
14348     case EL_KEY_2:
14349     case EL_KEY_3:
14350     case EL_KEY_4:
14351     case EL_EM_KEY_1:
14352     case EL_EM_KEY_2:
14353     case EL_EM_KEY_3:
14354     case EL_EM_KEY_4:
14355     case EL_EMC_KEY_5:
14356     case EL_EMC_KEY_6:
14357     case EL_EMC_KEY_7:
14358     case EL_EMC_KEY_8:
14359     case EL_DC_KEY_WHITE:
14360       RaiseScore(level.score[SC_KEY]);
14361       break;
14362     default:
14363       RaiseScore(element_info[element].collect_score);
14364       break;
14365   }
14366 }
14367
14368 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14369 {
14370   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14371   {
14372     /* closing door required in case of envelope style request dialogs */
14373     if (!skip_request)
14374       CloseDoor(DOOR_CLOSE_1);
14375
14376 #if defined(NETWORK_AVALIABLE)
14377     if (options.network)
14378       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14379     else
14380 #endif
14381     {
14382       if (quick_quit)
14383       {
14384         FadeSkipNextFadeIn();
14385
14386         game_status = GAME_MODE_MAIN;
14387
14388         DrawAndFadeInMainMenu(REDRAW_FIELD);
14389       }
14390       else
14391       {
14392         game_status = GAME_MODE_MAIN;
14393
14394         DrawAndFadeInMainMenu(REDRAW_FIELD);
14395       }
14396     }
14397   }
14398   else          /* continue playing the game */
14399   {
14400     if (tape.playing && tape.deactivate_display)
14401       TapeDeactivateDisplayOff(TRUE);
14402
14403     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14404
14405     if (tape.playing && tape.deactivate_display)
14406       TapeDeactivateDisplayOn();
14407   }
14408 }
14409
14410 void RequestQuitGame(boolean ask_if_really_quit)
14411 {
14412   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14413   boolean skip_request = AllPlayersGone || quick_quit;
14414
14415   RequestQuitGameExt(skip_request, quick_quit,
14416                      "Do you really want to quit the game?");
14417 }
14418
14419
14420 /* ------------------------------------------------------------------------- */
14421 /* random generator functions                                                */
14422 /* ------------------------------------------------------------------------- */
14423
14424 unsigned int InitEngineRandom_RND(int seed)
14425 {
14426   game.num_random_calls = 0;
14427
14428   return InitEngineRandom(seed);
14429 }
14430
14431 unsigned int RND(int max)
14432 {
14433   if (max > 0)
14434   {
14435     game.num_random_calls++;
14436
14437     return GetEngineRandom(max);
14438   }
14439
14440   return 0;
14441 }
14442
14443
14444 /* ------------------------------------------------------------------------- */
14445 /* game engine snapshot handling functions                                   */
14446 /* ------------------------------------------------------------------------- */
14447
14448 struct EngineSnapshotInfo
14449 {
14450   /* runtime values for custom element collect score */
14451   int collect_score[NUM_CUSTOM_ELEMENTS];
14452
14453   /* runtime values for group element choice position */
14454   int choice_pos[NUM_GROUP_ELEMENTS];
14455
14456   /* runtime values for belt position animations */
14457   int belt_graphic[4][NUM_BELT_PARTS];
14458   int belt_anim_mode[4][NUM_BELT_PARTS];
14459 };
14460
14461 static struct EngineSnapshotInfo engine_snapshot_rnd;
14462 static char *snapshot_level_identifier = NULL;
14463 static int snapshot_level_nr = -1;
14464
14465 static void SaveEngineSnapshotValues_RND()
14466 {
14467   static int belt_base_active_element[4] =
14468   {
14469     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14470     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14471     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14472     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14473   };
14474   int i, j;
14475
14476   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14477   {
14478     int element = EL_CUSTOM_START + i;
14479
14480     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14481   }
14482
14483   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14484   {
14485     int element = EL_GROUP_START + i;
14486
14487     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14488   }
14489
14490   for (i = 0; i < 4; i++)
14491   {
14492     for (j = 0; j < NUM_BELT_PARTS; j++)
14493     {
14494       int element = belt_base_active_element[i] + j;
14495       int graphic = el2img(element);
14496       int anim_mode = graphic_info[graphic].anim_mode;
14497
14498       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14499       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14500     }
14501   }
14502 }
14503
14504 static void LoadEngineSnapshotValues_RND()
14505 {
14506   unsigned int num_random_calls = game.num_random_calls;
14507   int i, j;
14508
14509   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14510   {
14511     int element = EL_CUSTOM_START + i;
14512
14513     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14514   }
14515
14516   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14517   {
14518     int element = EL_GROUP_START + i;
14519
14520     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14521   }
14522
14523   for (i = 0; i < 4; i++)
14524   {
14525     for (j = 0; j < NUM_BELT_PARTS; j++)
14526     {
14527       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14528       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14529
14530       graphic_info[graphic].anim_mode = anim_mode;
14531     }
14532   }
14533
14534   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14535   {
14536     InitRND(tape.random_seed);
14537     for (i = 0; i < num_random_calls; i++)
14538       RND(1);
14539   }
14540
14541   if (game.num_random_calls != num_random_calls)
14542   {
14543     Error(ERR_INFO, "number of random calls out of sync");
14544     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14545     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14546     Error(ERR_EXIT, "this should not happen -- please debug");
14547   }
14548 }
14549
14550 void FreeEngineSnapshot()
14551 {
14552   FreeEngineSnapshotBuffers();
14553
14554   setString(&snapshot_level_identifier, NULL);
14555   snapshot_level_nr = -1;
14556 }
14557
14558 void SaveEngineSnapshot()
14559 {
14560   /* do not save snapshots from editor */
14561   if (level_editor_test_game)
14562     return;
14563
14564   /* free previous snapshot buffers, if needed */
14565   FreeEngineSnapshotBuffers();
14566
14567   /* copy some special values to a structure better suited for the snapshot */
14568
14569   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14570     SaveEngineSnapshotValues_RND();
14571   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14572     SaveEngineSnapshotValues_EM();
14573   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14574     SaveEngineSnapshotValues_SP();
14575
14576   /* save values stored in special snapshot structure */
14577
14578   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14579     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14580   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14581     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14582   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14583     SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14584
14585   /* save further RND engine values */
14586
14587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14590
14591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14595
14596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14601
14602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14605
14606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14607
14608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14609
14610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14612
14613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14631
14632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14634
14635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14638
14639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14641
14642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14645   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14647
14648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14650
14651   /* save level identification information */
14652
14653   setString(&snapshot_level_identifier, leveldir_current->identifier);
14654   snapshot_level_nr = level_nr;
14655
14656 #if 0
14657   ListNode *node = engine_snapshot_list_rnd;
14658   int num_bytes = 0;
14659
14660   while (node != NULL)
14661   {
14662     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14663
14664     node = node->next;
14665   }
14666
14667   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14668 #endif
14669 }
14670
14671 void LoadEngineSnapshot()
14672 {
14673   /* restore generically stored snapshot buffers */
14674
14675   LoadEngineSnapshotBuffers();
14676
14677   /* restore special values from snapshot structure */
14678
14679   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14680     LoadEngineSnapshotValues_RND();
14681   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14682     LoadEngineSnapshotValues_EM();
14683   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14684     LoadEngineSnapshotValues_SP();
14685 }
14686
14687 boolean CheckEngineSnapshot()
14688 {
14689   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14690           snapshot_level_nr == level_nr);
14691 }
14692
14693
14694 /* ---------- new game button stuff ---------------------------------------- */
14695
14696 static struct
14697 {
14698   int graphic;
14699   struct Rect *pos;
14700   int gadget_id;
14701   char *infotext;
14702 } gamebutton_info[NUM_GAME_BUTTONS] =
14703 {
14704   {
14705     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14706     GAME_CTRL_ID_STOP,                  "stop game"
14707   },
14708   {
14709     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14710     GAME_CTRL_ID_PAUSE,                 "pause game"
14711   },
14712   {
14713     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14714     GAME_CTRL_ID_PLAY,                  "play game"
14715   },
14716   {
14717     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14718     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14719   },
14720   {
14721     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14722     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14723   },
14724   {
14725     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14726     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14727   },
14728   {
14729     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14730     GAME_CTRL_ID_SAVE,                  "save game"
14731   },
14732   {
14733     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14734     GAME_CTRL_ID_LOAD,                  "load game"
14735   }
14736 };
14737
14738 void CreateGameButtons()
14739 {
14740   int i;
14741
14742   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14743   {
14744     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14745     struct Rect *pos = gamebutton_info[i].pos;
14746     struct GadgetInfo *gi;
14747     int button_type;
14748     boolean checked;
14749     unsigned int event_mask;
14750     int base_x = (tape.show_game_buttons ? VX : DX);
14751     int base_y = (tape.show_game_buttons ? VY : DY);
14752     int gd_x   = gfx->src_x;
14753     int gd_y   = gfx->src_y;
14754     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14755     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14756     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14757     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14758     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14759     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14760     int id = i;
14761
14762     if (gfx->bitmap == NULL)
14763     {
14764       game_gadget[id] = NULL;
14765
14766       continue;
14767     }
14768
14769     if (id == GAME_CTRL_ID_STOP ||
14770         id == GAME_CTRL_ID_PAUSE ||
14771         id == GAME_CTRL_ID_PLAY ||
14772         id == GAME_CTRL_ID_SAVE ||
14773         id == GAME_CTRL_ID_LOAD)
14774     {
14775       button_type = GD_TYPE_NORMAL_BUTTON;
14776       checked = FALSE;
14777       event_mask = GD_EVENT_RELEASED;
14778     }
14779     else
14780     {
14781       button_type = GD_TYPE_CHECK_BUTTON;
14782       checked =
14783         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14784          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14785          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14786       event_mask = GD_EVENT_PRESSED;
14787     }
14788
14789     gi = CreateGadget(GDI_CUSTOM_ID, id,
14790                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14791                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14792                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14793                       GDI_WIDTH, gfx->width,
14794                       GDI_HEIGHT, gfx->height,
14795                       GDI_TYPE, button_type,
14796                       GDI_STATE, GD_BUTTON_UNPRESSED,
14797                       GDI_CHECKED, checked,
14798                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14799                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14800                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14801                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14802                       GDI_DIRECT_DRAW, FALSE,
14803                       GDI_EVENT_MASK, event_mask,
14804                       GDI_CALLBACK_ACTION, HandleGameButtons,
14805                       GDI_END);
14806
14807     if (gi == NULL)
14808       Error(ERR_EXIT, "cannot create gadget");
14809
14810     game_gadget[id] = gi;
14811   }
14812 }
14813
14814 void FreeGameButtons()
14815 {
14816   int i;
14817
14818   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14819     FreeGadget(game_gadget[i]);
14820 }
14821
14822 void MapGameButtons()
14823 {
14824   int i;
14825
14826   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14827     MapGadget(game_gadget[i]);
14828 }
14829
14830 void UnmapGameButtons()
14831 {
14832   int i;
14833
14834   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14835     UnmapGadget(game_gadget[i]);
14836 }
14837
14838 void RedrawGameButtons()
14839 {
14840   int i;
14841
14842   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14843     RedrawGadget(game_gadget[i]);
14844
14845   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14846   redraw_mask &= ~REDRAW_ALL;
14847 }
14848
14849 static void HandleGameButtonsExt(int id)
14850 {
14851   boolean handle_game_buttons =
14852     (game_status == GAME_MODE_PLAYING ||
14853      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
14854
14855   if (!handle_game_buttons)
14856     return;
14857
14858   switch (id)
14859   {
14860     case GAME_CTRL_ID_STOP:
14861       if (game_status == GAME_MODE_MAIN)
14862         break;
14863
14864       if (tape.playing)
14865         TapeStop();
14866       else
14867         RequestQuitGame(TRUE);
14868
14869       break;
14870
14871     case GAME_CTRL_ID_PAUSE:
14872       if (options.network && game_status == GAME_MODE_PLAYING)
14873       {
14874 #if defined(NETWORK_AVALIABLE)
14875         if (tape.pausing)
14876           SendToServer_ContinuePlaying();
14877         else
14878           SendToServer_PausePlaying();
14879 #endif
14880       }
14881       else
14882         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14883       break;
14884
14885     case GAME_CTRL_ID_PLAY:
14886       if (game_status == GAME_MODE_MAIN)
14887       {
14888         StartGameActions(options.network, setup.autorecord, level.random_seed);
14889       }
14890       else if (tape.pausing)
14891       {
14892 #if defined(NETWORK_AVALIABLE)
14893         if (options.network)
14894           SendToServer_ContinuePlaying();
14895         else
14896 #endif
14897         {
14898           tape.pausing = FALSE;
14899           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14900         }
14901       }
14902       break;
14903
14904     case SOUND_CTRL_ID_MUSIC:
14905       if (setup.sound_music)
14906       { 
14907         setup.sound_music = FALSE;
14908
14909         FadeMusic();
14910       }
14911       else if (audio.music_available)
14912       { 
14913         setup.sound = setup.sound_music = TRUE;
14914
14915         SetAudioMode(setup.sound);
14916
14917         PlayLevelMusic();
14918       }
14919       break;
14920
14921     case SOUND_CTRL_ID_LOOPS:
14922       if (setup.sound_loops)
14923         setup.sound_loops = FALSE;
14924       else if (audio.loops_available)
14925       {
14926         setup.sound = setup.sound_loops = TRUE;
14927
14928         SetAudioMode(setup.sound);
14929       }
14930       break;
14931
14932     case SOUND_CTRL_ID_SIMPLE:
14933       if (setup.sound_simple)
14934         setup.sound_simple = FALSE;
14935       else if (audio.sound_available)
14936       {
14937         setup.sound = setup.sound_simple = TRUE;
14938
14939         SetAudioMode(setup.sound);
14940       }
14941       break;
14942
14943     case GAME_CTRL_ID_SAVE:
14944       TapeQuickSave();
14945       break;
14946
14947     case GAME_CTRL_ID_LOAD:
14948       TapeQuickLoad();
14949       break;
14950
14951     default:
14952       break;
14953   }
14954 }
14955
14956 static void HandleGameButtons(struct GadgetInfo *gi)
14957 {
14958   HandleGameButtonsExt(gi->custom_id);
14959 }
14960
14961 void HandleSoundButtonKeys(Key key)
14962 {
14963
14964   if (key == setup.shortcut.sound_simple)
14965     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
14966   else if (key == setup.shortcut.sound_loops)
14967     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
14968   else if (key == setup.shortcut.sound_music)
14969     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
14970 }