improved handling mouse actions (for MM engine)
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP               0
1007 #define GAME_CTRL_ID_PAUSE              1
1008 #define GAME_CTRL_ID_PLAY               2
1009 #define GAME_CTRL_ID_UNDO               3
1010 #define GAME_CTRL_ID_REDO               4
1011 #define GAME_CTRL_ID_SAVE               5
1012 #define GAME_CTRL_ID_PAUSE2             6
1013 #define GAME_CTRL_ID_LOAD               7
1014 #define SOUND_CTRL_ID_MUSIC             8
1015 #define SOUND_CTRL_ID_LOOPS             9
1016 #define SOUND_CTRL_ID_SIMPLE            10
1017
1018 #define NUM_GAME_BUTTONS                11
1019
1020
1021 /* forward declaration for internal use */
1022
1023 static void CreateField(int, int, int);
1024
1025 static void ResetGfxAnimation(int, int);
1026
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1029
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1034
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1039
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1046
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1050
1051 static void HandleElementChange(int, int, int);
1052 static void ExecuteCustomElementAction(int, int, int, int);
1053 static boolean ChangeElement(int, int, int, int);
1054
1055 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1056 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1057         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1058 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1059         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1060 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1061         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1062 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1063         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1064
1065 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1066 #define CheckElementChange(x, y, e, te, ev)                             \
1067         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1068 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1069         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1070 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1071         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1072
1073 static void PlayLevelSound(int, int, int);
1074 static void PlayLevelSoundNearest(int, int, int);
1075 static void PlayLevelSoundAction(int, int, int);
1076 static void PlayLevelSoundElementAction(int, int, int, int);
1077 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1078 static void PlayLevelSoundActionIfLoop(int, int, int);
1079 static void StopLevelSoundActionIfLoop(int, int, int);
1080 static void PlayLevelMusic();
1081 static void FadeLevelSoundsAndMusic();
1082
1083 static void HandleGameButtons(struct GadgetInfo *);
1084
1085 int AmoebeNachbarNr(int, int);
1086 void AmoebeUmwandeln(int, int);
1087 void ContinueMoving(int, int);
1088 void Bang(int, int);
1089 void InitMovDir(int, int);
1090 void InitAmoebaNr(int, int);
1091 int NewHiScore(void);
1092
1093 void TestIfGoodThingHitsBadThing(int, int, int);
1094 void TestIfBadThingHitsGoodThing(int, int, int);
1095 void TestIfPlayerTouchesBadThing(int, int);
1096 void TestIfPlayerRunsIntoBadThing(int, int, int);
1097 void TestIfBadThingTouchesPlayer(int, int);
1098 void TestIfBadThingRunsIntoPlayer(int, int, int);
1099 void TestIfFriendTouchesBadThing(int, int);
1100 void TestIfBadThingTouchesFriend(int, int);
1101 void TestIfBadThingTouchesOtherBadThing(int, int);
1102 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1103
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1107
1108 static int getInvisibleActiveFromInvisibleElement(int);
1109 static int getInvisibleFromInvisibleActiveElement(int);
1110
1111 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1112
1113 /* for detection of endless loops, caused by custom element programming */
1114 /* (using maximal playfield width x 10 is just a rough approximation) */
1115 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1116
1117 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1118 {                                                                       \
1119   if (recursion_loop_detected)                                          \
1120     return (rc);                                                        \
1121                                                                         \
1122   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1123   {                                                                     \
1124     recursion_loop_detected = TRUE;                                     \
1125     recursion_loop_element = (e);                                       \
1126   }                                                                     \
1127                                                                         \
1128   recursion_loop_depth++;                                               \
1129 }
1130
1131 #define RECURSION_LOOP_DETECTION_END()                                  \
1132 {                                                                       \
1133   recursion_loop_depth--;                                               \
1134 }
1135
1136 static int recursion_loop_depth;
1137 static boolean recursion_loop_detected;
1138 static boolean recursion_loop_element;
1139
1140 static int map_player_action[MAX_PLAYERS];
1141
1142
1143 /* ------------------------------------------------------------------------- */
1144 /* definition of elements that automatically change to other elements after  */
1145 /* a specified time, eventually calling a function when changing             */
1146 /* ------------------------------------------------------------------------- */
1147
1148 /* forward declaration for changer functions */
1149 static void InitBuggyBase(int, int);
1150 static void WarnBuggyBase(int, int);
1151
1152 static void InitTrap(int, int);
1153 static void ActivateTrap(int, int);
1154 static void ChangeActiveTrap(int, int);
1155
1156 static void InitRobotWheel(int, int);
1157 static void RunRobotWheel(int, int);
1158 static void StopRobotWheel(int, int);
1159
1160 static void InitTimegateWheel(int, int);
1161 static void RunTimegateWheel(int, int);
1162
1163 static void InitMagicBallDelay(int, int);
1164 static void ActivateMagicBall(int, int);
1165
1166 struct ChangingElementInfo
1167 {
1168   int element;
1169   int target_element;
1170   int change_delay;
1171   void (*pre_change_function)(int x, int y);
1172   void (*change_function)(int x, int y);
1173   void (*post_change_function)(int x, int y);
1174 };
1175
1176 static struct ChangingElementInfo change_delay_list[] =
1177 {
1178   {
1179     EL_NUT_BREAKING,
1180     EL_EMERALD,
1181     6,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_PEARL_BREAKING,
1188     EL_EMPTY,
1189     8,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EXIT_OPENING,
1196     EL_EXIT_OPEN,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_CLOSING,
1204     EL_EXIT_CLOSED,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_STEEL_EXIT_OPENING,
1212     EL_STEEL_EXIT_OPEN,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_CLOSING,
1220     EL_STEEL_EXIT_CLOSED,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EM_EXIT_OPENING,
1228     EL_EM_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_CLOSING,
1236     EL_EMPTY,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_STEEL_EXIT_OPENING,
1244     EL_EM_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_CLOSING,
1252     EL_STEELWALL,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_SP_EXIT_OPENING,
1260     EL_SP_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_CLOSING,
1268     EL_SP_EXIT_CLOSED,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SWITCHGATE_OPENING,
1276     EL_SWITCHGATE_OPEN,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_CLOSING,
1284     EL_SWITCHGATE_CLOSED,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_TIMEGATE_OPENING,
1292     EL_TIMEGATE_OPEN,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_CLOSING,
1300     EL_TIMEGATE_CLOSED,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306
1307   {
1308     EL_ACID_SPLASH_LEFT,
1309     EL_EMPTY,
1310     8,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_ACID_SPLASH_RIGHT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_SP_BUGGY_BASE,
1325     EL_SP_BUGGY_BASE_ACTIVATING,
1326     0,
1327     InitBuggyBase,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE_ACTIVATING,
1333     EL_SP_BUGGY_BASE_ACTIVE,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVE,
1341     EL_SP_BUGGY_BASE,
1342     0,
1343     InitBuggyBase,
1344     WarnBuggyBase,
1345     NULL
1346   },
1347   {
1348     EL_TRAP,
1349     EL_TRAP_ACTIVE,
1350     0,
1351     InitTrap,
1352     NULL,
1353     ActivateTrap
1354   },
1355   {
1356     EL_TRAP_ACTIVE,
1357     EL_TRAP,
1358     31,
1359     NULL,
1360     ChangeActiveTrap,
1361     NULL
1362   },
1363   {
1364     EL_ROBOT_WHEEL_ACTIVE,
1365     EL_ROBOT_WHEEL,
1366     0,
1367     InitRobotWheel,
1368     RunRobotWheel,
1369     StopRobotWheel
1370   },
1371   {
1372     EL_TIMEGATE_SWITCH_ACTIVE,
1373     EL_TIMEGATE_SWITCH,
1374     0,
1375     InitTimegateWheel,
1376     RunTimegateWheel,
1377     NULL
1378   },
1379   {
1380     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381     EL_DC_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_EMC_MAGIC_BALL_ACTIVE,
1389     EL_EMC_MAGIC_BALL_ACTIVE,
1390     0,
1391     InitMagicBallDelay,
1392     NULL,
1393     ActivateMagicBall
1394   },
1395   {
1396     EL_EMC_SPRING_BUMPER_ACTIVE,
1397     EL_EMC_SPRING_BUMPER,
1398     8,
1399     NULL,
1400     NULL,
1401     NULL
1402   },
1403   {
1404     EL_DIAGONAL_SHRINKING,
1405     EL_UNDEFINED,
1406     0,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_GROWING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL,
1418   },
1419
1420   {
1421     EL_UNDEFINED,
1422     EL_UNDEFINED,
1423     -1,
1424     NULL,
1425     NULL,
1426     NULL
1427   }
1428 };
1429
1430 struct
1431 {
1432   int element;
1433   int push_delay_fixed, push_delay_random;
1434 }
1435 push_delay_list[] =
1436 {
1437   { EL_SPRING,                  0, 0 },
1438   { EL_BALLOON,                 0, 0 },
1439
1440   { EL_SOKOBAN_OBJECT,          2, 0 },
1441   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1442   { EL_SATELLITE,               2, 0 },
1443   { EL_SP_DISK_YELLOW,          2, 0 },
1444
1445   { EL_UNDEFINED,               0, 0 },
1446 };
1447
1448 struct
1449 {
1450   int element;
1451   int move_stepsize;
1452 }
1453 move_stepsize_list[] =
1454 {
1455   { EL_AMOEBA_DROP,             2 },
1456   { EL_AMOEBA_DROPPING,         2 },
1457   { EL_QUICKSAND_FILLING,       1 },
1458   { EL_QUICKSAND_EMPTYING,      1 },
1459   { EL_QUICKSAND_FAST_FILLING,  2 },
1460   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461   { EL_MAGIC_WALL_FILLING,      2 },
1462   { EL_MAGIC_WALL_EMPTYING,     2 },
1463   { EL_BD_MAGIC_WALL_FILLING,   2 },
1464   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1465   { EL_DC_MAGIC_WALL_FILLING,   2 },
1466   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1467
1468   { EL_UNDEFINED,               0 },
1469 };
1470
1471 struct
1472 {
1473   int element;
1474   int count;
1475 }
1476 collect_count_list[] =
1477 {
1478   { EL_EMERALD,                 1 },
1479   { EL_BD_DIAMOND,              1 },
1480   { EL_EMERALD_YELLOW,          1 },
1481   { EL_EMERALD_RED,             1 },
1482   { EL_EMERALD_PURPLE,          1 },
1483   { EL_DIAMOND,                 3 },
1484   { EL_SP_INFOTRON,             1 },
1485   { EL_PEARL,                   5 },
1486   { EL_CRYSTAL,                 8 },
1487
1488   { EL_UNDEFINED,               0 },
1489 };
1490
1491 struct
1492 {
1493   int element;
1494   int direction;
1495 }
1496 access_direction_list[] =
1497 {
1498   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1500   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1501   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1504   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1505   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1506   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1507   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1508   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1509
1510   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1511   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1512   { EL_SP_PORT_UP,                                                   MV_DOWN },
1513   { EL_SP_PORT_DOWN,                                         MV_UP           },
1514   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1515   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1516   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1518   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1519   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1521   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1522   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1523   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1524   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1525   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1526   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1527   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1528   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1529
1530   { EL_UNDEFINED,                       MV_NONE                              }
1531 };
1532
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1534
1535 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1538                                  IS_JUST_CHANGING(x, y))
1539
1540 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1541
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1547
1548 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1549                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1550                                      (y) += playfield_scan_delta_y)     \
1551                                 for ((x) = playfield_scan_start_x;      \
1552                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1553                                      (x) += playfield_scan_delta_x)
1554
1555 #ifdef DEBUG
1556 void DEBUG_SetMaximumDynamite()
1557 {
1558   int i;
1559
1560   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562       local_player->inventory_element[local_player->inventory_size++] =
1563         EL_DYNAMITE;
1564 }
1565 #endif
1566
1567 static void InitPlayfieldScanModeVars()
1568 {
1569   if (game.use_reverse_scan_direction)
1570   {
1571     playfield_scan_start_x = lev_fieldx - 1;
1572     playfield_scan_start_y = lev_fieldy - 1;
1573
1574     playfield_scan_delta_x = -1;
1575     playfield_scan_delta_y = -1;
1576   }
1577   else
1578   {
1579     playfield_scan_start_x = 0;
1580     playfield_scan_start_y = 0;
1581
1582     playfield_scan_delta_x = 1;
1583     playfield_scan_delta_y = 1;
1584   }
1585 }
1586
1587 static void InitPlayfieldScanMode(int mode)
1588 {
1589   game.use_reverse_scan_direction =
1590     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1591
1592   InitPlayfieldScanModeVars();
1593 }
1594
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1596 {
1597   move_stepsize =
1598     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1599
1600   /* make sure that stepsize value is always a power of 2 */
1601   move_stepsize = (1 << log_2(move_stepsize));
1602
1603   return TILEX / move_stepsize;
1604 }
1605
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1607                                boolean init_game)
1608 {
1609   int player_nr = player->index_nr;
1610   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1612
1613   /* do no immediately change move delay -- the player might just be moving */
1614   player->move_delay_value_next = move_delay;
1615
1616   /* information if player can move must be set separately */
1617   player->cannot_move = cannot_move;
1618
1619   if (init_game)
1620   {
1621     player->move_delay       = game.initial_move_delay[player_nr];
1622     player->move_delay_value = game.initial_move_delay_value[player_nr];
1623
1624     player->move_delay_value_next = -1;
1625
1626     player->move_delay_reset_counter = 0;
1627   }
1628 }
1629
1630 void GetPlayerConfig()
1631 {
1632   GameFrameDelay = setup.game_frame_delay;
1633
1634   if (!audio.sound_available)
1635     setup.sound_simple = FALSE;
1636
1637   if (!audio.loops_available)
1638     setup.sound_loops = FALSE;
1639
1640   if (!audio.music_available)
1641     setup.sound_music = FALSE;
1642
1643   if (!video.fullscreen_available)
1644     setup.fullscreen = FALSE;
1645
1646   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1647
1648   SetAudioMode(setup.sound);
1649 }
1650
1651 int GetElementFromGroupElement(int element)
1652 {
1653   if (IS_GROUP_ELEMENT(element))
1654   {
1655     struct ElementGroupInfo *group = element_info[element].group;
1656     int last_anim_random_frame = gfx.anim_random_frame;
1657     int element_pos;
1658
1659     if (group->choice_mode == ANIM_RANDOM)
1660       gfx.anim_random_frame = RND(group->num_elements_resolved);
1661
1662     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1663                                     group->choice_mode, 0,
1664                                     group->choice_pos);
1665
1666     if (group->choice_mode == ANIM_RANDOM)
1667       gfx.anim_random_frame = last_anim_random_frame;
1668
1669     group->choice_pos++;
1670
1671     element = group->element_resolved[element_pos];
1672   }
1673
1674   return element;
1675 }
1676
1677 static void InitPlayerField(int x, int y, int element, boolean init_game)
1678 {
1679   if (element == EL_SP_MURPHY)
1680   {
1681     if (init_game)
1682     {
1683       if (stored_player[0].present)
1684       {
1685         Feld[x][y] = EL_SP_MURPHY_CLONE;
1686
1687         return;
1688       }
1689       else
1690       {
1691         stored_player[0].initial_element = element;
1692         stored_player[0].use_murphy = TRUE;
1693
1694         if (!level.use_artwork_element[0])
1695           stored_player[0].artwork_element = EL_SP_MURPHY;
1696       }
1697
1698       Feld[x][y] = EL_PLAYER_1;
1699     }
1700   }
1701
1702   if (init_game)
1703   {
1704     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705     int jx = player->jx, jy = player->jy;
1706
1707     player->present = TRUE;
1708
1709     player->block_last_field = (element == EL_SP_MURPHY ?
1710                                 level.sp_block_last_field :
1711                                 level.block_last_field);
1712
1713     /* ---------- initialize player's last field block delay --------------- */
1714
1715     /* always start with reliable default value (no adjustment needed) */
1716     player->block_delay_adjustment = 0;
1717
1718     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719     if (player->block_last_field && element == EL_SP_MURPHY)
1720       player->block_delay_adjustment = 1;
1721
1722     /* special case 2: in game engines before 3.1.1, blocking was different */
1723     if (game.use_block_last_field_bug)
1724       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1725
1726     if (!options.network || player->connected)
1727     {
1728       player->active = TRUE;
1729
1730       /* remove potentially duplicate players */
1731       if (StorePlayer[jx][jy] == Feld[x][y])
1732         StorePlayer[jx][jy] = 0;
1733
1734       StorePlayer[x][y] = Feld[x][y];
1735
1736 #if DEBUG_INIT_PLAYER
1737       if (options.debug)
1738       {
1739         printf("- player element %d activated", player->element_nr);
1740         printf(" (local player is %d and currently %s)\n",
1741                local_player->element_nr,
1742                local_player->active ? "active" : "not active");
1743       }
1744     }
1745 #endif
1746
1747     Feld[x][y] = EL_EMPTY;
1748
1749     player->jx = player->last_jx = x;
1750     player->jy = player->last_jy = y;
1751   }
1752
1753   if (!init_game)
1754   {
1755     int player_nr = GET_PLAYER_NR(element);
1756     struct PlayerInfo *player = &stored_player[player_nr];
1757
1758     if (player->active && player->killed)
1759       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1760   }
1761 }
1762
1763 static void InitField(int x, int y, boolean init_game)
1764 {
1765   int element = Feld[x][y];
1766
1767   switch (element)
1768   {
1769     case EL_SP_MURPHY:
1770     case EL_PLAYER_1:
1771     case EL_PLAYER_2:
1772     case EL_PLAYER_3:
1773     case EL_PLAYER_4:
1774       InitPlayerField(x, y, element, init_game);
1775       break;
1776
1777     case EL_SOKOBAN_FIELD_PLAYER:
1778       element = Feld[x][y] = EL_PLAYER_1;
1779       InitField(x, y, init_game);
1780
1781       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1782       InitField(x, y, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_EMPTY:
1786       local_player->sokobanfields_still_needed++;
1787       break;
1788
1789     case EL_STONEBLOCK:
1790       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1791         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1792       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1793         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1794       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1795         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1798       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1799         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1800       break;
1801
1802     case EL_BUG:
1803     case EL_BUG_RIGHT:
1804     case EL_BUG_UP:
1805     case EL_BUG_LEFT:
1806     case EL_BUG_DOWN:
1807     case EL_SPACESHIP:
1808     case EL_SPACESHIP_RIGHT:
1809     case EL_SPACESHIP_UP:
1810     case EL_SPACESHIP_LEFT:
1811     case EL_SPACESHIP_DOWN:
1812     case EL_BD_BUTTERFLY:
1813     case EL_BD_BUTTERFLY_RIGHT:
1814     case EL_BD_BUTTERFLY_UP:
1815     case EL_BD_BUTTERFLY_LEFT:
1816     case EL_BD_BUTTERFLY_DOWN:
1817     case EL_BD_FIREFLY:
1818     case EL_BD_FIREFLY_RIGHT:
1819     case EL_BD_FIREFLY_UP:
1820     case EL_BD_FIREFLY_LEFT:
1821     case EL_BD_FIREFLY_DOWN:
1822     case EL_PACMAN_RIGHT:
1823     case EL_PACMAN_UP:
1824     case EL_PACMAN_LEFT:
1825     case EL_PACMAN_DOWN:
1826     case EL_YAMYAM:
1827     case EL_YAMYAM_LEFT:
1828     case EL_YAMYAM_RIGHT:
1829     case EL_YAMYAM_UP:
1830     case EL_YAMYAM_DOWN:
1831     case EL_DARK_YAMYAM:
1832     case EL_ROBOT:
1833     case EL_PACMAN:
1834     case EL_SP_SNIKSNAK:
1835     case EL_SP_ELECTRON:
1836     case EL_MOLE:
1837     case EL_MOLE_LEFT:
1838     case EL_MOLE_RIGHT:
1839     case EL_MOLE_UP:
1840     case EL_MOLE_DOWN:
1841       InitMovDir(x, y);
1842       break;
1843
1844     case EL_AMOEBA_FULL:
1845     case EL_BD_AMOEBA:
1846       InitAmoebaNr(x, y);
1847       break;
1848
1849     case EL_AMOEBA_DROP:
1850       if (y == lev_fieldy - 1)
1851       {
1852         Feld[x][y] = EL_AMOEBA_GROWING;
1853         Store[x][y] = EL_AMOEBA_WET;
1854       }
1855       break;
1856
1857     case EL_DYNAMITE_ACTIVE:
1858     case EL_SP_DISK_RED_ACTIVE:
1859     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1860     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1861     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1862     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1863       MovDelay[x][y] = 96;
1864       break;
1865
1866     case EL_EM_DYNAMITE_ACTIVE:
1867       MovDelay[x][y] = 32;
1868       break;
1869
1870     case EL_LAMP:
1871       local_player->lights_still_needed++;
1872       break;
1873
1874     case EL_PENGUIN:
1875       local_player->friends_still_needed++;
1876       break;
1877
1878     case EL_PIG:
1879     case EL_DRAGON:
1880       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1881       break;
1882
1883     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1884     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1885     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1886     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1887     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1888     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1889     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1890     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1891     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1892     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1895       if (init_game)
1896       {
1897         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1898         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1899         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1900
1901         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1902         {
1903           game.belt_dir[belt_nr] = belt_dir;
1904           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1905         }
1906         else    /* more than one switch -- set it like the first switch */
1907         {
1908           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1909         }
1910       }
1911       break;
1912
1913     case EL_LIGHT_SWITCH_ACTIVE:
1914       if (init_game)
1915         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1916       break;
1917
1918     case EL_INVISIBLE_STEELWALL:
1919     case EL_INVISIBLE_WALL:
1920     case EL_INVISIBLE_SAND:
1921       if (game.light_time_left > 0 ||
1922           game.lenses_time_left > 0)
1923         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1924       break;
1925
1926     case EL_EMC_MAGIC_BALL:
1927       if (game.ball_state)
1928         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1929       break;
1930
1931     case EL_EMC_MAGIC_BALL_SWITCH:
1932       if (game.ball_state)
1933         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1934       break;
1935
1936     case EL_TRIGGER_PLAYER:
1937     case EL_TRIGGER_ELEMENT:
1938     case EL_TRIGGER_CE_VALUE:
1939     case EL_TRIGGER_CE_SCORE:
1940     case EL_SELF:
1941     case EL_ANY_ELEMENT:
1942     case EL_CURRENT_CE_VALUE:
1943     case EL_CURRENT_CE_SCORE:
1944     case EL_PREV_CE_1:
1945     case EL_PREV_CE_2:
1946     case EL_PREV_CE_3:
1947     case EL_PREV_CE_4:
1948     case EL_PREV_CE_5:
1949     case EL_PREV_CE_6:
1950     case EL_PREV_CE_7:
1951     case EL_PREV_CE_8:
1952     case EL_NEXT_CE_1:
1953     case EL_NEXT_CE_2:
1954     case EL_NEXT_CE_3:
1955     case EL_NEXT_CE_4:
1956     case EL_NEXT_CE_5:
1957     case EL_NEXT_CE_6:
1958     case EL_NEXT_CE_7:
1959     case EL_NEXT_CE_8:
1960       /* reference elements should not be used on the playfield */
1961       Feld[x][y] = EL_EMPTY;
1962       break;
1963
1964     default:
1965       if (IS_CUSTOM_ELEMENT(element))
1966       {
1967         if (CAN_MOVE(element))
1968           InitMovDir(x, y);
1969
1970         if (!element_info[element].use_last_ce_value || init_game)
1971           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1972       }
1973       else if (IS_GROUP_ELEMENT(element))
1974       {
1975         Feld[x][y] = GetElementFromGroupElement(element);
1976
1977         InitField(x, y, init_game);
1978       }
1979
1980       break;
1981   }
1982
1983   if (!init_game)
1984     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1985 }
1986
1987 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1988 {
1989   InitField(x, y, init_game);
1990
1991   /* not needed to call InitMovDir() -- already done by InitField()! */
1992   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1993       CAN_MOVE(Feld[x][y]))
1994     InitMovDir(x, y);
1995 }
1996
1997 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1998 {
1999   int old_element = Feld[x][y];
2000
2001   InitField(x, y, init_game);
2002
2003   /* not needed to call InitMovDir() -- already done by InitField()! */
2004   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2005       CAN_MOVE(old_element) &&
2006       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2007     InitMovDir(x, y);
2008
2009   /* this case is in fact a combination of not less than three bugs:
2010      first, it calls InitMovDir() for elements that can move, although this is
2011      already done by InitField(); then, it checks the element that was at this
2012      field _before_ the call to InitField() (which can change it); lastly, it
2013      was not called for "mole with direction" elements, which were treated as
2014      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2015   */
2016 }
2017
2018 static int get_key_element_from_nr(int key_nr)
2019 {
2020   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2021                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2022                           EL_EM_KEY_1 : EL_KEY_1);
2023
2024   return key_base_element + key_nr;
2025 }
2026
2027 static int get_next_dropped_element(struct PlayerInfo *player)
2028 {
2029   return (player->inventory_size > 0 ?
2030           player->inventory_element[player->inventory_size - 1] :
2031           player->inventory_infinite_element != EL_UNDEFINED ?
2032           player->inventory_infinite_element :
2033           player->dynabombs_left > 0 ?
2034           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2035           EL_UNDEFINED);
2036 }
2037
2038 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2039 {
2040   /* pos >= 0: get element from bottom of the stack;
2041      pos <  0: get element from top of the stack */
2042
2043   if (pos < 0)
2044   {
2045     int min_inventory_size = -pos;
2046     int inventory_pos = player->inventory_size - min_inventory_size;
2047     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2048
2049     return (player->inventory_size >= min_inventory_size ?
2050             player->inventory_element[inventory_pos] :
2051             player->inventory_infinite_element != EL_UNDEFINED ?
2052             player->inventory_infinite_element :
2053             player->dynabombs_left >= min_dynabombs_left ?
2054             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2055             EL_UNDEFINED);
2056   }
2057   else
2058   {
2059     int min_dynabombs_left = pos + 1;
2060     int min_inventory_size = pos + 1 - player->dynabombs_left;
2061     int inventory_pos = pos - player->dynabombs_left;
2062
2063     return (player->inventory_infinite_element != EL_UNDEFINED ?
2064             player->inventory_infinite_element :
2065             player->dynabombs_left >= min_dynabombs_left ?
2066             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067             player->inventory_size >= min_inventory_size ?
2068             player->inventory_element[inventory_pos] :
2069             EL_UNDEFINED);
2070   }
2071 }
2072
2073 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2074 {
2075   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2076   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2077   int compare_result;
2078
2079   if (gpo1->sort_priority != gpo2->sort_priority)
2080     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2081   else
2082     compare_result = gpo1->nr - gpo2->nr;
2083
2084   return compare_result;
2085 }
2086
2087 int getPlayerInventorySize(int player_nr)
2088 {
2089   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2090     return level.native_em_level->ply[player_nr]->dynamite;
2091   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2092     return level.native_sp_level->game_sp->red_disk_count;
2093   else
2094     return stored_player[player_nr].inventory_size;
2095 }
2096
2097 void InitGameControlValues()
2098 {
2099   int i;
2100
2101   for (i = 0; game_panel_controls[i].nr != -1; i++)
2102   {
2103     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2104     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2105     struct TextPosInfo *pos = gpc->pos;
2106     int nr = gpc->nr;
2107     int type = gpc->type;
2108
2109     if (nr != i)
2110     {
2111       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2112       Error(ERR_EXIT, "this should not happen -- please debug");
2113     }
2114
2115     /* force update of game controls after initialization */
2116     gpc->value = gpc->last_value = -1;
2117     gpc->frame = gpc->last_frame = -1;
2118     gpc->gfx_frame = -1;
2119
2120     /* determine panel value width for later calculation of alignment */
2121     if (type == TYPE_INTEGER || type == TYPE_STRING)
2122     {
2123       pos->width = pos->size * getFontWidth(pos->font);
2124       pos->height = getFontHeight(pos->font);
2125     }
2126     else if (type == TYPE_ELEMENT)
2127     {
2128       pos->width = pos->size;
2129       pos->height = pos->size;
2130     }
2131
2132     /* fill structure for game panel draw order */
2133     gpo->nr = gpc->nr;
2134     gpo->sort_priority = pos->sort_priority;
2135   }
2136
2137   /* sort game panel controls according to sort_priority and control number */
2138   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2139         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2140 }
2141
2142 void UpdatePlayfieldElementCount()
2143 {
2144   boolean use_element_count = FALSE;
2145   int i, j, x, y;
2146
2147   /* first check if it is needed at all to calculate playfield element count */
2148   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2149     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2150       use_element_count = TRUE;
2151
2152   if (!use_element_count)
2153     return;
2154
2155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2156     element_info[i].element_count = 0;
2157
2158   SCAN_PLAYFIELD(x, y)
2159   {
2160     element_info[Feld[x][y]].element_count++;
2161   }
2162
2163   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2164     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2165       if (IS_IN_GROUP(j, i))
2166         element_info[EL_GROUP_START + i].element_count +=
2167           element_info[j].element_count;
2168 }
2169
2170 void UpdateGameControlValues()
2171 {
2172   int i, k;
2173   int time = (local_player->LevelSolved ?
2174               local_player->LevelSolved_CountingTime :
2175               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2176               level.native_em_level->lev->time :
2177               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2178               level.native_sp_level->game_sp->time_played :
2179               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2180               game_mm.energy_left :
2181               game.no_time_limit ? TimePlayed : TimeLeft);
2182   int score = (local_player->LevelSolved ?
2183                local_player->LevelSolved_CountingScore :
2184                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185                level.native_em_level->lev->score :
2186                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187                level.native_sp_level->game_sp->score :
2188                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189                game_mm.score :
2190                local_player->score);
2191   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2192               level.native_em_level->lev->required :
2193               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2194               level.native_sp_level->game_sp->infotrons_still_needed :
2195               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2196               game_mm.kettles_still_needed :
2197               local_player->gems_still_needed);
2198   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199                      level.native_em_level->lev->required > 0 :
2200                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2202                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2203                      game_mm.kettles_still_needed > 0 ||
2204                      game_mm.lights_still_needed > 0 :
2205                      local_player->gems_still_needed > 0 ||
2206                      local_player->sokobanfields_still_needed > 0 ||
2207                      local_player->lights_still_needed > 0);
2208   int health = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2209                 MIN(MAX(0, 100 - game_mm.laser_overload_value), 100) : 100);
2210
2211   UpdatePlayfieldElementCount();
2212
2213   /* update game panel control values */
2214
2215   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2217
2218   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219   for (i = 0; i < MAX_NUM_KEYS; i++)
2220     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2223
2224   if (game.centered_player_nr == -1)
2225   {
2226     for (i = 0; i < MAX_PLAYERS; i++)
2227     {
2228       /* only one player in Supaplex game engine */
2229       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2230         break;
2231
2232       for (k = 0; k < MAX_NUM_KEYS; k++)
2233       {
2234         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2235         {
2236           if (level.native_em_level->ply[i]->keys & (1 << k))
2237             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238               get_key_element_from_nr(k);
2239         }
2240         else if (stored_player[i].key[k])
2241           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242             get_key_element_from_nr(k);
2243       }
2244
2245       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2246         getPlayerInventorySize(i);
2247
2248       if (stored_player[i].num_white_keys > 0)
2249         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2250           EL_DC_KEY_WHITE;
2251
2252       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2253         stored_player[i].num_white_keys;
2254     }
2255   }
2256   else
2257   {
2258     int player_nr = game.centered_player_nr;
2259
2260     for (k = 0; k < MAX_NUM_KEYS; k++)
2261     {
2262       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2263       {
2264         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268       else if (stored_player[player_nr].key[k])
2269         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2270           get_key_element_from_nr(k);
2271     }
2272
2273     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274       getPlayerInventorySize(player_nr);
2275
2276     if (stored_player[player_nr].num_white_keys > 0)
2277       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2278
2279     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2280       stored_player[player_nr].num_white_keys;
2281   }
2282
2283   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2284   {
2285     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2286       get_inventory_element_from_pos(local_player, i);
2287     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2288       get_inventory_element_from_pos(local_player, -i - 1);
2289   }
2290
2291   game_panel_controls[GAME_PANEL_SCORE].value = score;
2292   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2293
2294   game_panel_controls[GAME_PANEL_TIME].value = time;
2295
2296   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2297   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2298   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2299
2300   if (game.no_time_limit)
2301     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2302   else
2303     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2304
2305   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2306   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2307
2308   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2309
2310   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2311     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2312      EL_EMPTY);
2313   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2314     local_player->shield_normal_time_left;
2315   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2316     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2317      EL_EMPTY);
2318   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2319     local_player->shield_deadly_time_left;
2320
2321   game_panel_controls[GAME_PANEL_EXIT].value =
2322     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2323
2324   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2325     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2326   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2327     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2328      EL_EMC_MAGIC_BALL_SWITCH);
2329
2330   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2331     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2332   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2333     game.light_time_left;
2334
2335   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2336     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2337   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2338     game.timegate_time_left;
2339
2340   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2341     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2342
2343   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2344     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2345   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2346     game.lenses_time_left;
2347
2348   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2349     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2350   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2351     game.magnify_time_left;
2352
2353   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2354     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2355      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2356      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2357      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2358      EL_BALLOON_SWITCH_NONE);
2359
2360   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2361     local_player->dynabomb_count;
2362   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2363     local_player->dynabomb_size;
2364   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2365     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2366
2367   game_panel_controls[GAME_PANEL_PENGUINS].value =
2368     local_player->friends_still_needed;
2369
2370   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2371     local_player->sokobanfields_still_needed;
2372   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2373     local_player->sokobanfields_still_needed;
2374
2375   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2376     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2377
2378   for (i = 0; i < NUM_BELTS; i++)
2379   {
2380     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2381       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2382        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2383     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2384       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2385   }
2386
2387   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2388     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2389   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2390     game.magic_wall_time_left;
2391
2392   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2393     local_player->gravity;
2394
2395   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2396     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2397
2398   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2399     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2400       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2401        game.panel.element[i].id : EL_UNDEFINED);
2402
2403   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2405       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2406        element_info[game.panel.element_count[i].id].element_count : 0);
2407
2408   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2409     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2410       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2411        element_info[game.panel.ce_score[i].id].collect_score : 0);
2412
2413   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2415       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2416        element_info[game.panel.ce_score_element[i].id].collect_score :
2417        EL_UNDEFINED);
2418
2419   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2420   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2421   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2422
2423   /* update game panel control frames */
2424
2425   for (i = 0; game_panel_controls[i].nr != -1; i++)
2426   {
2427     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2428
2429     if (gpc->type == TYPE_ELEMENT)
2430     {
2431       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2432       {
2433         int last_anim_random_frame = gfx.anim_random_frame;
2434         int element = gpc->value;
2435         int graphic = el2panelimg(element);
2436
2437         if (gpc->value != gpc->last_value)
2438         {
2439           gpc->gfx_frame = 0;
2440           gpc->gfx_random = INIT_GFX_RANDOM();
2441         }
2442         else
2443         {
2444           gpc->gfx_frame++;
2445
2446           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2447               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2448             gpc->gfx_random = INIT_GFX_RANDOM();
2449         }
2450
2451         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2452           gfx.anim_random_frame = gpc->gfx_random;
2453
2454         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2455           gpc->gfx_frame = element_info[element].collect_score;
2456
2457         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2458                                               gpc->gfx_frame);
2459
2460         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2461           gfx.anim_random_frame = last_anim_random_frame;
2462       }
2463     }
2464     else if (gpc->type == TYPE_GRAPHIC)
2465     {
2466       if (gpc->graphic != IMG_UNDEFINED)
2467       {
2468         int last_anim_random_frame = gfx.anim_random_frame;
2469         int graphic = gpc->graphic;
2470
2471         if (gpc->value != gpc->last_value)
2472         {
2473           gpc->gfx_frame = 0;
2474           gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476         else
2477         {
2478           gpc->gfx_frame++;
2479
2480           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2481               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2482             gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484
2485         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2486           gfx.anim_random_frame = gpc->gfx_random;
2487
2488         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2489
2490         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491           gfx.anim_random_frame = last_anim_random_frame;
2492       }
2493     }
2494   }
2495 }
2496
2497 void DisplayGameControlValues()
2498 {
2499   boolean redraw_panel = FALSE;
2500   int i;
2501
2502   for (i = 0; game_panel_controls[i].nr != -1; i++)
2503   {
2504     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2505
2506     if (PANEL_DEACTIVATED(gpc->pos))
2507       continue;
2508
2509     if (gpc->value == gpc->last_value &&
2510         gpc->frame == gpc->last_frame)
2511       continue;
2512
2513     redraw_panel = TRUE;
2514   }
2515
2516   if (!redraw_panel)
2517     return;
2518
2519   /* copy default game door content to main double buffer */
2520
2521   /* !!! CHECK AGAIN !!! */
2522   SetPanelBackground();
2523   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2524   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2525
2526   /* redraw game control buttons */
2527   RedrawGameButtons();
2528
2529   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2530
2531   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2532   {
2533     int nr = game_panel_order[i].nr;
2534     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2535     struct TextPosInfo *pos = gpc->pos;
2536     int type = gpc->type;
2537     int value = gpc->value;
2538     int frame = gpc->frame;
2539     int size = pos->size;
2540     int font = pos->font;
2541     boolean draw_masked = pos->draw_masked;
2542     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2543
2544     if (PANEL_DEACTIVATED(pos))
2545       continue;
2546
2547     gpc->last_value = value;
2548     gpc->last_frame = frame;
2549
2550     if (type == TYPE_INTEGER)
2551     {
2552       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2553           nr == GAME_PANEL_TIME)
2554       {
2555         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2556
2557         if (use_dynamic_size)           /* use dynamic number of digits */
2558         {
2559           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2560           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2561           int size2 = size1 + 1;
2562           int font1 = pos->font;
2563           int font2 = pos->font_alt;
2564
2565           size = (value < value_change ? size1 : size2);
2566           font = (value < value_change ? font1 : font2);
2567         }
2568       }
2569
2570       /* correct text size if "digits" is zero or less */
2571       if (size <= 0)
2572         size = strlen(int2str(value, size));
2573
2574       /* dynamically correct text alignment */
2575       pos->width = size * getFontWidth(font);
2576
2577       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2578                   int2str(value, size), font, mask_mode);
2579     }
2580     else if (type == TYPE_ELEMENT)
2581     {
2582       int element, graphic;
2583       Bitmap *src_bitmap;
2584       int src_x, src_y;
2585       int width, height;
2586       int dst_x = PANEL_XPOS(pos);
2587       int dst_y = PANEL_YPOS(pos);
2588
2589       if (value != EL_UNDEFINED && value != EL_EMPTY)
2590       {
2591         element = value;
2592         graphic = el2panelimg(value);
2593
2594         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2595
2596         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2597           size = TILESIZE;
2598
2599         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2600                               &src_x, &src_y);
2601
2602         width  = graphic_info[graphic].width  * size / TILESIZE;
2603         height = graphic_info[graphic].height * size / TILESIZE;
2604
2605         if (draw_masked)
2606           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2607                            dst_x, dst_y);
2608         else
2609           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2610                      dst_x, dst_y);
2611       }
2612     }
2613     else if (type == TYPE_GRAPHIC)
2614     {
2615       int graphic        = gpc->graphic;
2616       int graphic_active = gpc->graphic_active;
2617       Bitmap *src_bitmap;
2618       int src_x, src_y;
2619       int width, height;
2620       int dst_x = PANEL_XPOS(pos);
2621       int dst_y = PANEL_YPOS(pos);
2622       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2623                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2624
2625       if (graphic != IMG_UNDEFINED && !skip)
2626       {
2627         if (pos->style == STYLE_REVERSE)
2628           value = 100 - value;
2629
2630         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2631
2632         if (pos->direction & MV_HORIZONTAL)
2633         {
2634           width  = graphic_info[graphic_active].width * value / 100;
2635           height = graphic_info[graphic_active].height;
2636
2637           if (pos->direction == MV_LEFT)
2638           {
2639             src_x += graphic_info[graphic_active].width - width;
2640             dst_x += graphic_info[graphic_active].width - width;
2641           }
2642         }
2643         else
2644         {
2645           width  = graphic_info[graphic_active].width;
2646           height = graphic_info[graphic_active].height * value / 100;
2647
2648           if (pos->direction == MV_UP)
2649           {
2650             src_y += graphic_info[graphic_active].height - height;
2651             dst_y += graphic_info[graphic_active].height - height;
2652           }
2653         }
2654
2655         if (draw_masked)
2656           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2657                            dst_x, dst_y);
2658         else
2659           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2660                      dst_x, dst_y);
2661
2662         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2663
2664         if (pos->direction & MV_HORIZONTAL)
2665         {
2666           if (pos->direction == MV_RIGHT)
2667           {
2668             src_x += width;
2669             dst_x += width;
2670           }
2671           else
2672           {
2673             dst_x = PANEL_XPOS(pos);
2674           }
2675
2676           width = graphic_info[graphic].width - width;
2677         }
2678         else
2679         {
2680           if (pos->direction == MV_DOWN)
2681           {
2682             src_y += height;
2683             dst_y += height;
2684           }
2685           else
2686           {
2687             dst_y = PANEL_YPOS(pos);
2688           }
2689
2690           height = graphic_info[graphic].height - height;
2691         }
2692
2693         if (draw_masked)
2694           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2695                            dst_x, dst_y);
2696         else
2697           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2698                      dst_x, dst_y);
2699       }
2700     }
2701     else if (type == TYPE_STRING)
2702     {
2703       boolean active = (value != 0);
2704       char *state_normal = "off";
2705       char *state_active = "on";
2706       char *state = (active ? state_active : state_normal);
2707       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2708                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2709                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2710                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2711
2712       if (nr == GAME_PANEL_GRAVITY_STATE)
2713       {
2714         int font1 = pos->font;          /* (used for normal state) */
2715         int font2 = pos->font_alt;      /* (used for active state) */
2716
2717         font = (active ? font2 : font1);
2718       }
2719
2720       if (s != NULL)
2721       {
2722         char *s_cut;
2723
2724         if (size <= 0)
2725         {
2726           /* don't truncate output if "chars" is zero or less */
2727           size = strlen(s);
2728
2729           /* dynamically correct text alignment */
2730           pos->width = size * getFontWidth(font);
2731         }
2732
2733         s_cut = getStringCopyN(s, size);
2734
2735         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736                     s_cut, font, mask_mode);
2737
2738         free(s_cut);
2739       }
2740     }
2741
2742     redraw_mask |= REDRAW_DOOR_1;
2743   }
2744
2745   SetGameStatus(GAME_MODE_PLAYING);
2746 }
2747
2748 void UpdateAndDisplayGameControlValues()
2749 {
2750   if (tape.deactivate_display)
2751     return;
2752
2753   UpdateGameControlValues();
2754   DisplayGameControlValues();
2755 }
2756
2757 void UpdateGameDoorValues()
2758 {
2759   UpdateGameControlValues();
2760 }
2761
2762 void DrawGameDoorValues()
2763 {
2764   DisplayGameControlValues();
2765 }
2766
2767
2768 /*
2769   =============================================================================
2770   InitGameEngine()
2771   -----------------------------------------------------------------------------
2772   initialize game engine due to level / tape version number
2773   =============================================================================
2774 */
2775
2776 static void InitGameEngine()
2777 {
2778   int i, j, k, l, x, y;
2779
2780   /* set game engine from tape file when re-playing, else from level file */
2781   game.engine_version = (tape.playing ? tape.engine_version :
2782                          level.game_version);
2783
2784   /* set single or multi-player game mode (needed for re-playing tapes) */
2785   game.team_mode = setup.team_mode;
2786
2787   if (tape.playing)
2788   {
2789     int num_players = 0;
2790
2791     for (i = 0; i < MAX_PLAYERS; i++)
2792       if (tape.player_participates[i])
2793         num_players++;
2794
2795     /* multi-player tapes contain input data for more than one player */
2796     game.team_mode = (num_players > 1);
2797   }
2798
2799   /* ---------------------------------------------------------------------- */
2800   /* set flags for bugs and changes according to active game engine version */
2801   /* ---------------------------------------------------------------------- */
2802
2803   /*
2804     Summary of bugfix/change:
2805     Fixed handling for custom elements that change when pushed by the player.
2806
2807     Fixed/changed in version:
2808     3.1.0
2809
2810     Description:
2811     Before 3.1.0, custom elements that "change when pushing" changed directly
2812     after the player started pushing them (until then handled in "DigField()").
2813     Since 3.1.0, these custom elements are not changed until the "pushing"
2814     move of the element is finished (now handled in "ContinueMoving()").
2815
2816     Affected levels/tapes:
2817     The first condition is generally needed for all levels/tapes before version
2818     3.1.0, which might use the old behaviour before it was changed; known tapes
2819     that are affected are some tapes from the level set "Walpurgis Gardens" by
2820     Jamie Cullen.
2821     The second condition is an exception from the above case and is needed for
2822     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2823     above (including some development versions of 3.1.0), but before it was
2824     known that this change would break tapes like the above and was fixed in
2825     3.1.1, so that the changed behaviour was active although the engine version
2826     while recording maybe was before 3.1.0. There is at least one tape that is
2827     affected by this exception, which is the tape for the one-level set "Bug
2828     Machine" by Juergen Bonhagen.
2829   */
2830
2831   game.use_change_when_pushing_bug =
2832     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2833      !(tape.playing &&
2834        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2835        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2836
2837   /*
2838     Summary of bugfix/change:
2839     Fixed handling for blocking the field the player leaves when moving.
2840
2841     Fixed/changed in version:
2842     3.1.1
2843
2844     Description:
2845     Before 3.1.1, when "block last field when moving" was enabled, the field
2846     the player is leaving when moving was blocked for the time of the move,
2847     and was directly unblocked afterwards. This resulted in the last field
2848     being blocked for exactly one less than the number of frames of one player
2849     move. Additionally, even when blocking was disabled, the last field was
2850     blocked for exactly one frame.
2851     Since 3.1.1, due to changes in player movement handling, the last field
2852     is not blocked at all when blocking is disabled. When blocking is enabled,
2853     the last field is blocked for exactly the number of frames of one player
2854     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2855     last field is blocked for exactly one more than the number of frames of
2856     one player move.
2857
2858     Affected levels/tapes:
2859     (!!! yet to be determined -- probably many !!!)
2860   */
2861
2862   game.use_block_last_field_bug =
2863     (game.engine_version < VERSION_IDENT(3,1,1,0));
2864
2865   game_em.use_single_button =
2866     (game.engine_version > VERSION_IDENT(4,0,0,2));
2867
2868   game_em.use_snap_key_bug =
2869     (game.engine_version < VERSION_IDENT(4,0,1,0));
2870
2871   /* ---------------------------------------------------------------------- */
2872
2873   /* set maximal allowed number of custom element changes per game frame */
2874   game.max_num_changes_per_frame = 1;
2875
2876   /* default scan direction: scan playfield from top/left to bottom/right */
2877   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2878
2879   /* dynamically adjust element properties according to game engine version */
2880   InitElementPropertiesEngine(game.engine_version);
2881
2882 #if 0
2883   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2884   printf("          tape version == %06d [%s] [file: %06d]\n",
2885          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2886          tape.file_version);
2887   printf("       => game.engine_version == %06d\n", game.engine_version);
2888 #endif
2889
2890   /* ---------- initialize player's initial move delay --------------------- */
2891
2892   /* dynamically adjust player properties according to level information */
2893   for (i = 0; i < MAX_PLAYERS; i++)
2894     game.initial_move_delay_value[i] =
2895       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2896
2897   /* dynamically adjust player properties according to game engine version */
2898   for (i = 0; i < MAX_PLAYERS; i++)
2899     game.initial_move_delay[i] =
2900       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2901        game.initial_move_delay_value[i] : 0);
2902
2903   /* ---------- initialize player's initial push delay --------------------- */
2904
2905   /* dynamically adjust player properties according to game engine version */
2906   game.initial_push_delay_value =
2907     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2908
2909   /* ---------- initialize changing elements ------------------------------- */
2910
2911   /* initialize changing elements information */
2912   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2913   {
2914     struct ElementInfo *ei = &element_info[i];
2915
2916     /* this pointer might have been changed in the level editor */
2917     ei->change = &ei->change_page[0];
2918
2919     if (!IS_CUSTOM_ELEMENT(i))
2920     {
2921       ei->change->target_element = EL_EMPTY_SPACE;
2922       ei->change->delay_fixed = 0;
2923       ei->change->delay_random = 0;
2924       ei->change->delay_frames = 1;
2925     }
2926
2927     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2928     {
2929       ei->has_change_event[j] = FALSE;
2930
2931       ei->event_page_nr[j] = 0;
2932       ei->event_page[j] = &ei->change_page[0];
2933     }
2934   }
2935
2936   /* add changing elements from pre-defined list */
2937   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2938   {
2939     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2940     struct ElementInfo *ei = &element_info[ch_delay->element];
2941
2942     ei->change->target_element       = ch_delay->target_element;
2943     ei->change->delay_fixed          = ch_delay->change_delay;
2944
2945     ei->change->pre_change_function  = ch_delay->pre_change_function;
2946     ei->change->change_function      = ch_delay->change_function;
2947     ei->change->post_change_function = ch_delay->post_change_function;
2948
2949     ei->change->can_change = TRUE;
2950     ei->change->can_change_or_has_action = TRUE;
2951
2952     ei->has_change_event[CE_DELAY] = TRUE;
2953
2954     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2955     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2956   }
2957
2958   /* ---------- initialize internal run-time variables --------------------- */
2959
2960   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2961   {
2962     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2963
2964     for (j = 0; j < ei->num_change_pages; j++)
2965     {
2966       ei->change_page[j].can_change_or_has_action =
2967         (ei->change_page[j].can_change |
2968          ei->change_page[j].has_action);
2969     }
2970   }
2971
2972   /* add change events from custom element configuration */
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       if (!ei->change_page[j].can_change_or_has_action)
2980         continue;
2981
2982       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2983       {
2984         /* only add event page for the first page found with this event */
2985         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2986         {
2987           ei->has_change_event[k] = TRUE;
2988
2989           ei->event_page_nr[k] = j;
2990           ei->event_page[k] = &ei->change_page[j];
2991         }
2992       }
2993     }
2994   }
2995
2996   /* ---------- initialize reference elements in change conditions --------- */
2997
2998   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2999   {
3000     int element = EL_CUSTOM_START + i;
3001     struct ElementInfo *ei = &element_info[element];
3002
3003     for (j = 0; j < ei->num_change_pages; j++)
3004     {
3005       int trigger_element = ei->change_page[j].initial_trigger_element;
3006
3007       if (trigger_element >= EL_PREV_CE_8 &&
3008           trigger_element <= EL_NEXT_CE_8)
3009         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3010
3011       ei->change_page[j].trigger_element = trigger_element;
3012     }
3013   }
3014
3015   /* ---------- initialize run-time trigger player and element ------------- */
3016
3017   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3018   {
3019     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3020
3021     for (j = 0; j < ei->num_change_pages; j++)
3022     {
3023       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3024       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3025       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3026       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3027       ei->change_page[j].actual_trigger_ce_value = 0;
3028       ei->change_page[j].actual_trigger_ce_score = 0;
3029     }
3030   }
3031
3032   /* ---------- initialize trigger events ---------------------------------- */
3033
3034   /* initialize trigger events information */
3035   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3036     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3037       trigger_events[i][j] = FALSE;
3038
3039   /* add trigger events from element change event properties */
3040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3041   {
3042     struct ElementInfo *ei = &element_info[i];
3043
3044     for (j = 0; j < ei->num_change_pages; j++)
3045     {
3046       if (!ei->change_page[j].can_change_or_has_action)
3047         continue;
3048
3049       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3050       {
3051         int trigger_element = ei->change_page[j].trigger_element;
3052
3053         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3054         {
3055           if (ei->change_page[j].has_event[k])
3056           {
3057             if (IS_GROUP_ELEMENT(trigger_element))
3058             {
3059               struct ElementGroupInfo *group =
3060                 element_info[trigger_element].group;
3061
3062               for (l = 0; l < group->num_elements_resolved; l++)
3063                 trigger_events[group->element_resolved[l]][k] = TRUE;
3064             }
3065             else if (trigger_element == EL_ANY_ELEMENT)
3066               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3067                 trigger_events[l][k] = TRUE;
3068             else
3069               trigger_events[trigger_element][k] = TRUE;
3070           }
3071         }
3072       }
3073     }
3074   }
3075
3076   /* ---------- initialize push delay -------------------------------------- */
3077
3078   /* initialize push delay values to default */
3079   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3080   {
3081     if (!IS_CUSTOM_ELEMENT(i))
3082     {
3083       /* set default push delay values (corrected since version 3.0.7-1) */
3084       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3085       {
3086         element_info[i].push_delay_fixed = 2;
3087         element_info[i].push_delay_random = 8;
3088       }
3089       else
3090       {
3091         element_info[i].push_delay_fixed = 8;
3092         element_info[i].push_delay_random = 8;
3093       }
3094     }
3095   }
3096
3097   /* set push delay value for certain elements from pre-defined list */
3098   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3099   {
3100     int e = push_delay_list[i].element;
3101
3102     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3103     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3104   }
3105
3106   /* set push delay value for Supaplex elements for newer engine versions */
3107   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3108   {
3109     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3110     {
3111       if (IS_SP_ELEMENT(i))
3112       {
3113         /* set SP push delay to just enough to push under a falling zonk */
3114         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3115
3116         element_info[i].push_delay_fixed  = delay;
3117         element_info[i].push_delay_random = 0;
3118       }
3119     }
3120   }
3121
3122   /* ---------- initialize move stepsize ----------------------------------- */
3123
3124   /* initialize move stepsize values to default */
3125   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3126     if (!IS_CUSTOM_ELEMENT(i))
3127       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3128
3129   /* set move stepsize value for certain elements from pre-defined list */
3130   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3131   {
3132     int e = move_stepsize_list[i].element;
3133
3134     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3135   }
3136
3137   /* ---------- initialize collect score ----------------------------------- */
3138
3139   /* initialize collect score values for custom elements from initial value */
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141     if (IS_CUSTOM_ELEMENT(i))
3142       element_info[i].collect_score = element_info[i].collect_score_initial;
3143
3144   /* ---------- initialize collect count ----------------------------------- */
3145
3146   /* initialize collect count values for non-custom elements */
3147   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3148     if (!IS_CUSTOM_ELEMENT(i))
3149       element_info[i].collect_count_initial = 0;
3150
3151   /* add collect count values for all elements from pre-defined list */
3152   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3153     element_info[collect_count_list[i].element].collect_count_initial =
3154       collect_count_list[i].count;
3155
3156   /* ---------- initialize access direction -------------------------------- */
3157
3158   /* initialize access direction values to default (access from every side) */
3159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160     if (!IS_CUSTOM_ELEMENT(i))
3161       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3162
3163   /* set access direction value for certain elements from pre-defined list */
3164   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3165     element_info[access_direction_list[i].element].access_direction =
3166       access_direction_list[i].direction;
3167
3168   /* ---------- initialize explosion content ------------------------------- */
3169   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3170   {
3171     if (IS_CUSTOM_ELEMENT(i))
3172       continue;
3173
3174     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3175     {
3176       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3177
3178       element_info[i].content.e[x][y] =
3179         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3180          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3181          i == EL_PLAYER_3 ? EL_EMERALD :
3182          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3183          i == EL_MOLE ? EL_EMERALD_RED :
3184          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3185          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3186          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3187          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3188          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3189          i == EL_WALL_EMERALD ? EL_EMERALD :
3190          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3191          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3192          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3193          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3194          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3195          i == EL_WALL_PEARL ? EL_PEARL :
3196          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3197          EL_EMPTY);
3198     }
3199   }
3200
3201   /* ---------- initialize recursion detection ------------------------------ */
3202   recursion_loop_depth = 0;
3203   recursion_loop_detected = FALSE;
3204   recursion_loop_element = EL_UNDEFINED;
3205
3206   /* ---------- initialize graphics engine ---------------------------------- */
3207   game.scroll_delay_value =
3208     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3209      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3210   game.scroll_delay_value =
3211     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3212
3213   /* ---------- initialize game engine snapshots ---------------------------- */
3214   for (i = 0; i < MAX_PLAYERS; i++)
3215     game.snapshot.last_action[i] = 0;
3216   game.snapshot.changed_action = FALSE;
3217   game.snapshot.collected_item = FALSE;
3218   game.snapshot.mode =
3219     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3220      SNAPSHOT_MODE_EVERY_STEP :
3221      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3222      SNAPSHOT_MODE_EVERY_MOVE :
3223      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3224      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3225   game.snapshot.save_snapshot = FALSE;
3226
3227   /* ---------- initialize level time for Supaplex engine ------------------- */
3228   /* Supaplex levels with time limit currently unsupported -- should be added */
3229   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3230     level.time = 0;
3231 }
3232
3233 int get_num_special_action(int element, int action_first, int action_last)
3234 {
3235   int num_special_action = 0;
3236   int i, j;
3237
3238   for (i = action_first; i <= action_last; i++)
3239   {
3240     boolean found = FALSE;
3241
3242     for (j = 0; j < NUM_DIRECTIONS; j++)
3243       if (el_act_dir2img(element, i, j) !=
3244           el_act_dir2img(element, ACTION_DEFAULT, j))
3245         found = TRUE;
3246
3247     if (found)
3248       num_special_action++;
3249     else
3250       break;
3251   }
3252
3253   return num_special_action;
3254 }
3255
3256
3257 /*
3258   =============================================================================
3259   InitGame()
3260   -----------------------------------------------------------------------------
3261   initialize and start new game
3262   =============================================================================
3263 */
3264
3265 void InitGame()
3266 {
3267   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3268   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3269   int fade_mask = REDRAW_FIELD;
3270
3271   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3272   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3273   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3274   int initial_move_dir = MV_DOWN;
3275   int i, j, x, y;
3276
3277   // required here to update video display before fading (FIX THIS)
3278   DrawMaskedBorder(REDRAW_DOOR_2);
3279
3280   if (!game.restart_level)
3281     CloseDoor(DOOR_CLOSE_1);
3282
3283   SetGameStatus(GAME_MODE_PLAYING);
3284
3285   if (level_editor_test_game)
3286     FadeSkipNextFadeIn();
3287   else
3288     FadeSetEnterScreen();
3289
3290   if (CheckIfGlobalBorderHasChanged())
3291     fade_mask = REDRAW_ALL;
3292
3293   FadeLevelSoundsAndMusic();
3294
3295   ExpireSoundLoops(TRUE);
3296
3297   FadeOut(fade_mask);
3298
3299   /* needed if different viewport properties defined for playing */
3300   ChangeViewportPropertiesIfNeeded();
3301
3302   ClearField();
3303
3304   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3305
3306   DrawCompleteVideoDisplay();
3307
3308   InitGameEngine();
3309   InitGameControlValues();
3310
3311   /* don't play tapes over network */
3312   network_playing = (options.network && !tape.playing);
3313
3314   for (i = 0; i < MAX_PLAYERS; i++)
3315   {
3316     struct PlayerInfo *player = &stored_player[i];
3317
3318     player->index_nr = i;
3319     player->index_bit = (1 << i);
3320     player->element_nr = EL_PLAYER_1 + i;
3321
3322     player->present = FALSE;
3323     player->active = FALSE;
3324     player->mapped = FALSE;
3325
3326     player->killed = FALSE;
3327     player->reanimated = FALSE;
3328
3329     player->action = 0;
3330     player->effective_action = 0;
3331     player->programmed_action = 0;
3332
3333     player->mouse_action.lx = 0;
3334     player->mouse_action.ly = 0;
3335     player->mouse_action.button = 0;
3336
3337     player->score = 0;
3338     player->score_final = 0;
3339
3340     player->gems_still_needed = level.gems_needed;
3341     player->sokobanfields_still_needed = 0;
3342     player->lights_still_needed = 0;
3343     player->friends_still_needed = 0;
3344
3345     for (j = 0; j < MAX_NUM_KEYS; j++)
3346       player->key[j] = FALSE;
3347
3348     player->num_white_keys = 0;
3349
3350     player->dynabomb_count = 0;
3351     player->dynabomb_size = 1;
3352     player->dynabombs_left = 0;
3353     player->dynabomb_xl = FALSE;
3354
3355     player->MovDir = initial_move_dir;
3356     player->MovPos = 0;
3357     player->GfxPos = 0;
3358     player->GfxDir = initial_move_dir;
3359     player->GfxAction = ACTION_DEFAULT;
3360     player->Frame = 0;
3361     player->StepFrame = 0;
3362
3363     player->initial_element = player->element_nr;
3364     player->artwork_element =
3365       (level.use_artwork_element[i] ? level.artwork_element[i] :
3366        player->element_nr);
3367     player->use_murphy = FALSE;
3368
3369     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3370     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3371
3372     player->gravity = level.initial_player_gravity[i];
3373
3374     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3375
3376     player->actual_frame_counter = 0;
3377
3378     player->step_counter = 0;
3379
3380     player->last_move_dir = initial_move_dir;
3381
3382     player->is_active = FALSE;
3383
3384     player->is_waiting = FALSE;
3385     player->is_moving = FALSE;
3386     player->is_auto_moving = FALSE;
3387     player->is_digging = FALSE;
3388     player->is_snapping = FALSE;
3389     player->is_collecting = FALSE;
3390     player->is_pushing = FALSE;
3391     player->is_switching = FALSE;
3392     player->is_dropping = FALSE;
3393     player->is_dropping_pressed = FALSE;
3394
3395     player->is_bored = FALSE;
3396     player->is_sleeping = FALSE;
3397
3398     player->was_waiting = TRUE;
3399     player->was_moving = FALSE;
3400     player->was_snapping = FALSE;
3401     player->was_dropping = FALSE;
3402
3403     player->force_dropping = FALSE;
3404
3405     player->frame_counter_bored = -1;
3406     player->frame_counter_sleeping = -1;
3407
3408     player->anim_delay_counter = 0;
3409     player->post_delay_counter = 0;
3410
3411     player->dir_waiting = initial_move_dir;
3412     player->action_waiting = ACTION_DEFAULT;
3413     player->last_action_waiting = ACTION_DEFAULT;
3414     player->special_action_bored = ACTION_DEFAULT;
3415     player->special_action_sleeping = ACTION_DEFAULT;
3416
3417     player->switch_x = -1;
3418     player->switch_y = -1;
3419
3420     player->drop_x = -1;
3421     player->drop_y = -1;
3422
3423     player->show_envelope = 0;
3424
3425     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3426
3427     player->push_delay       = -1;      /* initialized when pushing starts */
3428     player->push_delay_value = game.initial_push_delay_value;
3429
3430     player->drop_delay = 0;
3431     player->drop_pressed_delay = 0;
3432
3433     player->last_jx = -1;
3434     player->last_jy = -1;
3435     player->jx = -1;
3436     player->jy = -1;
3437
3438     player->shield_normal_time_left = 0;
3439     player->shield_deadly_time_left = 0;
3440
3441     player->inventory_infinite_element = EL_UNDEFINED;
3442     player->inventory_size = 0;
3443
3444     if (level.use_initial_inventory[i])
3445     {
3446       for (j = 0; j < level.initial_inventory_size[i]; j++)
3447       {
3448         int element = level.initial_inventory_content[i][j];
3449         int collect_count = element_info[element].collect_count_initial;
3450         int k;
3451
3452         if (!IS_CUSTOM_ELEMENT(element))
3453           collect_count = 1;
3454
3455         if (collect_count == 0)
3456           player->inventory_infinite_element = element;
3457         else
3458           for (k = 0; k < collect_count; k++)
3459             if (player->inventory_size < MAX_INVENTORY_SIZE)
3460               player->inventory_element[player->inventory_size++] = element;
3461       }
3462     }
3463
3464     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3465     SnapField(player, 0, 0);
3466
3467     player->LevelSolved = FALSE;
3468     player->GameOver = FALSE;
3469
3470     player->LevelSolved_GameWon = FALSE;
3471     player->LevelSolved_GameEnd = FALSE;
3472     player->LevelSolved_PanelOff = FALSE;
3473     player->LevelSolved_SaveTape = FALSE;
3474     player->LevelSolved_SaveScore = FALSE;
3475     player->LevelSolved_CountingTime = 0;
3476     player->LevelSolved_CountingScore = 0;
3477
3478     map_player_action[i] = i;
3479   }
3480
3481   network_player_action_received = FALSE;
3482
3483 #if defined(NETWORK_AVALIABLE)
3484   /* initial null action */
3485   if (network_playing)
3486     SendToServer_MovePlayer(MV_NONE);
3487 #endif
3488
3489   ZX = ZY = -1;
3490   ExitX = ExitY = -1;
3491
3492   FrameCounter = 0;
3493   TimeFrames = 0;
3494   TimePlayed = 0;
3495   TimeLeft = level.time;
3496   TapeTime = 0;
3497
3498   ScreenMovDir = MV_NONE;
3499   ScreenMovPos = 0;
3500   ScreenGfxPos = 0;
3501
3502   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3503
3504   AllPlayersGone = FALSE;
3505
3506   game.no_time_limit = (level.time == 0);
3507
3508   game.yamyam_content_nr = 0;
3509   game.robot_wheel_active = FALSE;
3510   game.magic_wall_active = FALSE;
3511   game.magic_wall_time_left = 0;
3512   game.light_time_left = 0;
3513   game.timegate_time_left = 0;
3514   game.switchgate_pos = 0;
3515   game.wind_direction = level.wind_direction_initial;
3516
3517   game.lenses_time_left = 0;
3518   game.magnify_time_left = 0;
3519
3520   game.ball_state = level.ball_state_initial;
3521   game.ball_content_nr = 0;
3522
3523   game.envelope_active = FALSE;
3524
3525   /* set focus to local player for network games, else to all players */
3526   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3527   game.centered_player_nr_next = game.centered_player_nr;
3528   game.set_centered_player = FALSE;
3529
3530   if (network_playing && tape.recording)
3531   {
3532     /* store client dependent player focus when recording network games */
3533     tape.centered_player_nr_next = game.centered_player_nr_next;
3534     tape.set_centered_player = TRUE;
3535   }
3536
3537   for (i = 0; i < NUM_BELTS; i++)
3538   {
3539     game.belt_dir[i] = MV_NONE;
3540     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3541   }
3542
3543   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3544     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3545
3546 #if DEBUG_INIT_PLAYER
3547   if (options.debug)
3548   {
3549     printf("Player status at level initialization:\n");
3550   }
3551 #endif
3552
3553   SCAN_PLAYFIELD(x, y)
3554   {
3555     Feld[x][y] = level.field[x][y];
3556     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3557     ChangeDelay[x][y] = 0;
3558     ChangePage[x][y] = -1;
3559     CustomValue[x][y] = 0;              /* initialized in InitField() */
3560     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3561     AmoebaNr[x][y] = 0;
3562     WasJustMoving[x][y] = 0;
3563     WasJustFalling[x][y] = 0;
3564     CheckCollision[x][y] = 0;
3565     CheckImpact[x][y] = 0;
3566     Stop[x][y] = FALSE;
3567     Pushed[x][y] = FALSE;
3568
3569     ChangeCount[x][y] = 0;
3570     ChangeEvent[x][y] = -1;
3571
3572     ExplodePhase[x][y] = 0;
3573     ExplodeDelay[x][y] = 0;
3574     ExplodeField[x][y] = EX_TYPE_NONE;
3575
3576     RunnerVisit[x][y] = 0;
3577     PlayerVisit[x][y] = 0;
3578
3579     GfxFrame[x][y] = 0;
3580     GfxRandom[x][y] = INIT_GFX_RANDOM();
3581     GfxElement[x][y] = EL_UNDEFINED;
3582     GfxAction[x][y] = ACTION_DEFAULT;
3583     GfxDir[x][y] = MV_NONE;
3584     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3585   }
3586
3587   SCAN_PLAYFIELD(x, y)
3588   {
3589     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3590       emulate_bd = FALSE;
3591     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3592       emulate_sb = FALSE;
3593     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3594       emulate_sp = FALSE;
3595
3596     InitField(x, y, TRUE);
3597
3598     ResetGfxAnimation(x, y);
3599   }
3600
3601   InitBeltMovement();
3602
3603   for (i = 0; i < MAX_PLAYERS; i++)
3604   {
3605     struct PlayerInfo *player = &stored_player[i];
3606
3607     /* set number of special actions for bored and sleeping animation */
3608     player->num_special_action_bored =
3609       get_num_special_action(player->artwork_element,
3610                              ACTION_BORING_1, ACTION_BORING_LAST);
3611     player->num_special_action_sleeping =
3612       get_num_special_action(player->artwork_element,
3613                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3614   }
3615
3616   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3617                     emulate_sb ? EMU_SOKOBAN :
3618                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3619
3620   /* initialize type of slippery elements */
3621   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3622   {
3623     if (!IS_CUSTOM_ELEMENT(i))
3624     {
3625       /* default: elements slip down either to the left or right randomly */
3626       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3627
3628       /* SP style elements prefer to slip down on the left side */
3629       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3630         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3631
3632       /* BD style elements prefer to slip down on the left side */
3633       if (game.emulation == EMU_BOULDERDASH)
3634         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3635     }
3636   }
3637
3638   /* initialize explosion and ignition delay */
3639   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3640   {
3641     if (!IS_CUSTOM_ELEMENT(i))
3642     {
3643       int num_phase = 8;
3644       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3645                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3646                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3647       int last_phase = (num_phase + 1) * delay;
3648       int half_phase = (num_phase / 2) * delay;
3649
3650       element_info[i].explosion_delay = last_phase - 1;
3651       element_info[i].ignition_delay = half_phase;
3652
3653       if (i == EL_BLACK_ORB)
3654         element_info[i].ignition_delay = 1;
3655     }
3656   }
3657
3658   /* correct non-moving belts to start moving left */
3659   for (i = 0; i < NUM_BELTS; i++)
3660     if (game.belt_dir[i] == MV_NONE)
3661       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3662
3663 #if USE_NEW_PLAYER_ASSIGNMENTS
3664   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3665   /* choose default local player */
3666   local_player = &stored_player[0];
3667
3668   for (i = 0; i < MAX_PLAYERS; i++)
3669     stored_player[i].connected = FALSE;
3670
3671   local_player->connected = TRUE;
3672   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3673
3674   if (tape.playing)
3675   {
3676     for (i = 0; i < MAX_PLAYERS; i++)
3677       stored_player[i].connected = tape.player_participates[i];
3678   }
3679   else if (game.team_mode && !options.network)
3680   {
3681     /* try to guess locally connected team mode players (needed for correct
3682        assignment of player figures from level to locally playing players) */
3683
3684     for (i = 0; i < MAX_PLAYERS; i++)
3685       if (setup.input[i].use_joystick ||
3686           setup.input[i].key.left != KSYM_UNDEFINED)
3687         stored_player[i].connected = TRUE;
3688   }
3689
3690 #if DEBUG_INIT_PLAYER
3691   if (options.debug)
3692   {
3693     printf("Player status after level initialization:\n");
3694
3695     for (i = 0; i < MAX_PLAYERS; i++)
3696     {
3697       struct PlayerInfo *player = &stored_player[i];
3698
3699       printf("- player %d: present == %d, connected == %d, active == %d",
3700              i + 1,
3701              player->present,
3702              player->connected,
3703              player->active);
3704
3705       if (local_player == player)
3706         printf(" (local player)");
3707
3708       printf("\n");
3709     }
3710   }
3711 #endif
3712
3713 #if DEBUG_INIT_PLAYER
3714   if (options.debug)
3715     printf("Reassigning players ...\n");
3716 #endif
3717
3718   /* check if any connected player was not found in playfield */
3719   for (i = 0; i < MAX_PLAYERS; i++)
3720   {
3721     struct PlayerInfo *player = &stored_player[i];
3722
3723     if (player->connected && !player->present)
3724     {
3725       struct PlayerInfo *field_player = NULL;
3726
3727 #if DEBUG_INIT_PLAYER
3728       if (options.debug)
3729         printf("- looking for field player for player %d ...\n", i + 1);
3730 #endif
3731
3732       /* assign first free player found that is present in the playfield */
3733
3734       /* first try: look for unmapped playfield player that is not connected */
3735       for (j = 0; j < MAX_PLAYERS; j++)
3736         if (field_player == NULL &&
3737             stored_player[j].present &&
3738             !stored_player[j].mapped &&
3739             !stored_player[j].connected)
3740           field_player = &stored_player[j];
3741
3742       /* second try: look for *any* unmapped playfield player */
3743       for (j = 0; j < MAX_PLAYERS; j++)
3744         if (field_player == NULL &&
3745             stored_player[j].present &&
3746             !stored_player[j].mapped)
3747           field_player = &stored_player[j];
3748
3749       if (field_player != NULL)
3750       {
3751         int jx = field_player->jx, jy = field_player->jy;
3752
3753 #if DEBUG_INIT_PLAYER
3754         if (options.debug)
3755           printf("- found player %d\n", field_player->index_nr + 1);
3756 #endif
3757
3758         player->present = FALSE;
3759         player->active = FALSE;
3760
3761         field_player->present = TRUE;
3762         field_player->active = TRUE;
3763
3764         /*
3765         player->initial_element = field_player->initial_element;
3766         player->artwork_element = field_player->artwork_element;
3767
3768         player->block_last_field       = field_player->block_last_field;
3769         player->block_delay_adjustment = field_player->block_delay_adjustment;
3770         */
3771
3772         StorePlayer[jx][jy] = field_player->element_nr;
3773
3774         field_player->jx = field_player->last_jx = jx;
3775         field_player->jy = field_player->last_jy = jy;
3776
3777         if (local_player == player)
3778           local_player = field_player;
3779
3780         map_player_action[field_player->index_nr] = i;
3781
3782         field_player->mapped = TRUE;
3783
3784 #if DEBUG_INIT_PLAYER
3785         if (options.debug)
3786           printf("- map_player_action[%d] == %d\n",
3787                  field_player->index_nr + 1, i + 1);
3788 #endif
3789       }
3790     }
3791
3792     if (player->connected && player->present)
3793       player->mapped = TRUE;
3794   }
3795
3796 #if DEBUG_INIT_PLAYER
3797   if (options.debug)
3798   {
3799     printf("Player status after player assignment (first stage):\n");
3800
3801     for (i = 0; i < MAX_PLAYERS; i++)
3802     {
3803       struct PlayerInfo *player = &stored_player[i];
3804
3805       printf("- player %d: present == %d, connected == %d, active == %d",
3806              i + 1,
3807              player->present,
3808              player->connected,
3809              player->active);
3810
3811       if (local_player == player)
3812         printf(" (local player)");
3813
3814       printf("\n");
3815     }
3816   }
3817 #endif
3818
3819 #else
3820
3821   /* check if any connected player was not found in playfield */
3822   for (i = 0; i < MAX_PLAYERS; i++)
3823   {
3824     struct PlayerInfo *player = &stored_player[i];
3825
3826     if (player->connected && !player->present)
3827     {
3828       for (j = 0; j < MAX_PLAYERS; j++)
3829       {
3830         struct PlayerInfo *field_player = &stored_player[j];
3831         int jx = field_player->jx, jy = field_player->jy;
3832
3833         /* assign first free player found that is present in the playfield */
3834         if (field_player->present && !field_player->connected)
3835         {
3836           player->present = TRUE;
3837           player->active = TRUE;
3838
3839           field_player->present = FALSE;
3840           field_player->active = FALSE;
3841
3842           player->initial_element = field_player->initial_element;
3843           player->artwork_element = field_player->artwork_element;
3844
3845           player->block_last_field       = field_player->block_last_field;
3846           player->block_delay_adjustment = field_player->block_delay_adjustment;
3847
3848           StorePlayer[jx][jy] = player->element_nr;
3849
3850           player->jx = player->last_jx = jx;
3851           player->jy = player->last_jy = jy;
3852
3853           break;
3854         }
3855       }
3856     }
3857   }
3858 #endif
3859
3860 #if 0
3861   printf("::: local_player->present == %d\n", local_player->present);
3862 #endif
3863
3864   if (tape.playing)
3865   {
3866     /* when playing a tape, eliminate all players who do not participate */
3867
3868 #if USE_NEW_PLAYER_ASSIGNMENTS
3869
3870     if (!game.team_mode)
3871     {
3872       for (i = 0; i < MAX_PLAYERS; i++)
3873       {
3874         if (stored_player[i].active &&
3875             !tape.player_participates[map_player_action[i]])
3876         {
3877           struct PlayerInfo *player = &stored_player[i];
3878           int jx = player->jx, jy = player->jy;
3879
3880 #if DEBUG_INIT_PLAYER
3881           if (options.debug)
3882             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3883 #endif
3884
3885           player->active = FALSE;
3886           StorePlayer[jx][jy] = 0;
3887           Feld[jx][jy] = EL_EMPTY;
3888         }
3889       }
3890     }
3891
3892 #else
3893
3894     for (i = 0; i < MAX_PLAYERS; i++)
3895     {
3896       if (stored_player[i].active &&
3897           !tape.player_participates[i])
3898       {
3899         struct PlayerInfo *player = &stored_player[i];
3900         int jx = player->jx, jy = player->jy;
3901
3902         player->active = FALSE;
3903         StorePlayer[jx][jy] = 0;
3904         Feld[jx][jy] = EL_EMPTY;
3905       }
3906     }
3907 #endif
3908   }
3909   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3910   {
3911     /* when in single player mode, eliminate all but the first active player */
3912
3913     for (i = 0; i < MAX_PLAYERS; i++)
3914     {
3915       if (stored_player[i].active)
3916       {
3917         for (j = i + 1; j < MAX_PLAYERS; j++)
3918         {
3919           if (stored_player[j].active)
3920           {
3921             struct PlayerInfo *player = &stored_player[j];
3922             int jx = player->jx, jy = player->jy;
3923
3924             player->active = FALSE;
3925             player->present = FALSE;
3926
3927             StorePlayer[jx][jy] = 0;
3928             Feld[jx][jy] = EL_EMPTY;
3929           }
3930         }
3931       }
3932     }
3933   }
3934
3935   /* when recording the game, store which players take part in the game */
3936   if (tape.recording)
3937   {
3938 #if USE_NEW_PLAYER_ASSIGNMENTS
3939     for (i = 0; i < MAX_PLAYERS; i++)
3940       if (stored_player[i].connected)
3941         tape.player_participates[i] = TRUE;
3942 #else
3943     for (i = 0; i < MAX_PLAYERS; i++)
3944       if (stored_player[i].active)
3945         tape.player_participates[i] = TRUE;
3946 #endif
3947   }
3948
3949 #if DEBUG_INIT_PLAYER
3950   if (options.debug)
3951   {
3952     printf("Player status after player assignment (final stage):\n");
3953
3954     for (i = 0; i < MAX_PLAYERS; i++)
3955     {
3956       struct PlayerInfo *player = &stored_player[i];
3957
3958       printf("- player %d: present == %d, connected == %d, active == %d",
3959              i + 1,
3960              player->present,
3961              player->connected,
3962              player->active);
3963
3964       if (local_player == player)
3965         printf(" (local player)");
3966
3967       printf("\n");
3968     }
3969   }
3970 #endif
3971
3972   if (BorderElement == EL_EMPTY)
3973   {
3974     SBX_Left = 0;
3975     SBX_Right = lev_fieldx - SCR_FIELDX;
3976     SBY_Upper = 0;
3977     SBY_Lower = lev_fieldy - SCR_FIELDY;
3978   }
3979   else
3980   {
3981     SBX_Left = -1;
3982     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3983     SBY_Upper = -1;
3984     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3985   }
3986
3987   if (full_lev_fieldx <= SCR_FIELDX)
3988     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3989   if (full_lev_fieldy <= SCR_FIELDY)
3990     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3991
3992   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3993     SBX_Left--;
3994   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3995     SBY_Upper--;
3996
3997   /* if local player not found, look for custom element that might create
3998      the player (make some assumptions about the right custom element) */
3999   if (!local_player->present)
4000   {
4001     int start_x = 0, start_y = 0;
4002     int found_rating = 0;
4003     int found_element = EL_UNDEFINED;
4004     int player_nr = local_player->index_nr;
4005
4006     SCAN_PLAYFIELD(x, y)
4007     {
4008       int element = Feld[x][y];
4009       int content;
4010       int xx, yy;
4011       boolean is_player;
4012
4013       if (level.use_start_element[player_nr] &&
4014           level.start_element[player_nr] == element &&
4015           found_rating < 4)
4016       {
4017         start_x = x;
4018         start_y = y;
4019
4020         found_rating = 4;
4021         found_element = element;
4022       }
4023
4024       if (!IS_CUSTOM_ELEMENT(element))
4025         continue;
4026
4027       if (CAN_CHANGE(element))
4028       {
4029         for (i = 0; i < element_info[element].num_change_pages; i++)
4030         {
4031           /* check for player created from custom element as single target */
4032           content = element_info[element].change_page[i].target_element;
4033           is_player = ELEM_IS_PLAYER(content);
4034
4035           if (is_player && (found_rating < 3 ||
4036                             (found_rating == 3 && element < found_element)))
4037           {
4038             start_x = x;
4039             start_y = y;
4040
4041             found_rating = 3;
4042             found_element = element;
4043           }
4044         }
4045       }
4046
4047       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4048       {
4049         /* check for player created from custom element as explosion content */
4050         content = element_info[element].content.e[xx][yy];
4051         is_player = ELEM_IS_PLAYER(content);
4052
4053         if (is_player && (found_rating < 2 ||
4054                           (found_rating == 2 && element < found_element)))
4055         {
4056           start_x = x + xx - 1;
4057           start_y = y + yy - 1;
4058
4059           found_rating = 2;
4060           found_element = element;
4061         }
4062
4063         if (!CAN_CHANGE(element))
4064           continue;
4065
4066         for (i = 0; i < element_info[element].num_change_pages; i++)
4067         {
4068           /* check for player created from custom element as extended target */
4069           content =
4070             element_info[element].change_page[i].target_content.e[xx][yy];
4071
4072           is_player = ELEM_IS_PLAYER(content);
4073
4074           if (is_player && (found_rating < 1 ||
4075                             (found_rating == 1 && element < found_element)))
4076           {
4077             start_x = x + xx - 1;
4078             start_y = y + yy - 1;
4079
4080             found_rating = 1;
4081             found_element = element;
4082           }
4083         }
4084       }
4085     }
4086
4087     scroll_x = SCROLL_POSITION_X(start_x);
4088     scroll_y = SCROLL_POSITION_Y(start_y);
4089   }
4090   else
4091   {
4092     scroll_x = SCROLL_POSITION_X(local_player->jx);
4093     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4094   }
4095
4096   /* !!! FIX THIS (START) !!! */
4097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4098   {
4099     InitGameEngine_EM();
4100   }
4101   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4102   {
4103     InitGameEngine_SP();
4104   }
4105   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4106   {
4107     InitGameEngine_MM();
4108   }
4109   else
4110   {
4111     DrawLevel(REDRAW_FIELD);
4112     DrawAllPlayers();
4113
4114     /* after drawing the level, correct some elements */
4115     if (game.timegate_time_left == 0)
4116       CloseAllOpenTimegates();
4117   }
4118
4119   /* blit playfield from scroll buffer to normal back buffer for fading in */
4120   BlitScreenToBitmap(backbuffer);
4121   /* !!! FIX THIS (END) !!! */
4122
4123   DrawMaskedBorder(fade_mask);
4124
4125   FadeIn(fade_mask);
4126
4127 #if 1
4128   // full screen redraw is required at this point in the following cases:
4129   // - special editor door undrawn when game was started from level editor
4130   // - drawing area (playfield) was changed and has to be removed completely
4131   redraw_mask = REDRAW_ALL;
4132   BackToFront();
4133 #endif
4134
4135   if (!game.restart_level)
4136   {
4137     /* copy default game door content to main double buffer */
4138
4139     /* !!! CHECK AGAIN !!! */
4140     SetPanelBackground();
4141     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4142     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4143   }
4144
4145   SetPanelBackground();
4146   SetDrawBackgroundMask(REDRAW_DOOR_1);
4147
4148   UpdateAndDisplayGameControlValues();
4149
4150   if (!game.restart_level)
4151   {
4152     UnmapGameButtons();
4153     UnmapTapeButtons();
4154
4155     FreeGameButtons();
4156     CreateGameButtons();
4157
4158     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4159     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4160     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4161
4162     MapGameButtons();
4163     MapTapeButtons();
4164
4165     /* copy actual game door content to door double buffer for OpenDoor() */
4166     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4167
4168     OpenDoor(DOOR_OPEN_ALL);
4169
4170     PlaySound(SND_GAME_STARTING);
4171
4172     if (setup.sound_music)
4173       PlayLevelMusic();
4174
4175     KeyboardAutoRepeatOffUnlessAutoplay();
4176
4177 #if DEBUG_INIT_PLAYER
4178     if (options.debug)
4179     {
4180       printf("Player status (final):\n");
4181
4182       for (i = 0; i < MAX_PLAYERS; i++)
4183       {
4184         struct PlayerInfo *player = &stored_player[i];
4185
4186         printf("- player %d: present == %d, connected == %d, active == %d",
4187                i + 1,
4188                player->present,
4189                player->connected,
4190                player->active);
4191
4192         if (local_player == player)
4193           printf(" (local player)");
4194
4195         printf("\n");
4196       }
4197     }
4198 #endif
4199   }
4200
4201   UnmapAllGadgets();
4202
4203   MapGameButtons();
4204   MapTapeButtons();
4205
4206   if (!game.restart_level && !tape.playing)
4207   {
4208     LevelStats_incPlayed(level_nr);
4209
4210     SaveLevelSetup_SeriesInfo();
4211   }
4212
4213   game.restart_level = FALSE;
4214
4215   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4216     InitGameActions_MM();
4217
4218   SaveEngineSnapshotToListInitial();
4219 }
4220
4221 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4222                         int actual_player_x, int actual_player_y)
4223 {
4224   /* this is used for non-R'n'D game engines to update certain engine values */
4225
4226   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4227   {
4228     actual_player_x = correctLevelPosX_EM(actual_player_x);
4229     actual_player_y = correctLevelPosY_EM(actual_player_y);
4230   }
4231
4232   /* needed to determine if sounds are played within the visible screen area */
4233   scroll_x = actual_scroll_x;
4234   scroll_y = actual_scroll_y;
4235
4236   /* needed to get player position for "follow finger" playing input method */
4237   local_player->jx = actual_player_x;
4238   local_player->jy = actual_player_y;
4239 }
4240
4241 void InitMovDir(int x, int y)
4242 {
4243   int i, element = Feld[x][y];
4244   static int xy[4][2] =
4245   {
4246     {  0, +1 },
4247     { +1,  0 },
4248     {  0, -1 },
4249     { -1,  0 }
4250   };
4251   static int direction[3][4] =
4252   {
4253     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4254     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4255     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4256   };
4257
4258   switch (element)
4259   {
4260     case EL_BUG_RIGHT:
4261     case EL_BUG_UP:
4262     case EL_BUG_LEFT:
4263     case EL_BUG_DOWN:
4264       Feld[x][y] = EL_BUG;
4265       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4266       break;
4267
4268     case EL_SPACESHIP_RIGHT:
4269     case EL_SPACESHIP_UP:
4270     case EL_SPACESHIP_LEFT:
4271     case EL_SPACESHIP_DOWN:
4272       Feld[x][y] = EL_SPACESHIP;
4273       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4274       break;
4275
4276     case EL_BD_BUTTERFLY_RIGHT:
4277     case EL_BD_BUTTERFLY_UP:
4278     case EL_BD_BUTTERFLY_LEFT:
4279     case EL_BD_BUTTERFLY_DOWN:
4280       Feld[x][y] = EL_BD_BUTTERFLY;
4281       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4282       break;
4283
4284     case EL_BD_FIREFLY_RIGHT:
4285     case EL_BD_FIREFLY_UP:
4286     case EL_BD_FIREFLY_LEFT:
4287     case EL_BD_FIREFLY_DOWN:
4288       Feld[x][y] = EL_BD_FIREFLY;
4289       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4290       break;
4291
4292     case EL_PACMAN_RIGHT:
4293     case EL_PACMAN_UP:
4294     case EL_PACMAN_LEFT:
4295     case EL_PACMAN_DOWN:
4296       Feld[x][y] = EL_PACMAN;
4297       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4298       break;
4299
4300     case EL_YAMYAM_LEFT:
4301     case EL_YAMYAM_RIGHT:
4302     case EL_YAMYAM_UP:
4303     case EL_YAMYAM_DOWN:
4304       Feld[x][y] = EL_YAMYAM;
4305       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4306       break;
4307
4308     case EL_SP_SNIKSNAK:
4309       MovDir[x][y] = MV_UP;
4310       break;
4311
4312     case EL_SP_ELECTRON:
4313       MovDir[x][y] = MV_LEFT;
4314       break;
4315
4316     case EL_MOLE_LEFT:
4317     case EL_MOLE_RIGHT:
4318     case EL_MOLE_UP:
4319     case EL_MOLE_DOWN:
4320       Feld[x][y] = EL_MOLE;
4321       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4322       break;
4323
4324     default:
4325       if (IS_CUSTOM_ELEMENT(element))
4326       {
4327         struct ElementInfo *ei = &element_info[element];
4328         int move_direction_initial = ei->move_direction_initial;
4329         int move_pattern = ei->move_pattern;
4330
4331         if (move_direction_initial == MV_START_PREVIOUS)
4332         {
4333           if (MovDir[x][y] != MV_NONE)
4334             return;
4335
4336           move_direction_initial = MV_START_AUTOMATIC;
4337         }
4338
4339         if (move_direction_initial == MV_START_RANDOM)
4340           MovDir[x][y] = 1 << RND(4);
4341         else if (move_direction_initial & MV_ANY_DIRECTION)
4342           MovDir[x][y] = move_direction_initial;
4343         else if (move_pattern == MV_ALL_DIRECTIONS ||
4344                  move_pattern == MV_TURNING_LEFT ||
4345                  move_pattern == MV_TURNING_RIGHT ||
4346                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4347                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4348                  move_pattern == MV_TURNING_RANDOM)
4349           MovDir[x][y] = 1 << RND(4);
4350         else if (move_pattern == MV_HORIZONTAL)
4351           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4352         else if (move_pattern == MV_VERTICAL)
4353           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4354         else if (move_pattern & MV_ANY_DIRECTION)
4355           MovDir[x][y] = element_info[element].move_pattern;
4356         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4357                  move_pattern == MV_ALONG_RIGHT_SIDE)
4358         {
4359           /* use random direction as default start direction */
4360           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4361             MovDir[x][y] = 1 << RND(4);
4362
4363           for (i = 0; i < NUM_DIRECTIONS; i++)
4364           {
4365             int x1 = x + xy[i][0];
4366             int y1 = y + xy[i][1];
4367
4368             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4369             {
4370               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4371                 MovDir[x][y] = direction[0][i];
4372               else
4373                 MovDir[x][y] = direction[1][i];
4374
4375               break;
4376             }
4377           }
4378         }                
4379       }
4380       else
4381       {
4382         MovDir[x][y] = 1 << RND(4);
4383
4384         if (element != EL_BUG &&
4385             element != EL_SPACESHIP &&
4386             element != EL_BD_BUTTERFLY &&
4387             element != EL_BD_FIREFLY)
4388           break;
4389
4390         for (i = 0; i < NUM_DIRECTIONS; i++)
4391         {
4392           int x1 = x + xy[i][0];
4393           int y1 = y + xy[i][1];
4394
4395           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4396           {
4397             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4398             {
4399               MovDir[x][y] = direction[0][i];
4400               break;
4401             }
4402             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4403                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4404             {
4405               MovDir[x][y] = direction[1][i];
4406               break;
4407             }
4408           }
4409         }
4410       }
4411       break;
4412   }
4413
4414   GfxDir[x][y] = MovDir[x][y];
4415 }
4416
4417 void InitAmoebaNr(int x, int y)
4418 {
4419   int i;
4420   int group_nr = AmoebeNachbarNr(x, y);
4421
4422   if (group_nr == 0)
4423   {
4424     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4425     {
4426       if (AmoebaCnt[i] == 0)
4427       {
4428         group_nr = i;
4429         break;
4430       }
4431     }
4432   }
4433
4434   AmoebaNr[x][y] = group_nr;
4435   AmoebaCnt[group_nr]++;
4436   AmoebaCnt2[group_nr]++;
4437 }
4438
4439 static void PlayerWins(struct PlayerInfo *player)
4440 {
4441   player->LevelSolved = TRUE;
4442   player->GameOver = TRUE;
4443
4444   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4445                          level.native_em_level->lev->score :
4446                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4447                          game_mm.score :
4448                          player->score);
4449
4450   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4451                                       TimeLeft);
4452   player->LevelSolved_CountingScore = player->score_final;
4453 }
4454
4455 void GameWon()
4456 {
4457   static int time, time_final;
4458   static int score, score_final;
4459   static int game_over_delay_1 = 0;
4460   static int game_over_delay_2 = 0;
4461   int game_over_delay_value_1 = 50;
4462   int game_over_delay_value_2 = 50;
4463
4464   if (!local_player->LevelSolved_GameWon)
4465   {
4466     int i;
4467
4468     /* do not start end game actions before the player stops moving (to exit) */
4469     if (local_player->MovPos)
4470       return;
4471
4472     local_player->LevelSolved_GameWon = TRUE;
4473     local_player->LevelSolved_SaveTape = tape.recording;
4474     local_player->LevelSolved_SaveScore = !tape.playing;
4475
4476     if (!tape.playing)
4477     {
4478       LevelStats_incSolved(level_nr);
4479
4480       SaveLevelSetup_SeriesInfo();
4481     }
4482
4483     if (tape.auto_play)         /* tape might already be stopped here */
4484       tape.auto_play_level_solved = TRUE;
4485
4486     TapeStop();
4487
4488     game_over_delay_1 = game_over_delay_value_1;
4489     game_over_delay_2 = game_over_delay_value_2;
4490
4491     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4492     score = score_final = local_player->score_final;
4493
4494     if (TimeLeft > 0)
4495     {
4496       time_final = 0;
4497       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4498     }
4499     else if (game.no_time_limit && TimePlayed < 999)
4500     {
4501       time_final = 999;
4502       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4503     }
4504
4505     local_player->score_final = score_final;
4506
4507     if (level_editor_test_game)
4508     {
4509       time = time_final;
4510       score = score_final;
4511
4512       local_player->LevelSolved_CountingTime = time;
4513       local_player->LevelSolved_CountingScore = score;
4514
4515       game_panel_controls[GAME_PANEL_TIME].value = time;
4516       game_panel_controls[GAME_PANEL_SCORE].value = score;
4517
4518       DisplayGameControlValues();
4519     }
4520
4521     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4522     {
4523       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4524       {
4525         /* close exit door after last player */
4526         if ((AllPlayersGone &&
4527              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4528               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4529               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4530             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4531             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4532         {
4533           int element = Feld[ExitX][ExitY];
4534
4535           Feld[ExitX][ExitY] =
4536             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4537              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4538              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4539              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4540              EL_EM_STEEL_EXIT_CLOSING);
4541
4542           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4543         }
4544
4545         /* player disappears */
4546         DrawLevelField(ExitX, ExitY);
4547       }
4548
4549       for (i = 0; i < MAX_PLAYERS; i++)
4550       {
4551         struct PlayerInfo *player = &stored_player[i];
4552
4553         if (player->present)
4554         {
4555           RemovePlayer(player);
4556
4557           /* player disappears */
4558           DrawLevelField(player->jx, player->jy);
4559         }
4560       }
4561     }
4562
4563     PlaySound(SND_GAME_WINNING);
4564   }
4565
4566   if (game_over_delay_1 > 0)
4567   {
4568     game_over_delay_1--;
4569
4570     return;
4571   }
4572
4573   if (time != time_final)
4574   {
4575     int time_to_go = ABS(time_final - time);
4576     int time_count_dir = (time < time_final ? +1 : -1);
4577     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4578
4579     time  += time_count_steps * time_count_dir;
4580     score += time_count_steps * level.score[SC_TIME_BONUS];
4581
4582     local_player->LevelSolved_CountingTime = time;
4583     local_player->LevelSolved_CountingScore = score;
4584
4585     game_panel_controls[GAME_PANEL_TIME].value = time;
4586     game_panel_controls[GAME_PANEL_SCORE].value = score;
4587
4588     DisplayGameControlValues();
4589
4590     if (time == time_final)
4591       StopSound(SND_GAME_LEVELTIME_BONUS);
4592     else if (setup.sound_loops)
4593       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4594     else
4595       PlaySound(SND_GAME_LEVELTIME_BONUS);
4596
4597     return;
4598   }
4599
4600   local_player->LevelSolved_PanelOff = TRUE;
4601
4602   if (game_over_delay_2 > 0)
4603   {
4604     game_over_delay_2--;
4605
4606     return;
4607   }
4608
4609   GameEnd();
4610 }
4611
4612 void GameEnd()
4613 {
4614   int hi_pos;
4615   boolean raise_level = FALSE;
4616
4617   local_player->LevelSolved_GameEnd = TRUE;
4618
4619   if (!global.use_envelope_request)
4620     CloseDoor(DOOR_CLOSE_1);
4621
4622   if (local_player->LevelSolved_SaveTape)
4623   {
4624     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4625   }
4626
4627   CloseDoor(DOOR_CLOSE_ALL);
4628
4629   if (level_editor_test_game)
4630   {
4631     SetGameStatus(GAME_MODE_MAIN);
4632
4633     DrawMainMenu();
4634
4635     return;
4636   }
4637
4638   if (!local_player->LevelSolved_SaveScore)
4639   {
4640     SetGameStatus(GAME_MODE_MAIN);
4641
4642     DrawMainMenu();
4643
4644     return;
4645   }
4646
4647   if (level_nr == leveldir_current->handicap_level)
4648   {
4649     leveldir_current->handicap_level++;
4650
4651     SaveLevelSetup_SeriesInfo();
4652   }
4653
4654   if (setup.increment_levels &&
4655       level_nr < leveldir_current->last_level)
4656     raise_level = TRUE;                 /* advance to next level */
4657
4658   if ((hi_pos = NewHiScore()) >= 0) 
4659   {
4660     SetGameStatus(GAME_MODE_SCORES);
4661
4662     DrawHallOfFame(hi_pos);
4663
4664     if (raise_level)
4665     {
4666       level_nr++;
4667       TapeErase();
4668     }
4669   }
4670   else
4671   {
4672     SetGameStatus(GAME_MODE_MAIN);
4673
4674     if (raise_level)
4675     {
4676       level_nr++;
4677       TapeErase();
4678     }
4679
4680     DrawMainMenu();
4681   }
4682 }
4683
4684 int NewHiScore()
4685 {
4686   int k, l;
4687   int position = -1;
4688   boolean one_score_entry_per_name = !program.many_scores_per_name;
4689
4690   LoadScore(level_nr);
4691
4692   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4693       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4694     return -1;
4695
4696   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4697   {
4698     if (local_player->score_final > highscore[k].Score)
4699     {
4700       /* player has made it to the hall of fame */
4701
4702       if (k < MAX_SCORE_ENTRIES - 1)
4703       {
4704         int m = MAX_SCORE_ENTRIES - 1;
4705
4706         if (one_score_entry_per_name)
4707         {
4708           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4709             if (strEqual(setup.player_name, highscore[l].Name))
4710               m = l;
4711
4712           if (m == k)   /* player's new highscore overwrites his old one */
4713             goto put_into_list;
4714         }
4715
4716         for (l = m; l > k; l--)
4717         {
4718           strcpy(highscore[l].Name, highscore[l - 1].Name);
4719           highscore[l].Score = highscore[l - 1].Score;
4720         }
4721       }
4722
4723       put_into_list:
4724
4725       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4726       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4727       highscore[k].Score = local_player->score_final; 
4728       position = k;
4729
4730       break;
4731     }
4732     else if (one_score_entry_per_name &&
4733              !strncmp(setup.player_name, highscore[k].Name,
4734                       MAX_PLAYER_NAME_LEN))
4735       break;    /* player already there with a higher score */
4736   }
4737
4738   if (position >= 0) 
4739     SaveScore(level_nr);
4740
4741   return position;
4742 }
4743
4744 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4745 {
4746   int element = Feld[x][y];
4747   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4748   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4749   int horiz_move = (dx != 0);
4750   int sign = (horiz_move ? dx : dy);
4751   int step = sign * element_info[element].move_stepsize;
4752
4753   /* special values for move stepsize for spring and things on conveyor belt */
4754   if (horiz_move)
4755   {
4756     if (CAN_FALL(element) &&
4757         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4758       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4759     else if (element == EL_SPRING)
4760       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4761   }
4762
4763   return step;
4764 }
4765
4766 inline static int getElementMoveStepsize(int x, int y)
4767 {
4768   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4769 }
4770
4771 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4772 {
4773   if (player->GfxAction != action || player->GfxDir != dir)
4774   {
4775     player->GfxAction = action;
4776     player->GfxDir = dir;
4777     player->Frame = 0;
4778     player->StepFrame = 0;
4779   }
4780 }
4781
4782 static void ResetGfxFrame(int x, int y)
4783 {
4784   // profiling showed that "autotest" spends 10~20% of its time in this function
4785   if (DrawingDeactivatedField())
4786     return;
4787
4788   int element = Feld[x][y];
4789   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4790
4791   if (graphic_info[graphic].anim_global_sync)
4792     GfxFrame[x][y] = FrameCounter;
4793   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4794     GfxFrame[x][y] = CustomValue[x][y];
4795   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4796     GfxFrame[x][y] = element_info[element].collect_score;
4797   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4798     GfxFrame[x][y] = ChangeDelay[x][y];
4799 }
4800
4801 static void ResetGfxAnimation(int x, int y)
4802 {
4803   GfxAction[x][y] = ACTION_DEFAULT;
4804   GfxDir[x][y] = MovDir[x][y];
4805   GfxFrame[x][y] = 0;
4806
4807   ResetGfxFrame(x, y);
4808 }
4809
4810 static void ResetRandomAnimationValue(int x, int y)
4811 {
4812   GfxRandom[x][y] = INIT_GFX_RANDOM();
4813 }
4814
4815 void InitMovingField(int x, int y, int direction)
4816 {
4817   int element = Feld[x][y];
4818   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4819   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4820   int newx = x + dx;
4821   int newy = y + dy;
4822   boolean is_moving_before, is_moving_after;
4823
4824   /* check if element was/is moving or being moved before/after mode change */
4825   is_moving_before = (WasJustMoving[x][y] != 0);
4826   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4827
4828   /* reset animation only for moving elements which change direction of moving
4829      or which just started or stopped moving
4830      (else CEs with property "can move" / "not moving" are reset each frame) */
4831   if (is_moving_before != is_moving_after ||
4832       direction != MovDir[x][y])
4833     ResetGfxAnimation(x, y);
4834
4835   MovDir[x][y] = direction;
4836   GfxDir[x][y] = direction;
4837
4838   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4839                      direction == MV_DOWN && CAN_FALL(element) ?
4840                      ACTION_FALLING : ACTION_MOVING);
4841
4842   /* this is needed for CEs with property "can move" / "not moving" */
4843
4844   if (is_moving_after)
4845   {
4846     if (Feld[newx][newy] == EL_EMPTY)
4847       Feld[newx][newy] = EL_BLOCKED;
4848
4849     MovDir[newx][newy] = MovDir[x][y];
4850
4851     CustomValue[newx][newy] = CustomValue[x][y];
4852
4853     GfxFrame[newx][newy] = GfxFrame[x][y];
4854     GfxRandom[newx][newy] = GfxRandom[x][y];
4855     GfxAction[newx][newy] = GfxAction[x][y];
4856     GfxDir[newx][newy] = GfxDir[x][y];
4857   }
4858 }
4859
4860 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4861 {
4862   int direction = MovDir[x][y];
4863   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4864   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4865
4866   *goes_to_x = newx;
4867   *goes_to_y = newy;
4868 }
4869
4870 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4871 {
4872   int oldx = x, oldy = y;
4873   int direction = MovDir[x][y];
4874
4875   if (direction == MV_LEFT)
4876     oldx++;
4877   else if (direction == MV_RIGHT)
4878     oldx--;
4879   else if (direction == MV_UP)
4880     oldy++;
4881   else if (direction == MV_DOWN)
4882     oldy--;
4883
4884   *comes_from_x = oldx;
4885   *comes_from_y = oldy;
4886 }
4887
4888 int MovingOrBlocked2Element(int x, int y)
4889 {
4890   int element = Feld[x][y];
4891
4892   if (element == EL_BLOCKED)
4893   {
4894     int oldx, oldy;
4895
4896     Blocked2Moving(x, y, &oldx, &oldy);
4897     return Feld[oldx][oldy];
4898   }
4899   else
4900     return element;
4901 }
4902
4903 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4904 {
4905   /* like MovingOrBlocked2Element(), but if element is moving
4906      and (x,y) is the field the moving element is just leaving,
4907      return EL_BLOCKED instead of the element value */
4908   int element = Feld[x][y];
4909
4910   if (IS_MOVING(x, y))
4911   {
4912     if (element == EL_BLOCKED)
4913     {
4914       int oldx, oldy;
4915
4916       Blocked2Moving(x, y, &oldx, &oldy);
4917       return Feld[oldx][oldy];
4918     }
4919     else
4920       return EL_BLOCKED;
4921   }
4922   else
4923     return element;
4924 }
4925
4926 static void RemoveField(int x, int y)
4927 {
4928   Feld[x][y] = EL_EMPTY;
4929
4930   MovPos[x][y] = 0;
4931   MovDir[x][y] = 0;
4932   MovDelay[x][y] = 0;
4933
4934   CustomValue[x][y] = 0;
4935
4936   AmoebaNr[x][y] = 0;
4937   ChangeDelay[x][y] = 0;
4938   ChangePage[x][y] = -1;
4939   Pushed[x][y] = FALSE;
4940
4941   GfxElement[x][y] = EL_UNDEFINED;
4942   GfxAction[x][y] = ACTION_DEFAULT;
4943   GfxDir[x][y] = MV_NONE;
4944 }
4945
4946 void RemoveMovingField(int x, int y)
4947 {
4948   int oldx = x, oldy = y, newx = x, newy = y;
4949   int element = Feld[x][y];
4950   int next_element = EL_UNDEFINED;
4951
4952   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4953     return;
4954
4955   if (IS_MOVING(x, y))
4956   {
4957     Moving2Blocked(x, y, &newx, &newy);
4958
4959     if (Feld[newx][newy] != EL_BLOCKED)
4960     {
4961       /* element is moving, but target field is not free (blocked), but
4962          already occupied by something different (example: acid pool);
4963          in this case, only remove the moving field, but not the target */
4964
4965       RemoveField(oldx, oldy);
4966
4967       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4968
4969       TEST_DrawLevelField(oldx, oldy);
4970
4971       return;
4972     }
4973   }
4974   else if (element == EL_BLOCKED)
4975   {
4976     Blocked2Moving(x, y, &oldx, &oldy);
4977     if (!IS_MOVING(oldx, oldy))
4978       return;
4979   }
4980
4981   if (element == EL_BLOCKED &&
4982       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4983        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4984        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4985        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4986        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4987        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4988     next_element = get_next_element(Feld[oldx][oldy]);
4989
4990   RemoveField(oldx, oldy);
4991   RemoveField(newx, newy);
4992
4993   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4994
4995   if (next_element != EL_UNDEFINED)
4996     Feld[oldx][oldy] = next_element;
4997
4998   TEST_DrawLevelField(oldx, oldy);
4999   TEST_DrawLevelField(newx, newy);
5000 }
5001
5002 void DrawDynamite(int x, int y)
5003 {
5004   int sx = SCREENX(x), sy = SCREENY(y);
5005   int graphic = el2img(Feld[x][y]);
5006   int frame;
5007
5008   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5009     return;
5010
5011   if (IS_WALKABLE_INSIDE(Back[x][y]))
5012     return;
5013
5014   if (Back[x][y])
5015     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5016   else if (Store[x][y])
5017     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5018
5019   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5020
5021   if (Back[x][y] || Store[x][y])
5022     DrawGraphicThruMask(sx, sy, graphic, frame);
5023   else
5024     DrawGraphic(sx, sy, graphic, frame);
5025 }
5026
5027 void CheckDynamite(int x, int y)
5028 {
5029   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5030   {
5031     MovDelay[x][y]--;
5032
5033     if (MovDelay[x][y] != 0)
5034     {
5035       DrawDynamite(x, y);
5036       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5037
5038       return;
5039     }
5040   }
5041
5042   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5043
5044   Bang(x, y);
5045 }
5046
5047 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5048 {
5049   boolean num_checked_players = 0;
5050   int i;
5051
5052   for (i = 0; i < MAX_PLAYERS; i++)
5053   {
5054     if (stored_player[i].active)
5055     {
5056       int sx = stored_player[i].jx;
5057       int sy = stored_player[i].jy;
5058
5059       if (num_checked_players == 0)
5060       {
5061         *sx1 = *sx2 = sx;
5062         *sy1 = *sy2 = sy;
5063       }
5064       else
5065       {
5066         *sx1 = MIN(*sx1, sx);
5067         *sy1 = MIN(*sy1, sy);
5068         *sx2 = MAX(*sx2, sx);
5069         *sy2 = MAX(*sy2, sy);
5070       }
5071
5072       num_checked_players++;
5073     }
5074   }
5075 }
5076
5077 static boolean checkIfAllPlayersFitToScreen_RND()
5078 {
5079   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5080
5081   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5082
5083   return (sx2 - sx1 < SCR_FIELDX &&
5084           sy2 - sy1 < SCR_FIELDY);
5085 }
5086
5087 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5088 {
5089   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5090
5091   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5092
5093   *sx = (sx1 + sx2) / 2;
5094   *sy = (sy1 + sy2) / 2;
5095 }
5096
5097 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5098                         boolean center_screen, boolean quick_relocation)
5099 {
5100   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5101   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5102   boolean no_delay = (tape.warp_forward);
5103   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5104   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5105   int new_scroll_x, new_scroll_y;
5106
5107   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5108   {
5109     /* case 1: quick relocation inside visible screen (without scrolling) */
5110
5111     RedrawPlayfield();
5112
5113     return;
5114   }
5115
5116   if (!level.shifted_relocation || center_screen)
5117   {
5118     /* relocation _with_ centering of screen */
5119
5120     new_scroll_x = SCROLL_POSITION_X(x);
5121     new_scroll_y = SCROLL_POSITION_Y(y);
5122   }
5123   else
5124   {
5125     /* relocation _without_ centering of screen */
5126
5127     int center_scroll_x = SCROLL_POSITION_X(old_x);
5128     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5129     int offset_x = x + (scroll_x - center_scroll_x);
5130     int offset_y = y + (scroll_y - center_scroll_y);
5131
5132     /* for new screen position, apply previous offset to center position */
5133     new_scroll_x = SCROLL_POSITION_X(offset_x);
5134     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5135   }
5136
5137   if (quick_relocation)
5138   {
5139     /* case 2: quick relocation (redraw without visible scrolling) */
5140
5141     scroll_x = new_scroll_x;
5142     scroll_y = new_scroll_y;
5143
5144     RedrawPlayfield();
5145
5146     return;
5147   }
5148
5149   /* case 3: visible relocation (with scrolling to new position) */
5150
5151   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5152
5153   SetVideoFrameDelay(wait_delay_value);
5154
5155   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5156   {
5157     int dx = 0, dy = 0;
5158     int fx = FX, fy = FY;
5159
5160     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5161     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5162
5163     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5164       break;
5165
5166     scroll_x -= dx;
5167     scroll_y -= dy;
5168
5169     fx += dx * TILEX / 2;
5170     fy += dy * TILEY / 2;
5171
5172     ScrollLevel(dx, dy);
5173     DrawAllPlayers();
5174
5175     /* scroll in two steps of half tile size to make things smoother */
5176     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5177
5178     /* scroll second step to align at full tile size */
5179     BlitScreenToBitmap(window);
5180   }
5181
5182   DrawAllPlayers();
5183   BackToFront();
5184
5185   SetVideoFrameDelay(frame_delay_value_old);
5186 }
5187
5188 void RelocatePlayer(int jx, int jy, int el_player_raw)
5189 {
5190   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5191   int player_nr = GET_PLAYER_NR(el_player);
5192   struct PlayerInfo *player = &stored_player[player_nr];
5193   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5194   boolean no_delay = (tape.warp_forward);
5195   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5196   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5197   int old_jx = player->jx;
5198   int old_jy = player->jy;
5199   int old_element = Feld[old_jx][old_jy];
5200   int element = Feld[jx][jy];
5201   boolean player_relocated = (old_jx != jx || old_jy != jy);
5202
5203   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5204   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5205   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5206   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5207   int leave_side_horiz = move_dir_horiz;
5208   int leave_side_vert  = move_dir_vert;
5209   int enter_side = enter_side_horiz | enter_side_vert;
5210   int leave_side = leave_side_horiz | leave_side_vert;
5211
5212   if (player->GameOver)         /* do not reanimate dead player */
5213     return;
5214
5215   if (!player_relocated)        /* no need to relocate the player */
5216     return;
5217
5218   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5219   {
5220     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5221     DrawLevelField(jx, jy);
5222   }
5223
5224   if (player->present)
5225   {
5226     while (player->MovPos)
5227     {
5228       ScrollPlayer(player, SCROLL_GO_ON);
5229       ScrollScreen(NULL, SCROLL_GO_ON);
5230
5231       AdvanceFrameAndPlayerCounters(player->index_nr);
5232
5233       DrawPlayer(player);
5234
5235       BackToFront_WithFrameDelay(wait_delay_value);
5236     }
5237
5238     DrawPlayer(player);         /* needed here only to cleanup last field */
5239     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5240
5241     player->is_moving = FALSE;
5242   }
5243
5244   if (IS_CUSTOM_ELEMENT(old_element))
5245     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5246                                CE_LEFT_BY_PLAYER,
5247                                player->index_bit, leave_side);
5248
5249   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5250                                       CE_PLAYER_LEAVES_X,
5251                                       player->index_bit, leave_side);
5252
5253   Feld[jx][jy] = el_player;
5254   InitPlayerField(jx, jy, el_player, TRUE);
5255
5256   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5257      possible that the relocation target field did not contain a player element,
5258      but a walkable element, to which the new player was relocated -- in this
5259      case, restore that (already initialized!) element on the player field */
5260   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5261   {
5262     Feld[jx][jy] = element;     /* restore previously existing element */
5263   }
5264
5265   /* only visually relocate centered player */
5266   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5267                      FALSE, level.instant_relocation);
5268
5269   TestIfPlayerTouchesBadThing(jx, jy);
5270   TestIfPlayerTouchesCustomElement(jx, jy);
5271
5272   if (IS_CUSTOM_ELEMENT(element))
5273     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5274                                player->index_bit, enter_side);
5275
5276   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5277                                       player->index_bit, enter_side);
5278
5279   if (player->is_switching)
5280   {
5281     /* ensure that relocation while still switching an element does not cause
5282        a new element to be treated as also switched directly after relocation
5283        (this is important for teleporter switches that teleport the player to
5284        a place where another teleporter switch is in the same direction, which
5285        would then incorrectly be treated as immediately switched before the
5286        direction key that caused the switch was released) */
5287
5288     player->switch_x += jx - old_jx;
5289     player->switch_y += jy - old_jy;
5290   }
5291 }
5292
5293 void Explode(int ex, int ey, int phase, int mode)
5294 {
5295   int x, y;
5296   int last_phase;
5297   int border_element;
5298
5299   /* !!! eliminate this variable !!! */
5300   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5301
5302   if (game.explosions_delayed)
5303   {
5304     ExplodeField[ex][ey] = mode;
5305     return;
5306   }
5307
5308   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5309   {
5310     int center_element = Feld[ex][ey];
5311     int artwork_element, explosion_element;     /* set these values later */
5312
5313     /* remove things displayed in background while burning dynamite */
5314     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5315       Back[ex][ey] = 0;
5316
5317     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5318     {
5319       /* put moving element to center field (and let it explode there) */
5320       center_element = MovingOrBlocked2Element(ex, ey);
5321       RemoveMovingField(ex, ey);
5322       Feld[ex][ey] = center_element;
5323     }
5324
5325     /* now "center_element" is finally determined -- set related values now */
5326     artwork_element = center_element;           /* for custom player artwork */
5327     explosion_element = center_element;         /* for custom player artwork */
5328
5329     if (IS_PLAYER(ex, ey))
5330     {
5331       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5332
5333       artwork_element = stored_player[player_nr].artwork_element;
5334
5335       if (level.use_explosion_element[player_nr])
5336       {
5337         explosion_element = level.explosion_element[player_nr];
5338         artwork_element = explosion_element;
5339       }
5340     }
5341
5342     if (mode == EX_TYPE_NORMAL ||
5343         mode == EX_TYPE_CENTER ||
5344         mode == EX_TYPE_CROSS)
5345       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5346
5347     last_phase = element_info[explosion_element].explosion_delay + 1;
5348
5349     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5350     {
5351       int xx = x - ex + 1;
5352       int yy = y - ey + 1;
5353       int element;
5354
5355       if (!IN_LEV_FIELD(x, y) ||
5356           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5357           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5358         continue;
5359
5360       element = Feld[x][y];
5361
5362       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5363       {
5364         element = MovingOrBlocked2Element(x, y);
5365
5366         if (!IS_EXPLOSION_PROOF(element))
5367           RemoveMovingField(x, y);
5368       }
5369
5370       /* indestructible elements can only explode in center (but not flames) */
5371       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5372                                            mode == EX_TYPE_BORDER)) ||
5373           element == EL_FLAMES)
5374         continue;
5375
5376       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5377          behaviour, for example when touching a yamyam that explodes to rocks
5378          with active deadly shield, a rock is created under the player !!! */
5379       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5380 #if 0
5381       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5382           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5383            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5384 #else
5385       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5386 #endif
5387       {
5388         if (IS_ACTIVE_BOMB(element))
5389         {
5390           /* re-activate things under the bomb like gate or penguin */
5391           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5392           Back[x][y] = 0;
5393         }
5394
5395         continue;
5396       }
5397
5398       /* save walkable background elements while explosion on same tile */
5399       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5400           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5401         Back[x][y] = element;
5402
5403       /* ignite explodable elements reached by other explosion */
5404       if (element == EL_EXPLOSION)
5405         element = Store2[x][y];
5406
5407       if (AmoebaNr[x][y] &&
5408           (element == EL_AMOEBA_FULL ||
5409            element == EL_BD_AMOEBA ||
5410            element == EL_AMOEBA_GROWING))
5411       {
5412         AmoebaCnt[AmoebaNr[x][y]]--;
5413         AmoebaCnt2[AmoebaNr[x][y]]--;
5414       }
5415
5416       RemoveField(x, y);
5417
5418       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5419       {
5420         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5421
5422         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5423
5424         if (PLAYERINFO(ex, ey)->use_murphy)
5425           Store[x][y] = EL_EMPTY;
5426       }
5427
5428       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5429          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5430       else if (ELEM_IS_PLAYER(center_element))
5431         Store[x][y] = EL_EMPTY;
5432       else if (center_element == EL_YAMYAM)
5433         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5434       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5435         Store[x][y] = element_info[center_element].content.e[xx][yy];
5436 #if 1
5437       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5438          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5439          otherwise) -- FIX THIS !!! */
5440       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5441         Store[x][y] = element_info[element].content.e[1][1];
5442 #else
5443       else if (!CAN_EXPLODE(element))
5444         Store[x][y] = element_info[element].content.e[1][1];
5445 #endif
5446       else
5447         Store[x][y] = EL_EMPTY;
5448
5449       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5450           center_element == EL_AMOEBA_TO_DIAMOND)
5451         Store2[x][y] = element;
5452
5453       Feld[x][y] = EL_EXPLOSION;
5454       GfxElement[x][y] = artwork_element;
5455
5456       ExplodePhase[x][y] = 1;
5457       ExplodeDelay[x][y] = last_phase;
5458
5459       Stop[x][y] = TRUE;
5460     }
5461
5462     if (center_element == EL_YAMYAM)
5463       game.yamyam_content_nr =
5464         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5465
5466     return;
5467   }
5468
5469   if (Stop[ex][ey])
5470     return;
5471
5472   x = ex;
5473   y = ey;
5474
5475   if (phase == 1)
5476     GfxFrame[x][y] = 0;         /* restart explosion animation */
5477
5478   last_phase = ExplodeDelay[x][y];
5479
5480   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5481
5482   /* this can happen if the player leaves an explosion just in time */
5483   if (GfxElement[x][y] == EL_UNDEFINED)
5484     GfxElement[x][y] = EL_EMPTY;
5485
5486   border_element = Store2[x][y];
5487   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5488     border_element = StorePlayer[x][y];
5489
5490   if (phase == element_info[border_element].ignition_delay ||
5491       phase == last_phase)
5492   {
5493     boolean border_explosion = FALSE;
5494
5495     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5496         !PLAYER_EXPLOSION_PROTECTED(x, y))
5497     {
5498       KillPlayerUnlessExplosionProtected(x, y);
5499       border_explosion = TRUE;
5500     }
5501     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5502     {
5503       Feld[x][y] = Store2[x][y];
5504       Store2[x][y] = 0;
5505       Bang(x, y);
5506       border_explosion = TRUE;
5507     }
5508     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5509     {
5510       AmoebeUmwandeln(x, y);
5511       Store2[x][y] = 0;
5512       border_explosion = TRUE;
5513     }
5514
5515     /* if an element just explodes due to another explosion (chain-reaction),
5516        do not immediately end the new explosion when it was the last frame of
5517        the explosion (as it would be done in the following "if"-statement!) */
5518     if (border_explosion && phase == last_phase)
5519       return;
5520   }
5521
5522   if (phase == last_phase)
5523   {
5524     int element;
5525
5526     element = Feld[x][y] = Store[x][y];
5527     Store[x][y] = Store2[x][y] = 0;
5528     GfxElement[x][y] = EL_UNDEFINED;
5529
5530     /* player can escape from explosions and might therefore be still alive */
5531     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5532         element <= EL_PLAYER_IS_EXPLODING_4)
5533     {
5534       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5535       int explosion_element = EL_PLAYER_1 + player_nr;
5536       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5537       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5538
5539       if (level.use_explosion_element[player_nr])
5540         explosion_element = level.explosion_element[player_nr];
5541
5542       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5543                     element_info[explosion_element].content.e[xx][yy]);
5544     }
5545
5546     /* restore probably existing indestructible background element */
5547     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5548       element = Feld[x][y] = Back[x][y];
5549     Back[x][y] = 0;
5550
5551     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5552     GfxDir[x][y] = MV_NONE;
5553     ChangeDelay[x][y] = 0;
5554     ChangePage[x][y] = -1;
5555
5556     CustomValue[x][y] = 0;
5557
5558     InitField_WithBug2(x, y, FALSE);
5559
5560     TEST_DrawLevelField(x, y);
5561
5562     TestIfElementTouchesCustomElement(x, y);
5563
5564     if (GFX_CRUMBLED(element))
5565       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5566
5567     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5568       StorePlayer[x][y] = 0;
5569
5570     if (ELEM_IS_PLAYER(element))
5571       RelocatePlayer(x, y, element);
5572   }
5573   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5574   {
5575     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5576     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5577
5578     if (phase == delay)
5579       TEST_DrawLevelFieldCrumbled(x, y);
5580
5581     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5582     {
5583       DrawLevelElement(x, y, Back[x][y]);
5584       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5585     }
5586     else if (IS_WALKABLE_UNDER(Back[x][y]))
5587     {
5588       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5589       DrawLevelElementThruMask(x, y, Back[x][y]);
5590     }
5591     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5592       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5593   }
5594 }
5595
5596 void DynaExplode(int ex, int ey)
5597 {
5598   int i, j;
5599   int dynabomb_element = Feld[ex][ey];
5600   int dynabomb_size = 1;
5601   boolean dynabomb_xl = FALSE;
5602   struct PlayerInfo *player;
5603   static int xy[4][2] =
5604   {
5605     { 0, -1 },
5606     { -1, 0 },
5607     { +1, 0 },
5608     { 0, +1 }
5609   };
5610
5611   if (IS_ACTIVE_BOMB(dynabomb_element))
5612   {
5613     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5614     dynabomb_size = player->dynabomb_size;
5615     dynabomb_xl = player->dynabomb_xl;
5616     player->dynabombs_left++;
5617   }
5618
5619   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5620
5621   for (i = 0; i < NUM_DIRECTIONS; i++)
5622   {
5623     for (j = 1; j <= dynabomb_size; j++)
5624     {
5625       int x = ex + j * xy[i][0];
5626       int y = ey + j * xy[i][1];
5627       int element;
5628
5629       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5630         break;
5631
5632       element = Feld[x][y];
5633
5634       /* do not restart explosions of fields with active bombs */
5635       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5636         continue;
5637
5638       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5639
5640       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5641           !IS_DIGGABLE(element) && !dynabomb_xl)
5642         break;
5643     }
5644   }
5645 }
5646
5647 void Bang(int x, int y)
5648 {
5649   int element = MovingOrBlocked2Element(x, y);
5650   int explosion_type = EX_TYPE_NORMAL;
5651
5652   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5653   {
5654     struct PlayerInfo *player = PLAYERINFO(x, y);
5655
5656     element = Feld[x][y] = player->initial_element;
5657
5658     if (level.use_explosion_element[player->index_nr])
5659     {
5660       int explosion_element = level.explosion_element[player->index_nr];
5661
5662       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5663         explosion_type = EX_TYPE_CROSS;
5664       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5665         explosion_type = EX_TYPE_CENTER;
5666     }
5667   }
5668
5669   switch (element)
5670   {
5671     case EL_BUG:
5672     case EL_SPACESHIP:
5673     case EL_BD_BUTTERFLY:
5674     case EL_BD_FIREFLY:
5675     case EL_YAMYAM:
5676     case EL_DARK_YAMYAM:
5677     case EL_ROBOT:
5678     case EL_PACMAN:
5679     case EL_MOLE:
5680       RaiseScoreElement(element);
5681       break;
5682
5683     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5684     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5685     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5686     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5687     case EL_DYNABOMB_INCREASE_NUMBER:
5688     case EL_DYNABOMB_INCREASE_SIZE:
5689     case EL_DYNABOMB_INCREASE_POWER:
5690       explosion_type = EX_TYPE_DYNA;
5691       break;
5692
5693     case EL_DC_LANDMINE:
5694       explosion_type = EX_TYPE_CENTER;
5695       break;
5696
5697     case EL_PENGUIN:
5698     case EL_LAMP:
5699     case EL_LAMP_ACTIVE:
5700     case EL_AMOEBA_TO_DIAMOND:
5701       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5702         explosion_type = EX_TYPE_CENTER;
5703       break;
5704
5705     default:
5706       if (element_info[element].explosion_type == EXPLODES_CROSS)
5707         explosion_type = EX_TYPE_CROSS;
5708       else if (element_info[element].explosion_type == EXPLODES_1X1)
5709         explosion_type = EX_TYPE_CENTER;
5710       break;
5711   }
5712
5713   if (explosion_type == EX_TYPE_DYNA)
5714     DynaExplode(x, y);
5715   else
5716     Explode(x, y, EX_PHASE_START, explosion_type);
5717
5718   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5719 }
5720
5721 void SplashAcid(int x, int y)
5722 {
5723   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5724       (!IN_LEV_FIELD(x - 1, y - 2) ||
5725        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5726     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5727
5728   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5729       (!IN_LEV_FIELD(x + 1, y - 2) ||
5730        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5731     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5732
5733   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5734 }
5735
5736 static void InitBeltMovement()
5737 {
5738   static int belt_base_element[4] =
5739   {
5740     EL_CONVEYOR_BELT_1_LEFT,
5741     EL_CONVEYOR_BELT_2_LEFT,
5742     EL_CONVEYOR_BELT_3_LEFT,
5743     EL_CONVEYOR_BELT_4_LEFT
5744   };
5745   static int belt_base_active_element[4] =
5746   {
5747     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5748     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5749     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5750     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5751   };
5752
5753   int x, y, i, j;
5754
5755   /* set frame order for belt animation graphic according to belt direction */
5756   for (i = 0; i < NUM_BELTS; i++)
5757   {
5758     int belt_nr = i;
5759
5760     for (j = 0; j < NUM_BELT_PARTS; j++)
5761     {
5762       int element = belt_base_active_element[belt_nr] + j;
5763       int graphic_1 = el2img(element);
5764       int graphic_2 = el2panelimg(element);
5765
5766       if (game.belt_dir[i] == MV_LEFT)
5767       {
5768         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5769         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5770       }
5771       else
5772       {
5773         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5774         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5775       }
5776     }
5777   }
5778
5779   SCAN_PLAYFIELD(x, y)
5780   {
5781     int element = Feld[x][y];
5782
5783     for (i = 0; i < NUM_BELTS; i++)
5784     {
5785       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5786       {
5787         int e_belt_nr = getBeltNrFromBeltElement(element);
5788         int belt_nr = i;
5789
5790         if (e_belt_nr == belt_nr)
5791         {
5792           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5793
5794           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5795         }
5796       }
5797     }
5798   }
5799 }
5800
5801 static void ToggleBeltSwitch(int x, int y)
5802 {
5803   static int belt_base_element[4] =
5804   {
5805     EL_CONVEYOR_BELT_1_LEFT,
5806     EL_CONVEYOR_BELT_2_LEFT,
5807     EL_CONVEYOR_BELT_3_LEFT,
5808     EL_CONVEYOR_BELT_4_LEFT
5809   };
5810   static int belt_base_active_element[4] =
5811   {
5812     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5813     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5814     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5815     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5816   };
5817   static int belt_base_switch_element[4] =
5818   {
5819     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5820     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5821     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5822     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5823   };
5824   static int belt_move_dir[4] =
5825   {
5826     MV_LEFT,
5827     MV_NONE,
5828     MV_RIGHT,
5829     MV_NONE,
5830   };
5831
5832   int element = Feld[x][y];
5833   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5834   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5835   int belt_dir = belt_move_dir[belt_dir_nr];
5836   int xx, yy, i;
5837
5838   if (!IS_BELT_SWITCH(element))
5839     return;
5840
5841   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5842   game.belt_dir[belt_nr] = belt_dir;
5843
5844   if (belt_dir_nr == 3)
5845     belt_dir_nr = 1;
5846
5847   /* set frame order for belt animation graphic according to belt direction */
5848   for (i = 0; i < NUM_BELT_PARTS; i++)
5849   {
5850     int element = belt_base_active_element[belt_nr] + i;
5851     int graphic_1 = el2img(element);
5852     int graphic_2 = el2panelimg(element);
5853
5854     if (belt_dir == MV_LEFT)
5855     {
5856       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5857       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5858     }
5859     else
5860     {
5861       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5862       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5863     }
5864   }
5865
5866   SCAN_PLAYFIELD(xx, yy)
5867   {
5868     int element = Feld[xx][yy];
5869
5870     if (IS_BELT_SWITCH(element))
5871     {
5872       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5873
5874       if (e_belt_nr == belt_nr)
5875       {
5876         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5877         TEST_DrawLevelField(xx, yy);
5878       }
5879     }
5880     else if (IS_BELT(element) && belt_dir != MV_NONE)
5881     {
5882       int e_belt_nr = getBeltNrFromBeltElement(element);
5883
5884       if (e_belt_nr == belt_nr)
5885       {
5886         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5887
5888         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5889         TEST_DrawLevelField(xx, yy);
5890       }
5891     }
5892     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5893     {
5894       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5895
5896       if (e_belt_nr == belt_nr)
5897       {
5898         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5899
5900         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5901         TEST_DrawLevelField(xx, yy);
5902       }
5903     }
5904   }
5905 }
5906
5907 static void ToggleSwitchgateSwitch(int x, int y)
5908 {
5909   int xx, yy;
5910
5911   game.switchgate_pos = !game.switchgate_pos;
5912
5913   SCAN_PLAYFIELD(xx, yy)
5914   {
5915     int element = Feld[xx][yy];
5916
5917     if (element == EL_SWITCHGATE_SWITCH_UP)
5918     {
5919       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5920       TEST_DrawLevelField(xx, yy);
5921     }
5922     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5923     {
5924       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5925       TEST_DrawLevelField(xx, yy);
5926     }
5927     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5928     {
5929       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5930       TEST_DrawLevelField(xx, yy);
5931     }
5932     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5933     {
5934       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5935       TEST_DrawLevelField(xx, yy);
5936     }
5937     else if (element == EL_SWITCHGATE_OPEN ||
5938              element == EL_SWITCHGATE_OPENING)
5939     {
5940       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5941
5942       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5943     }
5944     else if (element == EL_SWITCHGATE_CLOSED ||
5945              element == EL_SWITCHGATE_CLOSING)
5946     {
5947       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5948
5949       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5950     }
5951   }
5952 }
5953
5954 static int getInvisibleActiveFromInvisibleElement(int element)
5955 {
5956   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5957           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5958           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5959           element);
5960 }
5961
5962 static int getInvisibleFromInvisibleActiveElement(int element)
5963 {
5964   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5965           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5966           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5967           element);
5968 }
5969
5970 static void RedrawAllLightSwitchesAndInvisibleElements()
5971 {
5972   int x, y;
5973
5974   SCAN_PLAYFIELD(x, y)
5975   {
5976     int element = Feld[x][y];
5977
5978     if (element == EL_LIGHT_SWITCH &&
5979         game.light_time_left > 0)
5980     {
5981       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5982       TEST_DrawLevelField(x, y);
5983     }
5984     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5985              game.light_time_left == 0)
5986     {
5987       Feld[x][y] = EL_LIGHT_SWITCH;
5988       TEST_DrawLevelField(x, y);
5989     }
5990     else if (element == EL_EMC_DRIPPER &&
5991              game.light_time_left > 0)
5992     {
5993       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5994       TEST_DrawLevelField(x, y);
5995     }
5996     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5997              game.light_time_left == 0)
5998     {
5999       Feld[x][y] = EL_EMC_DRIPPER;
6000       TEST_DrawLevelField(x, y);
6001     }
6002     else if (element == EL_INVISIBLE_STEELWALL ||
6003              element == EL_INVISIBLE_WALL ||
6004              element == EL_INVISIBLE_SAND)
6005     {
6006       if (game.light_time_left > 0)
6007         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6008
6009       TEST_DrawLevelField(x, y);
6010
6011       /* uncrumble neighbour fields, if needed */
6012       if (element == EL_INVISIBLE_SAND)
6013         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6014     }
6015     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6016              element == EL_INVISIBLE_WALL_ACTIVE ||
6017              element == EL_INVISIBLE_SAND_ACTIVE)
6018     {
6019       if (game.light_time_left == 0)
6020         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6021
6022       TEST_DrawLevelField(x, y);
6023
6024       /* re-crumble neighbour fields, if needed */
6025       if (element == EL_INVISIBLE_SAND)
6026         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6027     }
6028   }
6029 }
6030
6031 static void RedrawAllInvisibleElementsForLenses()
6032 {
6033   int x, y;
6034
6035   SCAN_PLAYFIELD(x, y)
6036   {
6037     int element = Feld[x][y];
6038
6039     if (element == EL_EMC_DRIPPER &&
6040         game.lenses_time_left > 0)
6041     {
6042       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6043       TEST_DrawLevelField(x, y);
6044     }
6045     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6046              game.lenses_time_left == 0)
6047     {
6048       Feld[x][y] = EL_EMC_DRIPPER;
6049       TEST_DrawLevelField(x, y);
6050     }
6051     else if (element == EL_INVISIBLE_STEELWALL ||
6052              element == EL_INVISIBLE_WALL ||
6053              element == EL_INVISIBLE_SAND)
6054     {
6055       if (game.lenses_time_left > 0)
6056         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6057
6058       TEST_DrawLevelField(x, y);
6059
6060       /* uncrumble neighbour fields, if needed */
6061       if (element == EL_INVISIBLE_SAND)
6062         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6063     }
6064     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6065              element == EL_INVISIBLE_WALL_ACTIVE ||
6066              element == EL_INVISIBLE_SAND_ACTIVE)
6067     {
6068       if (game.lenses_time_left == 0)
6069         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6070
6071       TEST_DrawLevelField(x, y);
6072
6073       /* re-crumble neighbour fields, if needed */
6074       if (element == EL_INVISIBLE_SAND)
6075         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6076     }
6077   }
6078 }
6079
6080 static void RedrawAllInvisibleElementsForMagnifier()
6081 {
6082   int x, y;
6083
6084   SCAN_PLAYFIELD(x, y)
6085   {
6086     int element = Feld[x][y];
6087
6088     if (element == EL_EMC_FAKE_GRASS &&
6089         game.magnify_time_left > 0)
6090     {
6091       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6092       TEST_DrawLevelField(x, y);
6093     }
6094     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6095              game.magnify_time_left == 0)
6096     {
6097       Feld[x][y] = EL_EMC_FAKE_GRASS;
6098       TEST_DrawLevelField(x, y);
6099     }
6100     else if (IS_GATE_GRAY(element) &&
6101              game.magnify_time_left > 0)
6102     {
6103       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6104                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6105                     IS_EM_GATE_GRAY(element) ?
6106                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6107                     IS_EMC_GATE_GRAY(element) ?
6108                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6109                     IS_DC_GATE_GRAY(element) ?
6110                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6111                     element);
6112       TEST_DrawLevelField(x, y);
6113     }
6114     else if (IS_GATE_GRAY_ACTIVE(element) &&
6115              game.magnify_time_left == 0)
6116     {
6117       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6118                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6119                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6120                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6121                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6122                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6123                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6124                     EL_DC_GATE_WHITE_GRAY :
6125                     element);
6126       TEST_DrawLevelField(x, y);
6127     }
6128   }
6129 }
6130
6131 static void ToggleLightSwitch(int x, int y)
6132 {
6133   int element = Feld[x][y];
6134
6135   game.light_time_left =
6136     (element == EL_LIGHT_SWITCH ?
6137      level.time_light * FRAMES_PER_SECOND : 0);
6138
6139   RedrawAllLightSwitchesAndInvisibleElements();
6140 }
6141
6142 static void ActivateTimegateSwitch(int x, int y)
6143 {
6144   int xx, yy;
6145
6146   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6147
6148   SCAN_PLAYFIELD(xx, yy)
6149   {
6150     int element = Feld[xx][yy];
6151
6152     if (element == EL_TIMEGATE_CLOSED ||
6153         element == EL_TIMEGATE_CLOSING)
6154     {
6155       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6156       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6157     }
6158
6159     /*
6160     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6161     {
6162       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6163       TEST_DrawLevelField(xx, yy);
6164     }
6165     */
6166
6167   }
6168
6169   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6170                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6171 }
6172
6173 void Impact(int x, int y)
6174 {
6175   boolean last_line = (y == lev_fieldy - 1);
6176   boolean object_hit = FALSE;
6177   boolean impact = (last_line || object_hit);
6178   int element = Feld[x][y];
6179   int smashed = EL_STEELWALL;
6180
6181   if (!last_line)       /* check if element below was hit */
6182   {
6183     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6184       return;
6185
6186     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6187                                          MovDir[x][y + 1] != MV_DOWN ||
6188                                          MovPos[x][y + 1] <= TILEY / 2));
6189
6190     /* do not smash moving elements that left the smashed field in time */
6191     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6192         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6193       object_hit = FALSE;
6194
6195 #if USE_QUICKSAND_IMPACT_BUGFIX
6196     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6197     {
6198       RemoveMovingField(x, y + 1);
6199       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6200       Feld[x][y + 2] = EL_ROCK;
6201       TEST_DrawLevelField(x, y + 2);
6202
6203       object_hit = TRUE;
6204     }
6205
6206     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6207     {
6208       RemoveMovingField(x, y + 1);
6209       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6210       Feld[x][y + 2] = EL_ROCK;
6211       TEST_DrawLevelField(x, y + 2);
6212
6213       object_hit = TRUE;
6214     }
6215 #endif
6216
6217     if (object_hit)
6218       smashed = MovingOrBlocked2Element(x, y + 1);
6219
6220     impact = (last_line || object_hit);
6221   }
6222
6223   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6224   {
6225     SplashAcid(x, y + 1);
6226     return;
6227   }
6228
6229   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6230   /* only reset graphic animation if graphic really changes after impact */
6231   if (impact &&
6232       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6233   {
6234     ResetGfxAnimation(x, y);
6235     TEST_DrawLevelField(x, y);
6236   }
6237
6238   if (impact && CAN_EXPLODE_IMPACT(element))
6239   {
6240     Bang(x, y);
6241     return;
6242   }
6243   else if (impact && element == EL_PEARL &&
6244            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6245   {
6246     ResetGfxAnimation(x, y);
6247
6248     Feld[x][y] = EL_PEARL_BREAKING;
6249     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6250     return;
6251   }
6252   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6253   {
6254     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6255
6256     return;
6257   }
6258
6259   if (impact && element == EL_AMOEBA_DROP)
6260   {
6261     if (object_hit && IS_PLAYER(x, y + 1))
6262       KillPlayerUnlessEnemyProtected(x, y + 1);
6263     else if (object_hit && smashed == EL_PENGUIN)
6264       Bang(x, y + 1);
6265     else
6266     {
6267       Feld[x][y] = EL_AMOEBA_GROWING;
6268       Store[x][y] = EL_AMOEBA_WET;
6269
6270       ResetRandomAnimationValue(x, y);
6271     }
6272     return;
6273   }
6274
6275   if (object_hit)               /* check which object was hit */
6276   {
6277     if ((CAN_PASS_MAGIC_WALL(element) && 
6278          (smashed == EL_MAGIC_WALL ||
6279           smashed == EL_BD_MAGIC_WALL)) ||
6280         (CAN_PASS_DC_MAGIC_WALL(element) &&
6281          smashed == EL_DC_MAGIC_WALL))
6282     {
6283       int xx, yy;
6284       int activated_magic_wall =
6285         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6286          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6287          EL_DC_MAGIC_WALL_ACTIVE);
6288
6289       /* activate magic wall / mill */
6290       SCAN_PLAYFIELD(xx, yy)
6291       {
6292         if (Feld[xx][yy] == smashed)
6293           Feld[xx][yy] = activated_magic_wall;
6294       }
6295
6296       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6297       game.magic_wall_active = TRUE;
6298
6299       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6300                             SND_MAGIC_WALL_ACTIVATING :
6301                             smashed == EL_BD_MAGIC_WALL ?
6302                             SND_BD_MAGIC_WALL_ACTIVATING :
6303                             SND_DC_MAGIC_WALL_ACTIVATING));
6304     }
6305
6306     if (IS_PLAYER(x, y + 1))
6307     {
6308       if (CAN_SMASH_PLAYER(element))
6309       {
6310         KillPlayerUnlessEnemyProtected(x, y + 1);
6311         return;
6312       }
6313     }
6314     else if (smashed == EL_PENGUIN)
6315     {
6316       if (CAN_SMASH_PLAYER(element))
6317       {
6318         Bang(x, y + 1);
6319         return;
6320       }
6321     }
6322     else if (element == EL_BD_DIAMOND)
6323     {
6324       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6325       {
6326         Bang(x, y + 1);
6327         return;
6328       }
6329     }
6330     else if (((element == EL_SP_INFOTRON ||
6331                element == EL_SP_ZONK) &&
6332               (smashed == EL_SP_SNIKSNAK ||
6333                smashed == EL_SP_ELECTRON ||
6334                smashed == EL_SP_DISK_ORANGE)) ||
6335              (element == EL_SP_INFOTRON &&
6336               smashed == EL_SP_DISK_YELLOW))
6337     {
6338       Bang(x, y + 1);
6339       return;
6340     }
6341     else if (CAN_SMASH_EVERYTHING(element))
6342     {
6343       if (IS_CLASSIC_ENEMY(smashed) ||
6344           CAN_EXPLODE_SMASHED(smashed))
6345       {
6346         Bang(x, y + 1);
6347         return;
6348       }
6349       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6350       {
6351         if (smashed == EL_LAMP ||
6352             smashed == EL_LAMP_ACTIVE)
6353         {
6354           Bang(x, y + 1);
6355           return;
6356         }
6357         else if (smashed == EL_NUT)
6358         {
6359           Feld[x][y + 1] = EL_NUT_BREAKING;
6360           PlayLevelSound(x, y, SND_NUT_BREAKING);
6361           RaiseScoreElement(EL_NUT);
6362           return;
6363         }
6364         else if (smashed == EL_PEARL)
6365         {
6366           ResetGfxAnimation(x, y);
6367
6368           Feld[x][y + 1] = EL_PEARL_BREAKING;
6369           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6370           return;
6371         }
6372         else if (smashed == EL_DIAMOND)
6373         {
6374           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6375           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6376           return;
6377         }
6378         else if (IS_BELT_SWITCH(smashed))
6379         {
6380           ToggleBeltSwitch(x, y + 1);
6381         }
6382         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6383                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6384                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6385                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6386         {
6387           ToggleSwitchgateSwitch(x, y + 1);
6388         }
6389         else if (smashed == EL_LIGHT_SWITCH ||
6390                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6391         {
6392           ToggleLightSwitch(x, y + 1);
6393         }
6394         else
6395         {
6396           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6397
6398           CheckElementChangeBySide(x, y + 1, smashed, element,
6399                                    CE_SWITCHED, CH_SIDE_TOP);
6400           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6401                                             CH_SIDE_TOP);
6402         }
6403       }
6404       else
6405       {
6406         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6407       }
6408     }
6409   }
6410
6411   /* play sound of magic wall / mill */
6412   if (!last_line &&
6413       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6414        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6415        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6416   {
6417     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6418       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6419     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6420       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6421     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6422       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6423
6424     return;
6425   }
6426
6427   /* play sound of object that hits the ground */
6428   if (last_line || object_hit)
6429     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6430 }
6431
6432 inline static void TurnRoundExt(int x, int y)
6433 {
6434   static struct
6435   {
6436     int dx, dy;
6437   } move_xy[] =
6438   {
6439     {  0,  0 },
6440     { -1,  0 },
6441     { +1,  0 },
6442     {  0,  0 },
6443     {  0, -1 },
6444     {  0,  0 }, { 0, 0 }, { 0, 0 },
6445     {  0, +1 }
6446   };
6447   static struct
6448   {
6449     int left, right, back;
6450   } turn[] =
6451   {
6452     { 0,        0,              0        },
6453     { MV_DOWN,  MV_UP,          MV_RIGHT },
6454     { MV_UP,    MV_DOWN,        MV_LEFT  },
6455     { 0,        0,              0        },
6456     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6457     { 0,        0,              0        },
6458     { 0,        0,              0        },
6459     { 0,        0,              0        },
6460     { MV_RIGHT, MV_LEFT,        MV_UP    }
6461   };
6462
6463   int element = Feld[x][y];
6464   int move_pattern = element_info[element].move_pattern;
6465
6466   int old_move_dir = MovDir[x][y];
6467   int left_dir  = turn[old_move_dir].left;
6468   int right_dir = turn[old_move_dir].right;
6469   int back_dir  = turn[old_move_dir].back;
6470
6471   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6472   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6473   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6474   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6475
6476   int left_x  = x + left_dx,  left_y  = y + left_dy;
6477   int right_x = x + right_dx, right_y = y + right_dy;
6478   int move_x  = x + move_dx,  move_y  = y + move_dy;
6479
6480   int xx, yy;
6481
6482   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6483   {
6484     TestIfBadThingTouchesOtherBadThing(x, y);
6485
6486     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6487       MovDir[x][y] = right_dir;
6488     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6489       MovDir[x][y] = left_dir;
6490
6491     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6492       MovDelay[x][y] = 9;
6493     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6494       MovDelay[x][y] = 1;
6495   }
6496   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6497   {
6498     TestIfBadThingTouchesOtherBadThing(x, y);
6499
6500     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6501       MovDir[x][y] = left_dir;
6502     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6503       MovDir[x][y] = right_dir;
6504
6505     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6506       MovDelay[x][y] = 9;
6507     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6508       MovDelay[x][y] = 1;
6509   }
6510   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6511   {
6512     TestIfBadThingTouchesOtherBadThing(x, y);
6513
6514     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6515       MovDir[x][y] = left_dir;
6516     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6517       MovDir[x][y] = right_dir;
6518
6519     if (MovDir[x][y] != old_move_dir)
6520       MovDelay[x][y] = 9;
6521   }
6522   else if (element == EL_YAMYAM)
6523   {
6524     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6525     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6526
6527     if (can_turn_left && can_turn_right)
6528       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6529     else if (can_turn_left)
6530       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6531     else if (can_turn_right)
6532       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6533     else
6534       MovDir[x][y] = back_dir;
6535
6536     MovDelay[x][y] = 16 + 16 * RND(3);
6537   }
6538   else if (element == EL_DARK_YAMYAM)
6539   {
6540     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6541                                                          left_x, left_y);
6542     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6543                                                          right_x, right_y);
6544
6545     if (can_turn_left && can_turn_right)
6546       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6547     else if (can_turn_left)
6548       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6549     else if (can_turn_right)
6550       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6551     else
6552       MovDir[x][y] = back_dir;
6553
6554     MovDelay[x][y] = 16 + 16 * RND(3);
6555   }
6556   else if (element == EL_PACMAN)
6557   {
6558     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6559     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6560
6561     if (can_turn_left && can_turn_right)
6562       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6563     else if (can_turn_left)
6564       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6565     else if (can_turn_right)
6566       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6567     else
6568       MovDir[x][y] = back_dir;
6569
6570     MovDelay[x][y] = 6 + RND(40);
6571   }
6572   else if (element == EL_PIG)
6573   {
6574     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6575     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6576     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6577     boolean should_turn_left, should_turn_right, should_move_on;
6578     int rnd_value = 24;
6579     int rnd = RND(rnd_value);
6580
6581     should_turn_left = (can_turn_left &&
6582                         (!can_move_on ||
6583                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6584                                                    y + back_dy + left_dy)));
6585     should_turn_right = (can_turn_right &&
6586                          (!can_move_on ||
6587                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6588                                                     y + back_dy + right_dy)));
6589     should_move_on = (can_move_on &&
6590                       (!can_turn_left ||
6591                        !can_turn_right ||
6592                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6593                                                  y + move_dy + left_dy) ||
6594                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6595                                                  y + move_dy + right_dy)));
6596
6597     if (should_turn_left || should_turn_right || should_move_on)
6598     {
6599       if (should_turn_left && should_turn_right && should_move_on)
6600         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6601                         rnd < 2 * rnd_value / 3 ? right_dir :
6602                         old_move_dir);
6603       else if (should_turn_left && should_turn_right)
6604         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6605       else if (should_turn_left && should_move_on)
6606         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6607       else if (should_turn_right && should_move_on)
6608         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6609       else if (should_turn_left)
6610         MovDir[x][y] = left_dir;
6611       else if (should_turn_right)
6612         MovDir[x][y] = right_dir;
6613       else if (should_move_on)
6614         MovDir[x][y] = old_move_dir;
6615     }
6616     else if (can_move_on && rnd > rnd_value / 8)
6617       MovDir[x][y] = old_move_dir;
6618     else if (can_turn_left && can_turn_right)
6619       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6620     else if (can_turn_left && rnd > rnd_value / 8)
6621       MovDir[x][y] = left_dir;
6622     else if (can_turn_right && rnd > rnd_value/8)
6623       MovDir[x][y] = right_dir;
6624     else
6625       MovDir[x][y] = back_dir;
6626
6627     xx = x + move_xy[MovDir[x][y]].dx;
6628     yy = y + move_xy[MovDir[x][y]].dy;
6629
6630     if (!IN_LEV_FIELD(xx, yy) ||
6631         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6632       MovDir[x][y] = old_move_dir;
6633
6634     MovDelay[x][y] = 0;
6635   }
6636   else if (element == EL_DRAGON)
6637   {
6638     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6639     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6640     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6641     int rnd_value = 24;
6642     int rnd = RND(rnd_value);
6643
6644     if (can_move_on && rnd > rnd_value / 8)
6645       MovDir[x][y] = old_move_dir;
6646     else if (can_turn_left && can_turn_right)
6647       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6648     else if (can_turn_left && rnd > rnd_value / 8)
6649       MovDir[x][y] = left_dir;
6650     else if (can_turn_right && rnd > rnd_value / 8)
6651       MovDir[x][y] = right_dir;
6652     else
6653       MovDir[x][y] = back_dir;
6654
6655     xx = x + move_xy[MovDir[x][y]].dx;
6656     yy = y + move_xy[MovDir[x][y]].dy;
6657
6658     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6659       MovDir[x][y] = old_move_dir;
6660
6661     MovDelay[x][y] = 0;
6662   }
6663   else if (element == EL_MOLE)
6664   {
6665     boolean can_move_on =
6666       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6667                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6668                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6669     if (!can_move_on)
6670     {
6671       boolean can_turn_left =
6672         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6673                               IS_AMOEBOID(Feld[left_x][left_y])));
6674
6675       boolean can_turn_right =
6676         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6677                               IS_AMOEBOID(Feld[right_x][right_y])));
6678
6679       if (can_turn_left && can_turn_right)
6680         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6681       else if (can_turn_left)
6682         MovDir[x][y] = left_dir;
6683       else
6684         MovDir[x][y] = right_dir;
6685     }
6686
6687     if (MovDir[x][y] != old_move_dir)
6688       MovDelay[x][y] = 9;
6689   }
6690   else if (element == EL_BALLOON)
6691   {
6692     MovDir[x][y] = game.wind_direction;
6693     MovDelay[x][y] = 0;
6694   }
6695   else if (element == EL_SPRING)
6696   {
6697     if (MovDir[x][y] & MV_HORIZONTAL)
6698     {
6699       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6700           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6701       {
6702         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6703         ResetGfxAnimation(move_x, move_y);
6704         TEST_DrawLevelField(move_x, move_y);
6705
6706         MovDir[x][y] = back_dir;
6707       }
6708       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6709                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6710         MovDir[x][y] = MV_NONE;
6711     }
6712
6713     MovDelay[x][y] = 0;
6714   }
6715   else if (element == EL_ROBOT ||
6716            element == EL_SATELLITE ||
6717            element == EL_PENGUIN ||
6718            element == EL_EMC_ANDROID)
6719   {
6720     int attr_x = -1, attr_y = -1;
6721
6722     if (AllPlayersGone)
6723     {
6724       attr_x = ExitX;
6725       attr_y = ExitY;
6726     }
6727     else
6728     {
6729       int i;
6730
6731       for (i = 0; i < MAX_PLAYERS; i++)
6732       {
6733         struct PlayerInfo *player = &stored_player[i];
6734         int jx = player->jx, jy = player->jy;
6735
6736         if (!player->active)
6737           continue;
6738
6739         if (attr_x == -1 ||
6740             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6741         {
6742           attr_x = jx;
6743           attr_y = jy;
6744         }
6745       }
6746     }
6747
6748     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6749         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6750          game.engine_version < VERSION_IDENT(3,1,0,0)))
6751     {
6752       attr_x = ZX;
6753       attr_y = ZY;
6754     }
6755
6756     if (element == EL_PENGUIN)
6757     {
6758       int i;
6759       static int xy[4][2] =
6760       {
6761         { 0, -1 },
6762         { -1, 0 },
6763         { +1, 0 },
6764         { 0, +1 }
6765       };
6766
6767       for (i = 0; i < NUM_DIRECTIONS; i++)
6768       {
6769         int ex = x + xy[i][0];
6770         int ey = y + xy[i][1];
6771
6772         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6773                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6774                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6775                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6776         {
6777           attr_x = ex;
6778           attr_y = ey;
6779           break;
6780         }
6781       }
6782     }
6783
6784     MovDir[x][y] = MV_NONE;
6785     if (attr_x < x)
6786       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6787     else if (attr_x > x)
6788       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6789     if (attr_y < y)
6790       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6791     else if (attr_y > y)
6792       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6793
6794     if (element == EL_ROBOT)
6795     {
6796       int newx, newy;
6797
6798       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6799         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6800       Moving2Blocked(x, y, &newx, &newy);
6801
6802       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6803         MovDelay[x][y] = 8 + 8 * !RND(3);
6804       else
6805         MovDelay[x][y] = 16;
6806     }
6807     else if (element == EL_PENGUIN)
6808     {
6809       int newx, newy;
6810
6811       MovDelay[x][y] = 1;
6812
6813       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6814       {
6815         boolean first_horiz = RND(2);
6816         int new_move_dir = MovDir[x][y];
6817
6818         MovDir[x][y] =
6819           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6820         Moving2Blocked(x, y, &newx, &newy);
6821
6822         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6823           return;
6824
6825         MovDir[x][y] =
6826           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6827         Moving2Blocked(x, y, &newx, &newy);
6828
6829         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6830           return;
6831
6832         MovDir[x][y] = old_move_dir;
6833         return;
6834       }
6835     }
6836     else if (element == EL_SATELLITE)
6837     {
6838       int newx, newy;
6839
6840       MovDelay[x][y] = 1;
6841
6842       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6843       {
6844         boolean first_horiz = RND(2);
6845         int new_move_dir = MovDir[x][y];
6846
6847         MovDir[x][y] =
6848           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6849         Moving2Blocked(x, y, &newx, &newy);
6850
6851         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6852           return;
6853
6854         MovDir[x][y] =
6855           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6856         Moving2Blocked(x, y, &newx, &newy);
6857
6858         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6859           return;
6860
6861         MovDir[x][y] = old_move_dir;
6862         return;
6863       }
6864     }
6865     else if (element == EL_EMC_ANDROID)
6866     {
6867       static int check_pos[16] =
6868       {
6869         -1,             /*  0 => (invalid)          */
6870         7,              /*  1 => MV_LEFT            */
6871         3,              /*  2 => MV_RIGHT           */
6872         -1,             /*  3 => (invalid)          */
6873         1,              /*  4 =>            MV_UP   */
6874         0,              /*  5 => MV_LEFT  | MV_UP   */
6875         2,              /*  6 => MV_RIGHT | MV_UP   */
6876         -1,             /*  7 => (invalid)          */
6877         5,              /*  8 =>            MV_DOWN */
6878         6,              /*  9 => MV_LEFT  | MV_DOWN */
6879         4,              /* 10 => MV_RIGHT | MV_DOWN */
6880         -1,             /* 11 => (invalid)          */
6881         -1,             /* 12 => (invalid)          */
6882         -1,             /* 13 => (invalid)          */
6883         -1,             /* 14 => (invalid)          */
6884         -1,             /* 15 => (invalid)          */
6885       };
6886       static struct
6887       {
6888         int dx, dy;
6889         int dir;
6890       } check_xy[8] =
6891       {
6892         { -1, -1,       MV_LEFT  | MV_UP   },
6893         {  0, -1,                  MV_UP   },
6894         { +1, -1,       MV_RIGHT | MV_UP   },
6895         { +1,  0,       MV_RIGHT           },
6896         { +1, +1,       MV_RIGHT | MV_DOWN },
6897         {  0, +1,                  MV_DOWN },
6898         { -1, +1,       MV_LEFT  | MV_DOWN },
6899         { -1,  0,       MV_LEFT            },
6900       };
6901       int start_pos, check_order;
6902       boolean can_clone = FALSE;
6903       int i;
6904
6905       /* check if there is any free field around current position */
6906       for (i = 0; i < 8; i++)
6907       {
6908         int newx = x + check_xy[i].dx;
6909         int newy = y + check_xy[i].dy;
6910
6911         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6912         {
6913           can_clone = TRUE;
6914
6915           break;
6916         }
6917       }
6918
6919       if (can_clone)            /* randomly find an element to clone */
6920       {
6921         can_clone = FALSE;
6922
6923         start_pos = check_pos[RND(8)];
6924         check_order = (RND(2) ? -1 : +1);
6925
6926         for (i = 0; i < 8; i++)
6927         {
6928           int pos_raw = start_pos + i * check_order;
6929           int pos = (pos_raw + 8) % 8;
6930           int newx = x + check_xy[pos].dx;
6931           int newy = y + check_xy[pos].dy;
6932
6933           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6934           {
6935             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6936             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6937
6938             Store[x][y] = Feld[newx][newy];
6939
6940             can_clone = TRUE;
6941
6942             break;
6943           }
6944         }
6945       }
6946
6947       if (can_clone)            /* randomly find a direction to move */
6948       {
6949         can_clone = FALSE;
6950
6951         start_pos = check_pos[RND(8)];
6952         check_order = (RND(2) ? -1 : +1);
6953
6954         for (i = 0; i < 8; i++)
6955         {
6956           int pos_raw = start_pos + i * check_order;
6957           int pos = (pos_raw + 8) % 8;
6958           int newx = x + check_xy[pos].dx;
6959           int newy = y + check_xy[pos].dy;
6960           int new_move_dir = check_xy[pos].dir;
6961
6962           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6963           {
6964             MovDir[x][y] = new_move_dir;
6965             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6966
6967             can_clone = TRUE;
6968
6969             break;
6970           }
6971         }
6972       }
6973
6974       if (can_clone)            /* cloning and moving successful */
6975         return;
6976
6977       /* cannot clone -- try to move towards player */
6978
6979       start_pos = check_pos[MovDir[x][y] & 0x0f];
6980       check_order = (RND(2) ? -1 : +1);
6981
6982       for (i = 0; i < 3; i++)
6983       {
6984         /* first check start_pos, then previous/next or (next/previous) pos */
6985         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6986         int pos = (pos_raw + 8) % 8;
6987         int newx = x + check_xy[pos].dx;
6988         int newy = y + check_xy[pos].dy;
6989         int new_move_dir = check_xy[pos].dir;
6990
6991         if (IS_PLAYER(newx, newy))
6992           break;
6993
6994         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6995         {
6996           MovDir[x][y] = new_move_dir;
6997           MovDelay[x][y] = level.android_move_time * 8 + 1;
6998
6999           break;
7000         }
7001       }
7002     }
7003   }
7004   else if (move_pattern == MV_TURNING_LEFT ||
7005            move_pattern == MV_TURNING_RIGHT ||
7006            move_pattern == MV_TURNING_LEFT_RIGHT ||
7007            move_pattern == MV_TURNING_RIGHT_LEFT ||
7008            move_pattern == MV_TURNING_RANDOM ||
7009            move_pattern == MV_ALL_DIRECTIONS)
7010   {
7011     boolean can_turn_left =
7012       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7013     boolean can_turn_right =
7014       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7015
7016     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7017       return;
7018
7019     if (move_pattern == MV_TURNING_LEFT)
7020       MovDir[x][y] = left_dir;
7021     else if (move_pattern == MV_TURNING_RIGHT)
7022       MovDir[x][y] = right_dir;
7023     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7024       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7025     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7026       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7027     else if (move_pattern == MV_TURNING_RANDOM)
7028       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7029                       can_turn_right && !can_turn_left ? right_dir :
7030                       RND(2) ? left_dir : right_dir);
7031     else if (can_turn_left && can_turn_right)
7032       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7033     else if (can_turn_left)
7034       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7035     else if (can_turn_right)
7036       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7037     else
7038       MovDir[x][y] = back_dir;
7039
7040     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7041   }
7042   else if (move_pattern == MV_HORIZONTAL ||
7043            move_pattern == MV_VERTICAL)
7044   {
7045     if (move_pattern & old_move_dir)
7046       MovDir[x][y] = back_dir;
7047     else if (move_pattern == MV_HORIZONTAL)
7048       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7049     else if (move_pattern == MV_VERTICAL)
7050       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7051
7052     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7053   }
7054   else if (move_pattern & MV_ANY_DIRECTION)
7055   {
7056     MovDir[x][y] = move_pattern;
7057     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7058   }
7059   else if (move_pattern & MV_WIND_DIRECTION)
7060   {
7061     MovDir[x][y] = game.wind_direction;
7062     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7063   }
7064   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7065   {
7066     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7067       MovDir[x][y] = left_dir;
7068     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7069       MovDir[x][y] = right_dir;
7070
7071     if (MovDir[x][y] != old_move_dir)
7072       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7073   }
7074   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7075   {
7076     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7077       MovDir[x][y] = right_dir;
7078     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7079       MovDir[x][y] = left_dir;
7080
7081     if (MovDir[x][y] != old_move_dir)
7082       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7083   }
7084   else if (move_pattern == MV_TOWARDS_PLAYER ||
7085            move_pattern == MV_AWAY_FROM_PLAYER)
7086   {
7087     int attr_x = -1, attr_y = -1;
7088     int newx, newy;
7089     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7090
7091     if (AllPlayersGone)
7092     {
7093       attr_x = ExitX;
7094       attr_y = ExitY;
7095     }
7096     else
7097     {
7098       int i;
7099
7100       for (i = 0; i < MAX_PLAYERS; i++)
7101       {
7102         struct PlayerInfo *player = &stored_player[i];
7103         int jx = player->jx, jy = player->jy;
7104
7105         if (!player->active)
7106           continue;
7107
7108         if (attr_x == -1 ||
7109             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7110         {
7111           attr_x = jx;
7112           attr_y = jy;
7113         }
7114       }
7115     }
7116
7117     MovDir[x][y] = MV_NONE;
7118     if (attr_x < x)
7119       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7120     else if (attr_x > x)
7121       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7122     if (attr_y < y)
7123       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7124     else if (attr_y > y)
7125       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7126
7127     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128
7129     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7130     {
7131       boolean first_horiz = RND(2);
7132       int new_move_dir = MovDir[x][y];
7133
7134       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7135       {
7136         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7137         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7138
7139         return;
7140       }
7141
7142       MovDir[x][y] =
7143         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7144       Moving2Blocked(x, y, &newx, &newy);
7145
7146       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7147         return;
7148
7149       MovDir[x][y] =
7150         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7151       Moving2Blocked(x, y, &newx, &newy);
7152
7153       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7154         return;
7155
7156       MovDir[x][y] = old_move_dir;
7157     }
7158   }
7159   else if (move_pattern == MV_WHEN_PUSHED ||
7160            move_pattern == MV_WHEN_DROPPED)
7161   {
7162     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7163       MovDir[x][y] = MV_NONE;
7164
7165     MovDelay[x][y] = 0;
7166   }
7167   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7168   {
7169     static int test_xy[7][2] =
7170     {
7171       { 0, -1 },
7172       { -1, 0 },
7173       { +1, 0 },
7174       { 0, +1 },
7175       { 0, -1 },
7176       { -1, 0 },
7177       { +1, 0 },
7178     };
7179     static int test_dir[7] =
7180     {
7181       MV_UP,
7182       MV_LEFT,
7183       MV_RIGHT,
7184       MV_DOWN,
7185       MV_UP,
7186       MV_LEFT,
7187       MV_RIGHT,
7188     };
7189     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7190     int move_preference = -1000000;     /* start with very low preference */
7191     int new_move_dir = MV_NONE;
7192     int start_test = RND(4);
7193     int i;
7194
7195     for (i = 0; i < NUM_DIRECTIONS; i++)
7196     {
7197       int move_dir = test_dir[start_test + i];
7198       int move_dir_preference;
7199
7200       xx = x + test_xy[start_test + i][0];
7201       yy = y + test_xy[start_test + i][1];
7202
7203       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7204           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7205       {
7206         new_move_dir = move_dir;
7207
7208         break;
7209       }
7210
7211       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7212         continue;
7213
7214       move_dir_preference = -1 * RunnerVisit[xx][yy];
7215       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7216         move_dir_preference = PlayerVisit[xx][yy];
7217
7218       if (move_dir_preference > move_preference)
7219       {
7220         /* prefer field that has not been visited for the longest time */
7221         move_preference = move_dir_preference;
7222         new_move_dir = move_dir;
7223       }
7224       else if (move_dir_preference == move_preference &&
7225                move_dir == old_move_dir)
7226       {
7227         /* prefer last direction when all directions are preferred equally */
7228         move_preference = move_dir_preference;
7229         new_move_dir = move_dir;
7230       }
7231     }
7232
7233     MovDir[x][y] = new_move_dir;
7234     if (old_move_dir != new_move_dir)
7235       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7236   }
7237 }
7238
7239 static void TurnRound(int x, int y)
7240 {
7241   int direction = MovDir[x][y];
7242
7243   TurnRoundExt(x, y);
7244
7245   GfxDir[x][y] = MovDir[x][y];
7246
7247   if (direction != MovDir[x][y])
7248     GfxFrame[x][y] = 0;
7249
7250   if (MovDelay[x][y])
7251     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7252
7253   ResetGfxFrame(x, y);
7254 }
7255
7256 static boolean JustBeingPushed(int x, int y)
7257 {
7258   int i;
7259
7260   for (i = 0; i < MAX_PLAYERS; i++)
7261   {
7262     struct PlayerInfo *player = &stored_player[i];
7263
7264     if (player->active && player->is_pushing && player->MovPos)
7265     {
7266       int next_jx = player->jx + (player->jx - player->last_jx);
7267       int next_jy = player->jy + (player->jy - player->last_jy);
7268
7269       if (x == next_jx && y == next_jy)
7270         return TRUE;
7271     }
7272   }
7273
7274   return FALSE;
7275 }
7276
7277 void StartMoving(int x, int y)
7278 {
7279   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7280   int element = Feld[x][y];
7281
7282   if (Stop[x][y])
7283     return;
7284
7285   if (MovDelay[x][y] == 0)
7286     GfxAction[x][y] = ACTION_DEFAULT;
7287
7288   if (CAN_FALL(element) && y < lev_fieldy - 1)
7289   {
7290     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7291         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7292       if (JustBeingPushed(x, y))
7293         return;
7294
7295     if (element == EL_QUICKSAND_FULL)
7296     {
7297       if (IS_FREE(x, y + 1))
7298       {
7299         InitMovingField(x, y, MV_DOWN);
7300         started_moving = TRUE;
7301
7302         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7303 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7304         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7305           Store[x][y] = EL_ROCK;
7306 #else
7307         Store[x][y] = EL_ROCK;
7308 #endif
7309
7310         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7311       }
7312       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7313       {
7314         if (!MovDelay[x][y])
7315         {
7316           MovDelay[x][y] = TILEY + 1;
7317
7318           ResetGfxAnimation(x, y);
7319           ResetGfxAnimation(x, y + 1);
7320         }
7321
7322         if (MovDelay[x][y])
7323         {
7324           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7325           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7326
7327           MovDelay[x][y]--;
7328           if (MovDelay[x][y])
7329             return;
7330         }
7331
7332         Feld[x][y] = EL_QUICKSAND_EMPTY;
7333         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7334         Store[x][y + 1] = Store[x][y];
7335         Store[x][y] = 0;
7336
7337         PlayLevelSoundAction(x, y, ACTION_FILLING);
7338       }
7339       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7340       {
7341         if (!MovDelay[x][y])
7342         {
7343           MovDelay[x][y] = TILEY + 1;
7344
7345           ResetGfxAnimation(x, y);
7346           ResetGfxAnimation(x, y + 1);
7347         }
7348
7349         if (MovDelay[x][y])
7350         {
7351           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7352           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7353
7354           MovDelay[x][y]--;
7355           if (MovDelay[x][y])
7356             return;
7357         }
7358
7359         Feld[x][y] = EL_QUICKSAND_EMPTY;
7360         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7361         Store[x][y + 1] = Store[x][y];
7362         Store[x][y] = 0;
7363
7364         PlayLevelSoundAction(x, y, ACTION_FILLING);
7365       }
7366     }
7367     else if (element == EL_QUICKSAND_FAST_FULL)
7368     {
7369       if (IS_FREE(x, y + 1))
7370       {
7371         InitMovingField(x, y, MV_DOWN);
7372         started_moving = TRUE;
7373
7374         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7375 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7376         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7377           Store[x][y] = EL_ROCK;
7378 #else
7379         Store[x][y] = EL_ROCK;
7380 #endif
7381
7382         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7383       }
7384       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7385       {
7386         if (!MovDelay[x][y])
7387         {
7388           MovDelay[x][y] = TILEY + 1;
7389
7390           ResetGfxAnimation(x, y);
7391           ResetGfxAnimation(x, y + 1);
7392         }
7393
7394         if (MovDelay[x][y])
7395         {
7396           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7397           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7398
7399           MovDelay[x][y]--;
7400           if (MovDelay[x][y])
7401             return;
7402         }
7403
7404         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7405         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7406         Store[x][y + 1] = Store[x][y];
7407         Store[x][y] = 0;
7408
7409         PlayLevelSoundAction(x, y, ACTION_FILLING);
7410       }
7411       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7412       {
7413         if (!MovDelay[x][y])
7414         {
7415           MovDelay[x][y] = TILEY + 1;
7416
7417           ResetGfxAnimation(x, y);
7418           ResetGfxAnimation(x, y + 1);
7419         }
7420
7421         if (MovDelay[x][y])
7422         {
7423           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7424           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7425
7426           MovDelay[x][y]--;
7427           if (MovDelay[x][y])
7428             return;
7429         }
7430
7431         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7432         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7433         Store[x][y + 1] = Store[x][y];
7434         Store[x][y] = 0;
7435
7436         PlayLevelSoundAction(x, y, ACTION_FILLING);
7437       }
7438     }
7439     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7440              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7441     {
7442       InitMovingField(x, y, MV_DOWN);
7443       started_moving = TRUE;
7444
7445       Feld[x][y] = EL_QUICKSAND_FILLING;
7446       Store[x][y] = element;
7447
7448       PlayLevelSoundAction(x, y, ACTION_FILLING);
7449     }
7450     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7451              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7452     {
7453       InitMovingField(x, y, MV_DOWN);
7454       started_moving = TRUE;
7455
7456       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7457       Store[x][y] = element;
7458
7459       PlayLevelSoundAction(x, y, ACTION_FILLING);
7460     }
7461     else if (element == EL_MAGIC_WALL_FULL)
7462     {
7463       if (IS_FREE(x, y + 1))
7464       {
7465         InitMovingField(x, y, MV_DOWN);
7466         started_moving = TRUE;
7467
7468         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7469         Store[x][y] = EL_CHANGED(Store[x][y]);
7470       }
7471       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7472       {
7473         if (!MovDelay[x][y])
7474           MovDelay[x][y] = TILEY / 4 + 1;
7475
7476         if (MovDelay[x][y])
7477         {
7478           MovDelay[x][y]--;
7479           if (MovDelay[x][y])
7480             return;
7481         }
7482
7483         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7484         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7485         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7486         Store[x][y] = 0;
7487       }
7488     }
7489     else if (element == EL_BD_MAGIC_WALL_FULL)
7490     {
7491       if (IS_FREE(x, y + 1))
7492       {
7493         InitMovingField(x, y, MV_DOWN);
7494         started_moving = TRUE;
7495
7496         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7497         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7498       }
7499       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7500       {
7501         if (!MovDelay[x][y])
7502           MovDelay[x][y] = TILEY / 4 + 1;
7503
7504         if (MovDelay[x][y])
7505         {
7506           MovDelay[x][y]--;
7507           if (MovDelay[x][y])
7508             return;
7509         }
7510
7511         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7512         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7513         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7514         Store[x][y] = 0;
7515       }
7516     }
7517     else if (element == EL_DC_MAGIC_WALL_FULL)
7518     {
7519       if (IS_FREE(x, y + 1))
7520       {
7521         InitMovingField(x, y, MV_DOWN);
7522         started_moving = TRUE;
7523
7524         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7525         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7526       }
7527       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7528       {
7529         if (!MovDelay[x][y])
7530           MovDelay[x][y] = TILEY / 4 + 1;
7531
7532         if (MovDelay[x][y])
7533         {
7534           MovDelay[x][y]--;
7535           if (MovDelay[x][y])
7536             return;
7537         }
7538
7539         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7540         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7541         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7542         Store[x][y] = 0;
7543       }
7544     }
7545     else if ((CAN_PASS_MAGIC_WALL(element) &&
7546               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7547                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7548              (CAN_PASS_DC_MAGIC_WALL(element) &&
7549               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7550
7551     {
7552       InitMovingField(x, y, MV_DOWN);
7553       started_moving = TRUE;
7554
7555       Feld[x][y] =
7556         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7557          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7558          EL_DC_MAGIC_WALL_FILLING);
7559       Store[x][y] = element;
7560     }
7561     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7562     {
7563       SplashAcid(x, y + 1);
7564
7565       InitMovingField(x, y, MV_DOWN);
7566       started_moving = TRUE;
7567
7568       Store[x][y] = EL_ACID;
7569     }
7570     else if (
7571              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7572               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7573              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7574               CAN_FALL(element) && WasJustFalling[x][y] &&
7575               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7576
7577              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7578               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7579               (Feld[x][y + 1] == EL_BLOCKED)))
7580     {
7581       /* this is needed for a special case not covered by calling "Impact()"
7582          from "ContinueMoving()": if an element moves to a tile directly below
7583          another element which was just falling on that tile (which was empty
7584          in the previous frame), the falling element above would just stop
7585          instead of smashing the element below (in previous version, the above
7586          element was just checked for "moving" instead of "falling", resulting
7587          in incorrect smashes caused by horizontal movement of the above
7588          element; also, the case of the player being the element to smash was
7589          simply not covered here... :-/ ) */
7590
7591       CheckCollision[x][y] = 0;
7592       CheckImpact[x][y] = 0;
7593
7594       Impact(x, y);
7595     }
7596     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7597     {
7598       if (MovDir[x][y] == MV_NONE)
7599       {
7600         InitMovingField(x, y, MV_DOWN);
7601         started_moving = TRUE;
7602       }
7603     }
7604     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7605     {
7606       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7607         MovDir[x][y] = MV_DOWN;
7608
7609       InitMovingField(x, y, MV_DOWN);
7610       started_moving = TRUE;
7611     }
7612     else if (element == EL_AMOEBA_DROP)
7613     {
7614       Feld[x][y] = EL_AMOEBA_GROWING;
7615       Store[x][y] = EL_AMOEBA_WET;
7616     }
7617     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7618               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7619              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7620              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7621     {
7622       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7623                                 (IS_FREE(x - 1, y + 1) ||
7624                                  Feld[x - 1][y + 1] == EL_ACID));
7625       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7626                                 (IS_FREE(x + 1, y + 1) ||
7627                                  Feld[x + 1][y + 1] == EL_ACID));
7628       boolean can_fall_any  = (can_fall_left || can_fall_right);
7629       boolean can_fall_both = (can_fall_left && can_fall_right);
7630       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7631
7632       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7633       {
7634         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7635           can_fall_right = FALSE;
7636         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7637           can_fall_left = FALSE;
7638         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7639           can_fall_right = FALSE;
7640         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7641           can_fall_left = FALSE;
7642
7643         can_fall_any  = (can_fall_left || can_fall_right);
7644         can_fall_both = FALSE;
7645       }
7646
7647       if (can_fall_both)
7648       {
7649         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7650           can_fall_right = FALSE;       /* slip down on left side */
7651         else
7652           can_fall_left = !(can_fall_right = RND(2));
7653
7654         can_fall_both = FALSE;
7655       }
7656
7657       if (can_fall_any)
7658       {
7659         /* if not determined otherwise, prefer left side for slipping down */
7660         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7661         started_moving = TRUE;
7662       }
7663     }
7664     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7665     {
7666       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7667       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7668       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7669       int belt_dir = game.belt_dir[belt_nr];
7670
7671       if ((belt_dir == MV_LEFT  && left_is_free) ||
7672           (belt_dir == MV_RIGHT && right_is_free))
7673       {
7674         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7675
7676         InitMovingField(x, y, belt_dir);
7677         started_moving = TRUE;
7678
7679         Pushed[x][y] = TRUE;
7680         Pushed[nextx][y] = TRUE;
7681
7682         GfxAction[x][y] = ACTION_DEFAULT;
7683       }
7684       else
7685       {
7686         MovDir[x][y] = 0;       /* if element was moving, stop it */
7687       }
7688     }
7689   }
7690
7691   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7692   if (CAN_MOVE(element) && !started_moving)
7693   {
7694     int move_pattern = element_info[element].move_pattern;
7695     int newx, newy;
7696
7697     Moving2Blocked(x, y, &newx, &newy);
7698
7699     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7700       return;
7701
7702     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7703         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7704     {
7705       WasJustMoving[x][y] = 0;
7706       CheckCollision[x][y] = 0;
7707
7708       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7709
7710       if (Feld[x][y] != element)        /* element has changed */
7711         return;
7712     }
7713
7714     if (!MovDelay[x][y])        /* start new movement phase */
7715     {
7716       /* all objects that can change their move direction after each step
7717          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7718
7719       if (element != EL_YAMYAM &&
7720           element != EL_DARK_YAMYAM &&
7721           element != EL_PACMAN &&
7722           !(move_pattern & MV_ANY_DIRECTION) &&
7723           move_pattern != MV_TURNING_LEFT &&
7724           move_pattern != MV_TURNING_RIGHT &&
7725           move_pattern != MV_TURNING_LEFT_RIGHT &&
7726           move_pattern != MV_TURNING_RIGHT_LEFT &&
7727           move_pattern != MV_TURNING_RANDOM)
7728       {
7729         TurnRound(x, y);
7730
7731         if (MovDelay[x][y] && (element == EL_BUG ||
7732                                element == EL_SPACESHIP ||
7733                                element == EL_SP_SNIKSNAK ||
7734                                element == EL_SP_ELECTRON ||
7735                                element == EL_MOLE))
7736           TEST_DrawLevelField(x, y);
7737       }
7738     }
7739
7740     if (MovDelay[x][y])         /* wait some time before next movement */
7741     {
7742       MovDelay[x][y]--;
7743
7744       if (element == EL_ROBOT ||
7745           element == EL_YAMYAM ||
7746           element == EL_DARK_YAMYAM)
7747       {
7748         DrawLevelElementAnimationIfNeeded(x, y, element);
7749         PlayLevelSoundAction(x, y, ACTION_WAITING);
7750       }
7751       else if (element == EL_SP_ELECTRON)
7752         DrawLevelElementAnimationIfNeeded(x, y, element);
7753       else if (element == EL_DRAGON)
7754       {
7755         int i;
7756         int dir = MovDir[x][y];
7757         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7758         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7759         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7760                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7761                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7762                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7763         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7764
7765         GfxAction[x][y] = ACTION_ATTACKING;
7766
7767         if (IS_PLAYER(x, y))
7768           DrawPlayerField(x, y);
7769         else
7770           TEST_DrawLevelField(x, y);
7771
7772         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7773
7774         for (i = 1; i <= 3; i++)
7775         {
7776           int xx = x + i * dx;
7777           int yy = y + i * dy;
7778           int sx = SCREENX(xx);
7779           int sy = SCREENY(yy);
7780           int flame_graphic = graphic + (i - 1);
7781
7782           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7783             break;
7784
7785           if (MovDelay[x][y])
7786           {
7787             int flamed = MovingOrBlocked2Element(xx, yy);
7788
7789             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7790               Bang(xx, yy);
7791             else
7792               RemoveMovingField(xx, yy);
7793
7794             ChangeDelay[xx][yy] = 0;
7795
7796             Feld[xx][yy] = EL_FLAMES;
7797
7798             if (IN_SCR_FIELD(sx, sy))
7799             {
7800               TEST_DrawLevelFieldCrumbled(xx, yy);
7801               DrawGraphic(sx, sy, flame_graphic, frame);
7802             }
7803           }
7804           else
7805           {
7806             if (Feld[xx][yy] == EL_FLAMES)
7807               Feld[xx][yy] = EL_EMPTY;
7808             TEST_DrawLevelField(xx, yy);
7809           }
7810         }
7811       }
7812
7813       if (MovDelay[x][y])       /* element still has to wait some time */
7814       {
7815         PlayLevelSoundAction(x, y, ACTION_WAITING);
7816
7817         return;
7818       }
7819     }
7820
7821     /* now make next step */
7822
7823     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7824
7825     if (DONT_COLLIDE_WITH(element) &&
7826         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7827         !PLAYER_ENEMY_PROTECTED(newx, newy))
7828     {
7829       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7830
7831       return;
7832     }
7833
7834     else if (CAN_MOVE_INTO_ACID(element) &&
7835              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7836              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7837              (MovDir[x][y] == MV_DOWN ||
7838               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7839     {
7840       SplashAcid(newx, newy);
7841       Store[x][y] = EL_ACID;
7842     }
7843     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7844     {
7845       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7846           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7847           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7848           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7849       {
7850         RemoveField(x, y);
7851         TEST_DrawLevelField(x, y);
7852
7853         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7854         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7855           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7856
7857         local_player->friends_still_needed--;
7858         if (!local_player->friends_still_needed &&
7859             !local_player->GameOver && AllPlayersGone)
7860           PlayerWins(local_player);
7861
7862         return;
7863       }
7864       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7865       {
7866         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7867           TEST_DrawLevelField(newx, newy);
7868         else
7869           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7870       }
7871       else if (!IS_FREE(newx, newy))
7872       {
7873         GfxAction[x][y] = ACTION_WAITING;
7874
7875         if (IS_PLAYER(x, y))
7876           DrawPlayerField(x, y);
7877         else
7878           TEST_DrawLevelField(x, y);
7879
7880         return;
7881       }
7882     }
7883     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7884     {
7885       if (IS_FOOD_PIG(Feld[newx][newy]))
7886       {
7887         if (IS_MOVING(newx, newy))
7888           RemoveMovingField(newx, newy);
7889         else
7890         {
7891           Feld[newx][newy] = EL_EMPTY;
7892           TEST_DrawLevelField(newx, newy);
7893         }
7894
7895         PlayLevelSound(x, y, SND_PIG_DIGGING);
7896       }
7897       else if (!IS_FREE(newx, newy))
7898       {
7899         if (IS_PLAYER(x, y))
7900           DrawPlayerField(x, y);
7901         else
7902           TEST_DrawLevelField(x, y);
7903
7904         return;
7905       }
7906     }
7907     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7908     {
7909       if (Store[x][y] != EL_EMPTY)
7910       {
7911         boolean can_clone = FALSE;
7912         int xx, yy;
7913
7914         /* check if element to clone is still there */
7915         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7916         {
7917           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7918           {
7919             can_clone = TRUE;
7920
7921             break;
7922           }
7923         }
7924
7925         /* cannot clone or target field not free anymore -- do not clone */
7926         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7927           Store[x][y] = EL_EMPTY;
7928       }
7929
7930       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7931       {
7932         if (IS_MV_DIAGONAL(MovDir[x][y]))
7933         {
7934           int diagonal_move_dir = MovDir[x][y];
7935           int stored = Store[x][y];
7936           int change_delay = 8;
7937           int graphic;
7938
7939           /* android is moving diagonally */
7940
7941           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7942
7943           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7944           GfxElement[x][y] = EL_EMC_ANDROID;
7945           GfxAction[x][y] = ACTION_SHRINKING;
7946           GfxDir[x][y] = diagonal_move_dir;
7947           ChangeDelay[x][y] = change_delay;
7948
7949           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7950                                    GfxDir[x][y]);
7951
7952           DrawLevelGraphicAnimation(x, y, graphic);
7953           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7954
7955           if (Feld[newx][newy] == EL_ACID)
7956           {
7957             SplashAcid(newx, newy);
7958
7959             return;
7960           }
7961
7962           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7963
7964           Store[newx][newy] = EL_EMC_ANDROID;
7965           GfxElement[newx][newy] = EL_EMC_ANDROID;
7966           GfxAction[newx][newy] = ACTION_GROWING;
7967           GfxDir[newx][newy] = diagonal_move_dir;
7968           ChangeDelay[newx][newy] = change_delay;
7969
7970           graphic = el_act_dir2img(GfxElement[newx][newy],
7971                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7972
7973           DrawLevelGraphicAnimation(newx, newy, graphic);
7974           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7975
7976           return;
7977         }
7978         else
7979         {
7980           Feld[newx][newy] = EL_EMPTY;
7981           TEST_DrawLevelField(newx, newy);
7982
7983           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7984         }
7985       }
7986       else if (!IS_FREE(newx, newy))
7987       {
7988         return;
7989       }
7990     }
7991     else if (IS_CUSTOM_ELEMENT(element) &&
7992              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7993     {
7994       if (!DigFieldByCE(newx, newy, element))
7995         return;
7996
7997       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7998       {
7999         RunnerVisit[x][y] = FrameCounter;
8000         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8001       }
8002     }
8003     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8004     {
8005       if (!IS_FREE(newx, newy))
8006       {
8007         if (IS_PLAYER(x, y))
8008           DrawPlayerField(x, y);
8009         else
8010           TEST_DrawLevelField(x, y);
8011
8012         return;
8013       }
8014       else
8015       {
8016         boolean wanna_flame = !RND(10);
8017         int dx = newx - x, dy = newy - y;
8018         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8019         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8020         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8021                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8022         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8023                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8024
8025         if ((wanna_flame ||
8026              IS_CLASSIC_ENEMY(element1) ||
8027              IS_CLASSIC_ENEMY(element2)) &&
8028             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8029             element1 != EL_FLAMES && element2 != EL_FLAMES)
8030         {
8031           ResetGfxAnimation(x, y);
8032           GfxAction[x][y] = ACTION_ATTACKING;
8033
8034           if (IS_PLAYER(x, y))
8035             DrawPlayerField(x, y);
8036           else
8037             TEST_DrawLevelField(x, y);
8038
8039           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8040
8041           MovDelay[x][y] = 50;
8042
8043           Feld[newx][newy] = EL_FLAMES;
8044           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8045             Feld[newx1][newy1] = EL_FLAMES;
8046           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8047             Feld[newx2][newy2] = EL_FLAMES;
8048
8049           return;
8050         }
8051       }
8052     }
8053     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8054              Feld[newx][newy] == EL_DIAMOND)
8055     {
8056       if (IS_MOVING(newx, newy))
8057         RemoveMovingField(newx, newy);
8058       else
8059       {
8060         Feld[newx][newy] = EL_EMPTY;
8061         TEST_DrawLevelField(newx, newy);
8062       }
8063
8064       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8065     }
8066     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8067              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8068     {
8069       if (AmoebaNr[newx][newy])
8070       {
8071         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8072         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8073             Feld[newx][newy] == EL_BD_AMOEBA)
8074           AmoebaCnt[AmoebaNr[newx][newy]]--;
8075       }
8076
8077       if (IS_MOVING(newx, newy))
8078       {
8079         RemoveMovingField(newx, newy);
8080       }
8081       else
8082       {
8083         Feld[newx][newy] = EL_EMPTY;
8084         TEST_DrawLevelField(newx, newy);
8085       }
8086
8087       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8088     }
8089     else if ((element == EL_PACMAN || element == EL_MOLE)
8090              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8091     {
8092       if (AmoebaNr[newx][newy])
8093       {
8094         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8095         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8096             Feld[newx][newy] == EL_BD_AMOEBA)
8097           AmoebaCnt[AmoebaNr[newx][newy]]--;
8098       }
8099
8100       if (element == EL_MOLE)
8101       {
8102         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8103         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8104
8105         ResetGfxAnimation(x, y);
8106         GfxAction[x][y] = ACTION_DIGGING;
8107         TEST_DrawLevelField(x, y);
8108
8109         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8110
8111         return;                         /* wait for shrinking amoeba */
8112       }
8113       else      /* element == EL_PACMAN */
8114       {
8115         Feld[newx][newy] = EL_EMPTY;
8116         TEST_DrawLevelField(newx, newy);
8117         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8118       }
8119     }
8120     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8121              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8122               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8123     {
8124       /* wait for shrinking amoeba to completely disappear */
8125       return;
8126     }
8127     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8128     {
8129       /* object was running against a wall */
8130
8131       TurnRound(x, y);
8132
8133       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8134         DrawLevelElementAnimation(x, y, element);
8135
8136       if (DONT_TOUCH(element))
8137         TestIfBadThingTouchesPlayer(x, y);
8138
8139       return;
8140     }
8141
8142     InitMovingField(x, y, MovDir[x][y]);
8143
8144     PlayLevelSoundAction(x, y, ACTION_MOVING);
8145   }
8146
8147   if (MovDir[x][y])
8148     ContinueMoving(x, y);
8149 }
8150
8151 void ContinueMoving(int x, int y)
8152 {
8153   int element = Feld[x][y];
8154   struct ElementInfo *ei = &element_info[element];
8155   int direction = MovDir[x][y];
8156   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8157   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8158   int newx = x + dx, newy = y + dy;
8159   int stored = Store[x][y];
8160   int stored_new = Store[newx][newy];
8161   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8162   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8163   boolean last_line = (newy == lev_fieldy - 1);
8164
8165   MovPos[x][y] += getElementMoveStepsize(x, y);
8166
8167   if (pushed_by_player) /* special case: moving object pushed by player */
8168     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8169
8170   if (ABS(MovPos[x][y]) < TILEX)
8171   {
8172     TEST_DrawLevelField(x, y);
8173
8174     return;     /* element is still moving */
8175   }
8176
8177   /* element reached destination field */
8178
8179   Feld[x][y] = EL_EMPTY;
8180   Feld[newx][newy] = element;
8181   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8182
8183   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8184   {
8185     element = Feld[newx][newy] = EL_ACID;
8186   }
8187   else if (element == EL_MOLE)
8188   {
8189     Feld[x][y] = EL_SAND;
8190
8191     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8192   }
8193   else if (element == EL_QUICKSAND_FILLING)
8194   {
8195     element = Feld[newx][newy] = get_next_element(element);
8196     Store[newx][newy] = Store[x][y];
8197   }
8198   else if (element == EL_QUICKSAND_EMPTYING)
8199   {
8200     Feld[x][y] = get_next_element(element);
8201     element = Feld[newx][newy] = Store[x][y];
8202   }
8203   else if (element == EL_QUICKSAND_FAST_FILLING)
8204   {
8205     element = Feld[newx][newy] = get_next_element(element);
8206     Store[newx][newy] = Store[x][y];
8207   }
8208   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8209   {
8210     Feld[x][y] = get_next_element(element);
8211     element = Feld[newx][newy] = Store[x][y];
8212   }
8213   else if (element == EL_MAGIC_WALL_FILLING)
8214   {
8215     element = Feld[newx][newy] = get_next_element(element);
8216     if (!game.magic_wall_active)
8217       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8218     Store[newx][newy] = Store[x][y];
8219   }
8220   else if (element == EL_MAGIC_WALL_EMPTYING)
8221   {
8222     Feld[x][y] = get_next_element(element);
8223     if (!game.magic_wall_active)
8224       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8225     element = Feld[newx][newy] = Store[x][y];
8226
8227     InitField(newx, newy, FALSE);
8228   }
8229   else if (element == EL_BD_MAGIC_WALL_FILLING)
8230   {
8231     element = Feld[newx][newy] = get_next_element(element);
8232     if (!game.magic_wall_active)
8233       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8234     Store[newx][newy] = Store[x][y];
8235   }
8236   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8237   {
8238     Feld[x][y] = get_next_element(element);
8239     if (!game.magic_wall_active)
8240       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8241     element = Feld[newx][newy] = Store[x][y];
8242
8243     InitField(newx, newy, FALSE);
8244   }
8245   else if (element == EL_DC_MAGIC_WALL_FILLING)
8246   {
8247     element = Feld[newx][newy] = get_next_element(element);
8248     if (!game.magic_wall_active)
8249       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8250     Store[newx][newy] = Store[x][y];
8251   }
8252   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8253   {
8254     Feld[x][y] = get_next_element(element);
8255     if (!game.magic_wall_active)
8256       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8257     element = Feld[newx][newy] = Store[x][y];
8258
8259     InitField(newx, newy, FALSE);
8260   }
8261   else if (element == EL_AMOEBA_DROPPING)
8262   {
8263     Feld[x][y] = get_next_element(element);
8264     element = Feld[newx][newy] = Store[x][y];
8265   }
8266   else if (element == EL_SOKOBAN_OBJECT)
8267   {
8268     if (Back[x][y])
8269       Feld[x][y] = Back[x][y];
8270
8271     if (Back[newx][newy])
8272       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8273
8274     Back[x][y] = Back[newx][newy] = 0;
8275   }
8276
8277   Store[x][y] = EL_EMPTY;
8278   MovPos[x][y] = 0;
8279   MovDir[x][y] = 0;
8280   MovDelay[x][y] = 0;
8281
8282   MovDelay[newx][newy] = 0;
8283
8284   if (CAN_CHANGE_OR_HAS_ACTION(element))
8285   {
8286     /* copy element change control values to new field */
8287     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8288     ChangePage[newx][newy]  = ChangePage[x][y];
8289     ChangeCount[newx][newy] = ChangeCount[x][y];
8290     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8291   }
8292
8293   CustomValue[newx][newy] = CustomValue[x][y];
8294
8295   ChangeDelay[x][y] = 0;
8296   ChangePage[x][y] = -1;
8297   ChangeCount[x][y] = 0;
8298   ChangeEvent[x][y] = -1;
8299
8300   CustomValue[x][y] = 0;
8301
8302   /* copy animation control values to new field */
8303   GfxFrame[newx][newy]  = GfxFrame[x][y];
8304   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8305   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8306   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8307
8308   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8309
8310   /* some elements can leave other elements behind after moving */
8311   if (ei->move_leave_element != EL_EMPTY &&
8312       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8313       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8314   {
8315     int move_leave_element = ei->move_leave_element;
8316
8317     /* this makes it possible to leave the removed element again */
8318     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8319       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8320
8321     Feld[x][y] = move_leave_element;
8322
8323     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8324       MovDir[x][y] = direction;
8325
8326     InitField(x, y, FALSE);
8327
8328     if (GFX_CRUMBLED(Feld[x][y]))
8329       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8330
8331     if (ELEM_IS_PLAYER(move_leave_element))
8332       RelocatePlayer(x, y, move_leave_element);
8333   }
8334
8335   /* do this after checking for left-behind element */
8336   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8337
8338   if (!CAN_MOVE(element) ||
8339       (CAN_FALL(element) && direction == MV_DOWN &&
8340        (element == EL_SPRING ||
8341         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8342         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8343     GfxDir[x][y] = MovDir[newx][newy] = 0;
8344
8345   TEST_DrawLevelField(x, y);
8346   TEST_DrawLevelField(newx, newy);
8347
8348   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8349
8350   /* prevent pushed element from moving on in pushed direction */
8351   if (pushed_by_player && CAN_MOVE(element) &&
8352       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8353       !(element_info[element].move_pattern & direction))
8354     TurnRound(newx, newy);
8355
8356   /* prevent elements on conveyor belt from moving on in last direction */
8357   if (pushed_by_conveyor && CAN_FALL(element) &&
8358       direction & MV_HORIZONTAL)
8359     MovDir[newx][newy] = 0;
8360
8361   if (!pushed_by_player)
8362   {
8363     int nextx = newx + dx, nexty = newy + dy;
8364     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8365
8366     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8367
8368     if (CAN_FALL(element) && direction == MV_DOWN)
8369       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8370
8371     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8372       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8373
8374     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8375       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8376   }
8377
8378   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8379   {
8380     TestIfBadThingTouchesPlayer(newx, newy);
8381     TestIfBadThingTouchesFriend(newx, newy);
8382
8383     if (!IS_CUSTOM_ELEMENT(element))
8384       TestIfBadThingTouchesOtherBadThing(newx, newy);
8385   }
8386   else if (element == EL_PENGUIN)
8387     TestIfFriendTouchesBadThing(newx, newy);
8388
8389   if (DONT_GET_HIT_BY(element))
8390   {
8391     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8392   }
8393
8394   /* give the player one last chance (one more frame) to move away */
8395   if (CAN_FALL(element) && direction == MV_DOWN &&
8396       (last_line || (!IS_FREE(x, newy + 1) &&
8397                      (!IS_PLAYER(x, newy + 1) ||
8398                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8399     Impact(x, newy);
8400
8401   if (pushed_by_player && !game.use_change_when_pushing_bug)
8402   {
8403     int push_side = MV_DIR_OPPOSITE(direction);
8404     struct PlayerInfo *player = PLAYERINFO(x, y);
8405
8406     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8407                                player->index_bit, push_side);
8408     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8409                                         player->index_bit, push_side);
8410   }
8411
8412   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8413     MovDelay[newx][newy] = 1;
8414
8415   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8416
8417   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8418   TestIfElementHitsCustomElement(newx, newy, direction);
8419   TestIfPlayerTouchesCustomElement(newx, newy);
8420   TestIfElementTouchesCustomElement(newx, newy);
8421
8422   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8423       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8424     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8425                              MV_DIR_OPPOSITE(direction));
8426 }
8427
8428 int AmoebeNachbarNr(int ax, int ay)
8429 {
8430   int i;
8431   int element = Feld[ax][ay];
8432   int group_nr = 0;
8433   static int xy[4][2] =
8434   {
8435     { 0, -1 },
8436     { -1, 0 },
8437     { +1, 0 },
8438     { 0, +1 }
8439   };
8440
8441   for (i = 0; i < NUM_DIRECTIONS; i++)
8442   {
8443     int x = ax + xy[i][0];
8444     int y = ay + xy[i][1];
8445
8446     if (!IN_LEV_FIELD(x, y))
8447       continue;
8448
8449     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8450       group_nr = AmoebaNr[x][y];
8451   }
8452
8453   return group_nr;
8454 }
8455
8456 void AmoebenVereinigen(int ax, int ay)
8457 {
8458   int i, x, y, xx, yy;
8459   int new_group_nr = AmoebaNr[ax][ay];
8460   static int xy[4][2] =
8461   {
8462     { 0, -1 },
8463     { -1, 0 },
8464     { +1, 0 },
8465     { 0, +1 }
8466   };
8467
8468   if (new_group_nr == 0)
8469     return;
8470
8471   for (i = 0; i < NUM_DIRECTIONS; i++)
8472   {
8473     x = ax + xy[i][0];
8474     y = ay + xy[i][1];
8475
8476     if (!IN_LEV_FIELD(x, y))
8477       continue;
8478
8479     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8480          Feld[x][y] == EL_BD_AMOEBA ||
8481          Feld[x][y] == EL_AMOEBA_DEAD) &&
8482         AmoebaNr[x][y] != new_group_nr)
8483     {
8484       int old_group_nr = AmoebaNr[x][y];
8485
8486       if (old_group_nr == 0)
8487         return;
8488
8489       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8490       AmoebaCnt[old_group_nr] = 0;
8491       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8492       AmoebaCnt2[old_group_nr] = 0;
8493
8494       SCAN_PLAYFIELD(xx, yy)
8495       {
8496         if (AmoebaNr[xx][yy] == old_group_nr)
8497           AmoebaNr[xx][yy] = new_group_nr;
8498       }
8499     }
8500   }
8501 }
8502
8503 void AmoebeUmwandeln(int ax, int ay)
8504 {
8505   int i, x, y;
8506
8507   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8508   {
8509     int group_nr = AmoebaNr[ax][ay];
8510
8511 #ifdef DEBUG
8512     if (group_nr == 0)
8513     {
8514       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8515       printf("AmoebeUmwandeln(): This should never happen!\n");
8516       return;
8517     }
8518 #endif
8519
8520     SCAN_PLAYFIELD(x, y)
8521     {
8522       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8523       {
8524         AmoebaNr[x][y] = 0;
8525         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8526       }
8527     }
8528
8529     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8530                             SND_AMOEBA_TURNING_TO_GEM :
8531                             SND_AMOEBA_TURNING_TO_ROCK));
8532     Bang(ax, ay);
8533   }
8534   else
8535   {
8536     static int xy[4][2] =
8537     {
8538       { 0, -1 },
8539       { -1, 0 },
8540       { +1, 0 },
8541       { 0, +1 }
8542     };
8543
8544     for (i = 0; i < NUM_DIRECTIONS; i++)
8545     {
8546       x = ax + xy[i][0];
8547       y = ay + xy[i][1];
8548
8549       if (!IN_LEV_FIELD(x, y))
8550         continue;
8551
8552       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8553       {
8554         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8555                               SND_AMOEBA_TURNING_TO_GEM :
8556                               SND_AMOEBA_TURNING_TO_ROCK));
8557         Bang(x, y);
8558       }
8559     }
8560   }
8561 }
8562
8563 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8564 {
8565   int x, y;
8566   int group_nr = AmoebaNr[ax][ay];
8567   boolean done = FALSE;
8568
8569 #ifdef DEBUG
8570   if (group_nr == 0)
8571   {
8572     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8573     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8574     return;
8575   }
8576 #endif
8577
8578   SCAN_PLAYFIELD(x, y)
8579   {
8580     if (AmoebaNr[x][y] == group_nr &&
8581         (Feld[x][y] == EL_AMOEBA_DEAD ||
8582          Feld[x][y] == EL_BD_AMOEBA ||
8583          Feld[x][y] == EL_AMOEBA_GROWING))
8584     {
8585       AmoebaNr[x][y] = 0;
8586       Feld[x][y] = new_element;
8587       InitField(x, y, FALSE);
8588       TEST_DrawLevelField(x, y);
8589       done = TRUE;
8590     }
8591   }
8592
8593   if (done)
8594     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8595                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8596                             SND_BD_AMOEBA_TURNING_TO_GEM));
8597 }
8598
8599 void AmoebeWaechst(int x, int y)
8600 {
8601   static unsigned int sound_delay = 0;
8602   static unsigned int sound_delay_value = 0;
8603
8604   if (!MovDelay[x][y])          /* start new growing cycle */
8605   {
8606     MovDelay[x][y] = 7;
8607
8608     if (DelayReached(&sound_delay, sound_delay_value))
8609     {
8610       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8611       sound_delay_value = 30;
8612     }
8613   }
8614
8615   if (MovDelay[x][y])           /* wait some time before growing bigger */
8616   {
8617     MovDelay[x][y]--;
8618     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8619     {
8620       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8621                                            6 - MovDelay[x][y]);
8622
8623       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8624     }
8625
8626     if (!MovDelay[x][y])
8627     {
8628       Feld[x][y] = Store[x][y];
8629       Store[x][y] = 0;
8630       TEST_DrawLevelField(x, y);
8631     }
8632   }
8633 }
8634
8635 void AmoebaDisappearing(int x, int y)
8636 {
8637   static unsigned int sound_delay = 0;
8638   static unsigned int sound_delay_value = 0;
8639
8640   if (!MovDelay[x][y])          /* start new shrinking cycle */
8641   {
8642     MovDelay[x][y] = 7;
8643
8644     if (DelayReached(&sound_delay, sound_delay_value))
8645       sound_delay_value = 30;
8646   }
8647
8648   if (MovDelay[x][y])           /* wait some time before shrinking */
8649   {
8650     MovDelay[x][y]--;
8651     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8652     {
8653       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8654                                            6 - MovDelay[x][y]);
8655
8656       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8657     }
8658
8659     if (!MovDelay[x][y])
8660     {
8661       Feld[x][y] = EL_EMPTY;
8662       TEST_DrawLevelField(x, y);
8663
8664       /* don't let mole enter this field in this cycle;
8665          (give priority to objects falling to this field from above) */
8666       Stop[x][y] = TRUE;
8667     }
8668   }
8669 }
8670
8671 void AmoebeAbleger(int ax, int ay)
8672 {
8673   int i;
8674   int element = Feld[ax][ay];
8675   int graphic = el2img(element);
8676   int newax = ax, neway = ay;
8677   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8678   static int xy[4][2] =
8679   {
8680     { 0, -1 },
8681     { -1, 0 },
8682     { +1, 0 },
8683     { 0, +1 }
8684   };
8685
8686   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8687   {
8688     Feld[ax][ay] = EL_AMOEBA_DEAD;
8689     TEST_DrawLevelField(ax, ay);
8690     return;
8691   }
8692
8693   if (IS_ANIMATED(graphic))
8694     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8695
8696   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8697     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8698
8699   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8700   {
8701     MovDelay[ax][ay]--;
8702     if (MovDelay[ax][ay])
8703       return;
8704   }
8705
8706   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8707   {
8708     int start = RND(4);
8709     int x = ax + xy[start][0];
8710     int y = ay + xy[start][1];
8711
8712     if (!IN_LEV_FIELD(x, y))
8713       return;
8714
8715     if (IS_FREE(x, y) ||
8716         CAN_GROW_INTO(Feld[x][y]) ||
8717         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8718         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8719     {
8720       newax = x;
8721       neway = y;
8722     }
8723
8724     if (newax == ax && neway == ay)
8725       return;
8726   }
8727   else                          /* normal or "filled" (BD style) amoeba */
8728   {
8729     int start = RND(4);
8730     boolean waiting_for_player = FALSE;
8731
8732     for (i = 0; i < NUM_DIRECTIONS; i++)
8733     {
8734       int j = (start + i) % 4;
8735       int x = ax + xy[j][0];
8736       int y = ay + xy[j][1];
8737
8738       if (!IN_LEV_FIELD(x, y))
8739         continue;
8740
8741       if (IS_FREE(x, y) ||
8742           CAN_GROW_INTO(Feld[x][y]) ||
8743           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8744           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8745       {
8746         newax = x;
8747         neway = y;
8748         break;
8749       }
8750       else if (IS_PLAYER(x, y))
8751         waiting_for_player = TRUE;
8752     }
8753
8754     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8755     {
8756       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8757       {
8758         Feld[ax][ay] = EL_AMOEBA_DEAD;
8759         TEST_DrawLevelField(ax, ay);
8760         AmoebaCnt[AmoebaNr[ax][ay]]--;
8761
8762         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8763         {
8764           if (element == EL_AMOEBA_FULL)
8765             AmoebeUmwandeln(ax, ay);
8766           else if (element == EL_BD_AMOEBA)
8767             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8768         }
8769       }
8770       return;
8771     }
8772     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8773     {
8774       /* amoeba gets larger by growing in some direction */
8775
8776       int new_group_nr = AmoebaNr[ax][ay];
8777
8778 #ifdef DEBUG
8779   if (new_group_nr == 0)
8780   {
8781     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8782     printf("AmoebeAbleger(): This should never happen!\n");
8783     return;
8784   }
8785 #endif
8786
8787       AmoebaNr[newax][neway] = new_group_nr;
8788       AmoebaCnt[new_group_nr]++;
8789       AmoebaCnt2[new_group_nr]++;
8790
8791       /* if amoeba touches other amoeba(s) after growing, unify them */
8792       AmoebenVereinigen(newax, neway);
8793
8794       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8795       {
8796         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8797         return;
8798       }
8799     }
8800   }
8801
8802   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8803       (neway == lev_fieldy - 1 && newax != ax))
8804   {
8805     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8806     Store[newax][neway] = element;
8807   }
8808   else if (neway == ay || element == EL_EMC_DRIPPER)
8809   {
8810     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8811
8812     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8813   }
8814   else
8815   {
8816     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8817     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8818     Store[ax][ay] = EL_AMOEBA_DROP;
8819     ContinueMoving(ax, ay);
8820     return;
8821   }
8822
8823   TEST_DrawLevelField(newax, neway);
8824 }
8825
8826 void Life(int ax, int ay)
8827 {
8828   int x1, y1, x2, y2;
8829   int life_time = 40;
8830   int element = Feld[ax][ay];
8831   int graphic = el2img(element);
8832   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8833                          level.biomaze);
8834   boolean changed = FALSE;
8835
8836   if (IS_ANIMATED(graphic))
8837     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8838
8839   if (Stop[ax][ay])
8840     return;
8841
8842   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8843     MovDelay[ax][ay] = life_time;
8844
8845   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8846   {
8847     MovDelay[ax][ay]--;
8848     if (MovDelay[ax][ay])
8849       return;
8850   }
8851
8852   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8853   {
8854     int xx = ax+x1, yy = ay+y1;
8855     int nachbarn = 0;
8856
8857     if (!IN_LEV_FIELD(xx, yy))
8858       continue;
8859
8860     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8861     {
8862       int x = xx+x2, y = yy+y2;
8863
8864       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8865         continue;
8866
8867       if (((Feld[x][y] == element ||
8868             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8869            !Stop[x][y]) ||
8870           (IS_FREE(x, y) && Stop[x][y]))
8871         nachbarn++;
8872     }
8873
8874     if (xx == ax && yy == ay)           /* field in the middle */
8875     {
8876       if (nachbarn < life_parameter[0] ||
8877           nachbarn > life_parameter[1])
8878       {
8879         Feld[xx][yy] = EL_EMPTY;
8880         if (!Stop[xx][yy])
8881           TEST_DrawLevelField(xx, yy);
8882         Stop[xx][yy] = TRUE;
8883         changed = TRUE;
8884       }
8885     }
8886     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8887     {                                   /* free border field */
8888       if (nachbarn >= life_parameter[2] &&
8889           nachbarn <= life_parameter[3])
8890       {
8891         Feld[xx][yy] = element;
8892         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8893         if (!Stop[xx][yy])
8894           TEST_DrawLevelField(xx, yy);
8895         Stop[xx][yy] = TRUE;
8896         changed = TRUE;
8897       }
8898     }
8899   }
8900
8901   if (changed)
8902     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8903                    SND_GAME_OF_LIFE_GROWING);
8904 }
8905
8906 static void InitRobotWheel(int x, int y)
8907 {
8908   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8909 }
8910
8911 static void RunRobotWheel(int x, int y)
8912 {
8913   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8914 }
8915
8916 static void StopRobotWheel(int x, int y)
8917 {
8918   if (ZX == x && ZY == y)
8919   {
8920     ZX = ZY = -1;
8921
8922     game.robot_wheel_active = FALSE;
8923   }
8924 }
8925
8926 static void InitTimegateWheel(int x, int y)
8927 {
8928   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8929 }
8930
8931 static void RunTimegateWheel(int x, int y)
8932 {
8933   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8934 }
8935
8936 static void InitMagicBallDelay(int x, int y)
8937 {
8938   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8939 }
8940
8941 static void ActivateMagicBall(int bx, int by)
8942 {
8943   int x, y;
8944
8945   if (level.ball_random)
8946   {
8947     int pos_border = RND(8);    /* select one of the eight border elements */
8948     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8949     int xx = pos_content % 3;
8950     int yy = pos_content / 3;
8951
8952     x = bx - 1 + xx;
8953     y = by - 1 + yy;
8954
8955     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8956       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8957   }
8958   else
8959   {
8960     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8961     {
8962       int xx = x - bx + 1;
8963       int yy = y - by + 1;
8964
8965       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8966         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8967     }
8968   }
8969
8970   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8971 }
8972
8973 void CheckExit(int x, int y)
8974 {
8975   if (local_player->gems_still_needed > 0 ||
8976       local_player->sokobanfields_still_needed > 0 ||
8977       local_player->lights_still_needed > 0)
8978   {
8979     int element = Feld[x][y];
8980     int graphic = el2img(element);
8981
8982     if (IS_ANIMATED(graphic))
8983       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8984
8985     return;
8986   }
8987
8988   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8989     return;
8990
8991   Feld[x][y] = EL_EXIT_OPENING;
8992
8993   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8994 }
8995
8996 void CheckExitEM(int x, int y)
8997 {
8998   if (local_player->gems_still_needed > 0 ||
8999       local_player->sokobanfields_still_needed > 0 ||
9000       local_player->lights_still_needed > 0)
9001   {
9002     int element = Feld[x][y];
9003     int graphic = el2img(element);
9004
9005     if (IS_ANIMATED(graphic))
9006       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9007
9008     return;
9009   }
9010
9011   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9012     return;
9013
9014   Feld[x][y] = EL_EM_EXIT_OPENING;
9015
9016   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9017 }
9018
9019 void CheckExitSteel(int x, int y)
9020 {
9021   if (local_player->gems_still_needed > 0 ||
9022       local_player->sokobanfields_still_needed > 0 ||
9023       local_player->lights_still_needed > 0)
9024   {
9025     int element = Feld[x][y];
9026     int graphic = el2img(element);
9027
9028     if (IS_ANIMATED(graphic))
9029       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9030
9031     return;
9032   }
9033
9034   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9035     return;
9036
9037   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9038
9039   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9040 }
9041
9042 void CheckExitSteelEM(int x, int y)
9043 {
9044   if (local_player->gems_still_needed > 0 ||
9045       local_player->sokobanfields_still_needed > 0 ||
9046       local_player->lights_still_needed > 0)
9047   {
9048     int element = Feld[x][y];
9049     int graphic = el2img(element);
9050
9051     if (IS_ANIMATED(graphic))
9052       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9053
9054     return;
9055   }
9056
9057   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9058     return;
9059
9060   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9061
9062   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9063 }
9064
9065 void CheckExitSP(int x, int y)
9066 {
9067   if (local_player->gems_still_needed > 0)
9068   {
9069     int element = Feld[x][y];
9070     int graphic = el2img(element);
9071
9072     if (IS_ANIMATED(graphic))
9073       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9074
9075     return;
9076   }
9077
9078   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9079     return;
9080
9081   Feld[x][y] = EL_SP_EXIT_OPENING;
9082
9083   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9084 }
9085
9086 static void CloseAllOpenTimegates()
9087 {
9088   int x, y;
9089
9090   SCAN_PLAYFIELD(x, y)
9091   {
9092     int element = Feld[x][y];
9093
9094     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9095     {
9096       Feld[x][y] = EL_TIMEGATE_CLOSING;
9097
9098       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9099     }
9100   }
9101 }
9102
9103 void DrawTwinkleOnField(int x, int y)
9104 {
9105   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9106     return;
9107
9108   if (Feld[x][y] == EL_BD_DIAMOND)
9109     return;
9110
9111   if (MovDelay[x][y] == 0)      /* next animation frame */
9112     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9113
9114   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9115   {
9116     MovDelay[x][y]--;
9117
9118     DrawLevelElementAnimation(x, y, Feld[x][y]);
9119
9120     if (MovDelay[x][y] != 0)
9121     {
9122       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9123                                            10 - MovDelay[x][y]);
9124
9125       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9126     }
9127   }
9128 }
9129
9130 void MauerWaechst(int x, int y)
9131 {
9132   int delay = 6;
9133
9134   if (!MovDelay[x][y])          /* next animation frame */
9135     MovDelay[x][y] = 3 * delay;
9136
9137   if (MovDelay[x][y])           /* wait some time before next frame */
9138   {
9139     MovDelay[x][y]--;
9140
9141     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9142     {
9143       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9144       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9145
9146       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9147     }
9148
9149     if (!MovDelay[x][y])
9150     {
9151       if (MovDir[x][y] == MV_LEFT)
9152       {
9153         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9154           TEST_DrawLevelField(x - 1, y);
9155       }
9156       else if (MovDir[x][y] == MV_RIGHT)
9157       {
9158         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9159           TEST_DrawLevelField(x + 1, y);
9160       }
9161       else if (MovDir[x][y] == MV_UP)
9162       {
9163         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9164           TEST_DrawLevelField(x, y - 1);
9165       }
9166       else
9167       {
9168         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9169           TEST_DrawLevelField(x, y + 1);
9170       }
9171
9172       Feld[x][y] = Store[x][y];
9173       Store[x][y] = 0;
9174       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9175       TEST_DrawLevelField(x, y);
9176     }
9177   }
9178 }
9179
9180 void MauerAbleger(int ax, int ay)
9181 {
9182   int element = Feld[ax][ay];
9183   int graphic = el2img(element);
9184   boolean oben_frei = FALSE, unten_frei = FALSE;
9185   boolean links_frei = FALSE, rechts_frei = FALSE;
9186   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9187   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9188   boolean new_wall = FALSE;
9189
9190   if (IS_ANIMATED(graphic))
9191     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9192
9193   if (!MovDelay[ax][ay])        /* start building new wall */
9194     MovDelay[ax][ay] = 6;
9195
9196   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9197   {
9198     MovDelay[ax][ay]--;
9199     if (MovDelay[ax][ay])
9200       return;
9201   }
9202
9203   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9204     oben_frei = TRUE;
9205   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9206     unten_frei = TRUE;
9207   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9208     links_frei = TRUE;
9209   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9210     rechts_frei = TRUE;
9211
9212   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9213       element == EL_EXPANDABLE_WALL_ANY)
9214   {
9215     if (oben_frei)
9216     {
9217       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9218       Store[ax][ay-1] = element;
9219       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9220       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9221         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9222                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9223       new_wall = TRUE;
9224     }
9225     if (unten_frei)
9226     {
9227       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9228       Store[ax][ay+1] = element;
9229       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9230       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9231         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9232                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9233       new_wall = TRUE;
9234     }
9235   }
9236
9237   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9238       element == EL_EXPANDABLE_WALL_ANY ||
9239       element == EL_EXPANDABLE_WALL ||
9240       element == EL_BD_EXPANDABLE_WALL)
9241   {
9242     if (links_frei)
9243     {
9244       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9245       Store[ax-1][ay] = element;
9246       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9247       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9248         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9249                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9250       new_wall = TRUE;
9251     }
9252
9253     if (rechts_frei)
9254     {
9255       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9256       Store[ax+1][ay] = element;
9257       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9258       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9259         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9260                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9261       new_wall = TRUE;
9262     }
9263   }
9264
9265   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9266     TEST_DrawLevelField(ax, ay);
9267
9268   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9269     oben_massiv = TRUE;
9270   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9271     unten_massiv = TRUE;
9272   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9273     links_massiv = TRUE;
9274   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9275     rechts_massiv = TRUE;
9276
9277   if (((oben_massiv && unten_massiv) ||
9278        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9279        element == EL_EXPANDABLE_WALL) &&
9280       ((links_massiv && rechts_massiv) ||
9281        element == EL_EXPANDABLE_WALL_VERTICAL))
9282     Feld[ax][ay] = EL_WALL;
9283
9284   if (new_wall)
9285     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9286 }
9287
9288 void MauerAblegerStahl(int ax, int ay)
9289 {
9290   int element = Feld[ax][ay];
9291   int graphic = el2img(element);
9292   boolean oben_frei = FALSE, unten_frei = FALSE;
9293   boolean links_frei = FALSE, rechts_frei = FALSE;
9294   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9295   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9296   boolean new_wall = FALSE;
9297
9298   if (IS_ANIMATED(graphic))
9299     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9300
9301   if (!MovDelay[ax][ay])        /* start building new wall */
9302     MovDelay[ax][ay] = 6;
9303
9304   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9305   {
9306     MovDelay[ax][ay]--;
9307     if (MovDelay[ax][ay])
9308       return;
9309   }
9310
9311   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9312     oben_frei = TRUE;
9313   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9314     unten_frei = TRUE;
9315   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9316     links_frei = TRUE;
9317   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9318     rechts_frei = TRUE;
9319
9320   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9321       element == EL_EXPANDABLE_STEELWALL_ANY)
9322   {
9323     if (oben_frei)
9324     {
9325       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9326       Store[ax][ay-1] = element;
9327       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9328       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9329         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9330                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9331       new_wall = TRUE;
9332     }
9333     if (unten_frei)
9334     {
9335       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9336       Store[ax][ay+1] = element;
9337       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9338       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9339         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9340                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9341       new_wall = TRUE;
9342     }
9343   }
9344
9345   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9346       element == EL_EXPANDABLE_STEELWALL_ANY)
9347   {
9348     if (links_frei)
9349     {
9350       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9351       Store[ax-1][ay] = element;
9352       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9353       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9354         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9355                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9356       new_wall = TRUE;
9357     }
9358
9359     if (rechts_frei)
9360     {
9361       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9362       Store[ax+1][ay] = element;
9363       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9364       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9365         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9366                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9367       new_wall = TRUE;
9368     }
9369   }
9370
9371   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9372     oben_massiv = TRUE;
9373   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9374     unten_massiv = TRUE;
9375   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9376     links_massiv = TRUE;
9377   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9378     rechts_massiv = TRUE;
9379
9380   if (((oben_massiv && unten_massiv) ||
9381        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9382       ((links_massiv && rechts_massiv) ||
9383        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9384     Feld[ax][ay] = EL_STEELWALL;
9385
9386   if (new_wall)
9387     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9388 }
9389
9390 void CheckForDragon(int x, int y)
9391 {
9392   int i, j;
9393   boolean dragon_found = FALSE;
9394   static int xy[4][2] =
9395   {
9396     { 0, -1 },
9397     { -1, 0 },
9398     { +1, 0 },
9399     { 0, +1 }
9400   };
9401
9402   for (i = 0; i < NUM_DIRECTIONS; i++)
9403   {
9404     for (j = 0; j < 4; j++)
9405     {
9406       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9407
9408       if (IN_LEV_FIELD(xx, yy) &&
9409           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9410       {
9411         if (Feld[xx][yy] == EL_DRAGON)
9412           dragon_found = TRUE;
9413       }
9414       else
9415         break;
9416     }
9417   }
9418
9419   if (!dragon_found)
9420   {
9421     for (i = 0; i < NUM_DIRECTIONS; i++)
9422     {
9423       for (j = 0; j < 3; j++)
9424       {
9425         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9426   
9427         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9428         {
9429           Feld[xx][yy] = EL_EMPTY;
9430           TEST_DrawLevelField(xx, yy);
9431         }
9432         else
9433           break;
9434       }
9435     }
9436   }
9437 }
9438
9439 static void InitBuggyBase(int x, int y)
9440 {
9441   int element = Feld[x][y];
9442   int activating_delay = FRAMES_PER_SECOND / 4;
9443
9444   ChangeDelay[x][y] =
9445     (element == EL_SP_BUGGY_BASE ?
9446      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9447      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9448      activating_delay :
9449      element == EL_SP_BUGGY_BASE_ACTIVE ?
9450      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9451 }
9452
9453 static void WarnBuggyBase(int x, int y)
9454 {
9455   int i;
9456   static int xy[4][2] =
9457   {
9458     { 0, -1 },
9459     { -1, 0 },
9460     { +1, 0 },
9461     { 0, +1 }
9462   };
9463
9464   for (i = 0; i < NUM_DIRECTIONS; i++)
9465   {
9466     int xx = x + xy[i][0];
9467     int yy = y + xy[i][1];
9468
9469     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9470     {
9471       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9472
9473       break;
9474     }
9475   }
9476 }
9477
9478 static void InitTrap(int x, int y)
9479 {
9480   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9481 }
9482
9483 static void ActivateTrap(int x, int y)
9484 {
9485   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9486 }
9487
9488 static void ChangeActiveTrap(int x, int y)
9489 {
9490   int graphic = IMG_TRAP_ACTIVE;
9491
9492   /* if new animation frame was drawn, correct crumbled sand border */
9493   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9494     TEST_DrawLevelFieldCrumbled(x, y);
9495 }
9496
9497 static int getSpecialActionElement(int element, int number, int base_element)
9498 {
9499   return (element != EL_EMPTY ? element :
9500           number != -1 ? base_element + number - 1 :
9501           EL_EMPTY);
9502 }
9503
9504 static int getModifiedActionNumber(int value_old, int operator, int operand,
9505                                    int value_min, int value_max)
9506 {
9507   int value_new = (operator == CA_MODE_SET      ? operand :
9508                    operator == CA_MODE_ADD      ? value_old + operand :
9509                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9510                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9511                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9512                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9513                    value_old);
9514
9515   return (value_new < value_min ? value_min :
9516           value_new > value_max ? value_max :
9517           value_new);
9518 }
9519
9520 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9521 {
9522   struct ElementInfo *ei = &element_info[element];
9523   struct ElementChangeInfo *change = &ei->change_page[page];
9524   int target_element = change->target_element;
9525   int action_type = change->action_type;
9526   int action_mode = change->action_mode;
9527   int action_arg = change->action_arg;
9528   int action_element = change->action_element;
9529   int i;
9530
9531   if (!change->has_action)
9532     return;
9533
9534   /* ---------- determine action paramater values -------------------------- */
9535
9536   int level_time_value =
9537     (level.time > 0 ? TimeLeft :
9538      TimePlayed);
9539
9540   int action_arg_element_raw =
9541     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9542      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9543      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9544      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9545      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9546      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9547      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9548      EL_EMPTY);
9549   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9550
9551   int action_arg_direction =
9552     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9553      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9554      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9555      change->actual_trigger_side :
9556      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9557      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9558      MV_NONE);
9559
9560   int action_arg_number_min =
9561     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9562      CA_ARG_MIN);
9563
9564   int action_arg_number_max =
9565     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9566      action_type == CA_SET_LEVEL_GEMS ? 999 :
9567      action_type == CA_SET_LEVEL_TIME ? 9999 :
9568      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9569      action_type == CA_SET_CE_VALUE ? 9999 :
9570      action_type == CA_SET_CE_SCORE ? 9999 :
9571      CA_ARG_MAX);
9572
9573   int action_arg_number_reset =
9574     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9575      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9576      action_type == CA_SET_LEVEL_TIME ? level.time :
9577      action_type == CA_SET_LEVEL_SCORE ? 0 :
9578      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9579      action_type == CA_SET_CE_SCORE ? 0 :
9580      0);
9581
9582   int action_arg_number =
9583     (action_arg <= CA_ARG_MAX ? action_arg :
9584      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9585      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9586      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9587      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9588      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9589      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9590      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9591      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9592      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9593      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9594      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9595      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9596      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9597      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9598      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9599      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9600      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9601      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9602      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9603      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9604      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9605      -1);
9606
9607   int action_arg_number_old =
9608     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9609      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9610      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9611      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9612      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9613      0);
9614
9615   int action_arg_number_new =
9616     getModifiedActionNumber(action_arg_number_old,
9617                             action_mode, action_arg_number,
9618                             action_arg_number_min, action_arg_number_max);
9619
9620   int trigger_player_bits =
9621     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9622      change->actual_trigger_player_bits : change->trigger_player);
9623
9624   int action_arg_player_bits =
9625     (action_arg >= CA_ARG_PLAYER_1 &&
9626      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9627      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9628      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9629      PLAYER_BITS_ANY);
9630
9631   /* ---------- execute action  -------------------------------------------- */
9632
9633   switch (action_type)
9634   {
9635     case CA_NO_ACTION:
9636     {
9637       return;
9638     }
9639
9640     /* ---------- level actions  ------------------------------------------- */
9641
9642     case CA_RESTART_LEVEL:
9643     {
9644       game.restart_level = TRUE;
9645
9646       break;
9647     }
9648
9649     case CA_SHOW_ENVELOPE:
9650     {
9651       int element = getSpecialActionElement(action_arg_element,
9652                                             action_arg_number, EL_ENVELOPE_1);
9653
9654       if (IS_ENVELOPE(element))
9655         local_player->show_envelope = element;
9656
9657       break;
9658     }
9659
9660     case CA_SET_LEVEL_TIME:
9661     {
9662       if (level.time > 0)       /* only modify limited time value */
9663       {
9664         TimeLeft = action_arg_number_new;
9665
9666         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9667
9668         DisplayGameControlValues();
9669
9670         if (!TimeLeft && setup.time_limit)
9671           for (i = 0; i < MAX_PLAYERS; i++)
9672             KillPlayer(&stored_player[i]);
9673       }
9674
9675       break;
9676     }
9677
9678     case CA_SET_LEVEL_SCORE:
9679     {
9680       local_player->score = action_arg_number_new;
9681
9682       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9683
9684       DisplayGameControlValues();
9685
9686       break;
9687     }
9688
9689     case CA_SET_LEVEL_GEMS:
9690     {
9691       local_player->gems_still_needed = action_arg_number_new;
9692
9693       game.snapshot.collected_item = TRUE;
9694
9695       game_panel_controls[GAME_PANEL_GEMS].value =
9696         local_player->gems_still_needed;
9697
9698       DisplayGameControlValues();
9699
9700       break;
9701     }
9702
9703     case CA_SET_LEVEL_WIND:
9704     {
9705       game.wind_direction = action_arg_direction;
9706
9707       break;
9708     }
9709
9710     case CA_SET_LEVEL_RANDOM_SEED:
9711     {
9712       /* ensure that setting a new random seed while playing is predictable */
9713       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9714
9715       break;
9716     }
9717
9718     /* ---------- player actions  ------------------------------------------ */
9719
9720     case CA_MOVE_PLAYER:
9721     {
9722       /* automatically move to the next field in specified direction */
9723       for (i = 0; i < MAX_PLAYERS; i++)
9724         if (trigger_player_bits & (1 << i))
9725           stored_player[i].programmed_action = action_arg_direction;
9726
9727       break;
9728     }
9729
9730     case CA_EXIT_PLAYER:
9731     {
9732       for (i = 0; i < MAX_PLAYERS; i++)
9733         if (action_arg_player_bits & (1 << i))
9734           PlayerWins(&stored_player[i]);
9735
9736       break;
9737     }
9738
9739     case CA_KILL_PLAYER:
9740     {
9741       for (i = 0; i < MAX_PLAYERS; i++)
9742         if (action_arg_player_bits & (1 << i))
9743           KillPlayer(&stored_player[i]);
9744
9745       break;
9746     }
9747
9748     case CA_SET_PLAYER_KEYS:
9749     {
9750       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9751       int element = getSpecialActionElement(action_arg_element,
9752                                             action_arg_number, EL_KEY_1);
9753
9754       if (IS_KEY(element))
9755       {
9756         for (i = 0; i < MAX_PLAYERS; i++)
9757         {
9758           if (trigger_player_bits & (1 << i))
9759           {
9760             stored_player[i].key[KEY_NR(element)] = key_state;
9761
9762             DrawGameDoorValues();
9763           }
9764         }
9765       }
9766
9767       break;
9768     }
9769
9770     case CA_SET_PLAYER_SPEED:
9771     {
9772       for (i = 0; i < MAX_PLAYERS; i++)
9773       {
9774         if (trigger_player_bits & (1 << i))
9775         {
9776           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9777
9778           if (action_arg == CA_ARG_SPEED_FASTER &&
9779               stored_player[i].cannot_move)
9780           {
9781             action_arg_number = STEPSIZE_VERY_SLOW;
9782           }
9783           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9784                    action_arg == CA_ARG_SPEED_FASTER)
9785           {
9786             action_arg_number = 2;
9787             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9788                            CA_MODE_MULTIPLY);
9789           }
9790           else if (action_arg == CA_ARG_NUMBER_RESET)
9791           {
9792             action_arg_number = level.initial_player_stepsize[i];
9793           }
9794
9795           move_stepsize =
9796             getModifiedActionNumber(move_stepsize,
9797                                     action_mode,
9798                                     action_arg_number,
9799                                     action_arg_number_min,
9800                                     action_arg_number_max);
9801
9802           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9803         }
9804       }
9805
9806       break;
9807     }
9808
9809     case CA_SET_PLAYER_SHIELD:
9810     {
9811       for (i = 0; i < MAX_PLAYERS; i++)
9812       {
9813         if (trigger_player_bits & (1 << i))
9814         {
9815           if (action_arg == CA_ARG_SHIELD_OFF)
9816           {
9817             stored_player[i].shield_normal_time_left = 0;
9818             stored_player[i].shield_deadly_time_left = 0;
9819           }
9820           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9821           {
9822             stored_player[i].shield_normal_time_left = 999999;
9823           }
9824           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9825           {
9826             stored_player[i].shield_normal_time_left = 999999;
9827             stored_player[i].shield_deadly_time_left = 999999;
9828           }
9829         }
9830       }
9831
9832       break;
9833     }
9834
9835     case CA_SET_PLAYER_GRAVITY:
9836     {
9837       for (i = 0; i < MAX_PLAYERS; i++)
9838       {
9839         if (trigger_player_bits & (1 << i))
9840         {
9841           stored_player[i].gravity =
9842             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9843              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9844              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9845              stored_player[i].gravity);
9846         }
9847       }
9848
9849       break;
9850     }
9851
9852     case CA_SET_PLAYER_ARTWORK:
9853     {
9854       for (i = 0; i < MAX_PLAYERS; i++)
9855       {
9856         if (trigger_player_bits & (1 << i))
9857         {
9858           int artwork_element = action_arg_element;
9859
9860           if (action_arg == CA_ARG_ELEMENT_RESET)
9861             artwork_element =
9862               (level.use_artwork_element[i] ? level.artwork_element[i] :
9863                stored_player[i].element_nr);
9864
9865           if (stored_player[i].artwork_element != artwork_element)
9866             stored_player[i].Frame = 0;
9867
9868           stored_player[i].artwork_element = artwork_element;
9869
9870           SetPlayerWaiting(&stored_player[i], FALSE);
9871
9872           /* set number of special actions for bored and sleeping animation */
9873           stored_player[i].num_special_action_bored =
9874             get_num_special_action(artwork_element,
9875                                    ACTION_BORING_1, ACTION_BORING_LAST);
9876           stored_player[i].num_special_action_sleeping =
9877             get_num_special_action(artwork_element,
9878                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9879         }
9880       }
9881
9882       break;
9883     }
9884
9885     case CA_SET_PLAYER_INVENTORY:
9886     {
9887       for (i = 0; i < MAX_PLAYERS; i++)
9888       {
9889         struct PlayerInfo *player = &stored_player[i];
9890         int j, k;
9891
9892         if (trigger_player_bits & (1 << i))
9893         {
9894           int inventory_element = action_arg_element;
9895
9896           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9897               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9898               action_arg == CA_ARG_ELEMENT_ACTION)
9899           {
9900             int element = inventory_element;
9901             int collect_count = element_info[element].collect_count_initial;
9902
9903             if (!IS_CUSTOM_ELEMENT(element))
9904               collect_count = 1;
9905
9906             if (collect_count == 0)
9907               player->inventory_infinite_element = element;
9908             else
9909               for (k = 0; k < collect_count; k++)
9910                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9911                   player->inventory_element[player->inventory_size++] =
9912                     element;
9913           }
9914           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9915                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9916                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9917           {
9918             if (player->inventory_infinite_element != EL_UNDEFINED &&
9919                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9920                                      action_arg_element_raw))
9921               player->inventory_infinite_element = EL_UNDEFINED;
9922
9923             for (k = 0, j = 0; j < player->inventory_size; j++)
9924             {
9925               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9926                                         action_arg_element_raw))
9927                 player->inventory_element[k++] = player->inventory_element[j];
9928             }
9929
9930             player->inventory_size = k;
9931           }
9932           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9933           {
9934             if (player->inventory_size > 0)
9935             {
9936               for (j = 0; j < player->inventory_size - 1; j++)
9937                 player->inventory_element[j] = player->inventory_element[j + 1];
9938
9939               player->inventory_size--;
9940             }
9941           }
9942           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9943           {
9944             if (player->inventory_size > 0)
9945               player->inventory_size--;
9946           }
9947           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9948           {
9949             player->inventory_infinite_element = EL_UNDEFINED;
9950             player->inventory_size = 0;
9951           }
9952           else if (action_arg == CA_ARG_INVENTORY_RESET)
9953           {
9954             player->inventory_infinite_element = EL_UNDEFINED;
9955             player->inventory_size = 0;
9956
9957             if (level.use_initial_inventory[i])
9958             {
9959               for (j = 0; j < level.initial_inventory_size[i]; j++)
9960               {
9961                 int element = level.initial_inventory_content[i][j];
9962                 int collect_count = element_info[element].collect_count_initial;
9963
9964                 if (!IS_CUSTOM_ELEMENT(element))
9965                   collect_count = 1;
9966
9967                 if (collect_count == 0)
9968                   player->inventory_infinite_element = element;
9969                 else
9970                   for (k = 0; k < collect_count; k++)
9971                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9972                       player->inventory_element[player->inventory_size++] =
9973                         element;
9974               }
9975             }
9976           }
9977         }
9978       }
9979
9980       break;
9981     }
9982
9983     /* ---------- CE actions  ---------------------------------------------- */
9984
9985     case CA_SET_CE_VALUE:
9986     {
9987       int last_ce_value = CustomValue[x][y];
9988
9989       CustomValue[x][y] = action_arg_number_new;
9990
9991       if (CustomValue[x][y] != last_ce_value)
9992       {
9993         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9994         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9995
9996         if (CustomValue[x][y] == 0)
9997         {
9998           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9999           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10000         }
10001       }
10002
10003       break;
10004     }
10005
10006     case CA_SET_CE_SCORE:
10007     {
10008       int last_ce_score = ei->collect_score;
10009
10010       ei->collect_score = action_arg_number_new;
10011
10012       if (ei->collect_score != last_ce_score)
10013       {
10014         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10015         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10016
10017         if (ei->collect_score == 0)
10018         {
10019           int xx, yy;
10020
10021           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10022           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10023
10024           /*
10025             This is a very special case that seems to be a mixture between
10026             CheckElementChange() and CheckTriggeredElementChange(): while
10027             the first one only affects single elements that are triggered
10028             directly, the second one affects multiple elements in the playfield
10029             that are triggered indirectly by another element. This is a third
10030             case: Changing the CE score always affects multiple identical CEs,
10031             so every affected CE must be checked, not only the single CE for
10032             which the CE score was changed in the first place (as every instance
10033             of that CE shares the same CE score, and therefore also can change)!
10034           */
10035           SCAN_PLAYFIELD(xx, yy)
10036           {
10037             if (Feld[xx][yy] == element)
10038               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10039                                  CE_SCORE_GETS_ZERO);
10040           }
10041         }
10042       }
10043
10044       break;
10045     }
10046
10047     case CA_SET_CE_ARTWORK:
10048     {
10049       int artwork_element = action_arg_element;
10050       boolean reset_frame = FALSE;
10051       int xx, yy;
10052
10053       if (action_arg == CA_ARG_ELEMENT_RESET)
10054         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10055                            element);
10056
10057       if (ei->gfx_element != artwork_element)
10058         reset_frame = TRUE;
10059
10060       ei->gfx_element = artwork_element;
10061
10062       SCAN_PLAYFIELD(xx, yy)
10063       {
10064         if (Feld[xx][yy] == element)
10065         {
10066           if (reset_frame)
10067           {
10068             ResetGfxAnimation(xx, yy);
10069             ResetRandomAnimationValue(xx, yy);
10070           }
10071
10072           TEST_DrawLevelField(xx, yy);
10073         }
10074       }
10075
10076       break;
10077     }
10078
10079     /* ---------- engine actions  ------------------------------------------ */
10080
10081     case CA_SET_ENGINE_SCAN_MODE:
10082     {
10083       InitPlayfieldScanMode(action_arg);
10084
10085       break;
10086     }
10087
10088     default:
10089       break;
10090   }
10091 }
10092
10093 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10094 {
10095   int old_element = Feld[x][y];
10096   int new_element = GetElementFromGroupElement(element);
10097   int previous_move_direction = MovDir[x][y];
10098   int last_ce_value = CustomValue[x][y];
10099   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10100   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10101   boolean add_player_onto_element = (new_element_is_player &&
10102                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10103                                      IS_WALKABLE(old_element));
10104
10105   if (!add_player_onto_element)
10106   {
10107     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10108       RemoveMovingField(x, y);
10109     else
10110       RemoveField(x, y);
10111
10112     Feld[x][y] = new_element;
10113
10114     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10115       MovDir[x][y] = previous_move_direction;
10116
10117     if (element_info[new_element].use_last_ce_value)
10118       CustomValue[x][y] = last_ce_value;
10119
10120     InitField_WithBug1(x, y, FALSE);
10121
10122     new_element = Feld[x][y];   /* element may have changed */
10123
10124     ResetGfxAnimation(x, y);
10125     ResetRandomAnimationValue(x, y);
10126
10127     TEST_DrawLevelField(x, y);
10128
10129     if (GFX_CRUMBLED(new_element))
10130       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10131   }
10132
10133   /* check if element under the player changes from accessible to unaccessible
10134      (needed for special case of dropping element which then changes) */
10135   /* (must be checked after creating new element for walkable group elements) */
10136   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10137       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10138   {
10139     Bang(x, y);
10140
10141     return;
10142   }
10143
10144   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10145   if (new_element_is_player)
10146     RelocatePlayer(x, y, new_element);
10147
10148   if (is_change)
10149     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10150
10151   TestIfBadThingTouchesPlayer(x, y);
10152   TestIfPlayerTouchesCustomElement(x, y);
10153   TestIfElementTouchesCustomElement(x, y);
10154 }
10155
10156 static void CreateField(int x, int y, int element)
10157 {
10158   CreateFieldExt(x, y, element, FALSE);
10159 }
10160
10161 static void CreateElementFromChange(int x, int y, int element)
10162 {
10163   element = GET_VALID_RUNTIME_ELEMENT(element);
10164
10165   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10166   {
10167     int old_element = Feld[x][y];
10168
10169     /* prevent changed element from moving in same engine frame
10170        unless both old and new element can either fall or move */
10171     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10172         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10173       Stop[x][y] = TRUE;
10174   }
10175
10176   CreateFieldExt(x, y, element, TRUE);
10177 }
10178
10179 static boolean ChangeElement(int x, int y, int element, int page)
10180 {
10181   struct ElementInfo *ei = &element_info[element];
10182   struct ElementChangeInfo *change = &ei->change_page[page];
10183   int ce_value = CustomValue[x][y];
10184   int ce_score = ei->collect_score;
10185   int target_element;
10186   int old_element = Feld[x][y];
10187
10188   /* always use default change event to prevent running into a loop */
10189   if (ChangeEvent[x][y] == -1)
10190     ChangeEvent[x][y] = CE_DELAY;
10191
10192   if (ChangeEvent[x][y] == CE_DELAY)
10193   {
10194     /* reset actual trigger element, trigger player and action element */
10195     change->actual_trigger_element = EL_EMPTY;
10196     change->actual_trigger_player = EL_EMPTY;
10197     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10198     change->actual_trigger_side = CH_SIDE_NONE;
10199     change->actual_trigger_ce_value = 0;
10200     change->actual_trigger_ce_score = 0;
10201   }
10202
10203   /* do not change elements more than a specified maximum number of changes */
10204   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10205     return FALSE;
10206
10207   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10208
10209   if (change->explode)
10210   {
10211     Bang(x, y);
10212
10213     return TRUE;
10214   }
10215
10216   if (change->use_target_content)
10217   {
10218     boolean complete_replace = TRUE;
10219     boolean can_replace[3][3];
10220     int xx, yy;
10221
10222     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10223     {
10224       boolean is_empty;
10225       boolean is_walkable;
10226       boolean is_diggable;
10227       boolean is_collectible;
10228       boolean is_removable;
10229       boolean is_destructible;
10230       int ex = x + xx - 1;
10231       int ey = y + yy - 1;
10232       int content_element = change->target_content.e[xx][yy];
10233       int e;
10234
10235       can_replace[xx][yy] = TRUE;
10236
10237       if (ex == x && ey == y)   /* do not check changing element itself */
10238         continue;
10239
10240       if (content_element == EL_EMPTY_SPACE)
10241       {
10242         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10243
10244         continue;
10245       }
10246
10247       if (!IN_LEV_FIELD(ex, ey))
10248       {
10249         can_replace[xx][yy] = FALSE;
10250         complete_replace = FALSE;
10251
10252         continue;
10253       }
10254
10255       e = Feld[ex][ey];
10256
10257       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10258         e = MovingOrBlocked2Element(ex, ey);
10259
10260       is_empty = (IS_FREE(ex, ey) ||
10261                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10262
10263       is_walkable     = (is_empty || IS_WALKABLE(e));
10264       is_diggable     = (is_empty || IS_DIGGABLE(e));
10265       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10266       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10267       is_removable    = (is_diggable || is_collectible);
10268
10269       can_replace[xx][yy] =
10270         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10271           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10272           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10273           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10274           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10275           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10276          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10277
10278       if (!can_replace[xx][yy])
10279         complete_replace = FALSE;
10280     }
10281
10282     if (!change->only_if_complete || complete_replace)
10283     {
10284       boolean something_has_changed = FALSE;
10285
10286       if (change->only_if_complete && change->use_random_replace &&
10287           RND(100) < change->random_percentage)
10288         return FALSE;
10289
10290       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10291       {
10292         int ex = x + xx - 1;
10293         int ey = y + yy - 1;
10294         int content_element;
10295
10296         if (can_replace[xx][yy] && (!change->use_random_replace ||
10297                                     RND(100) < change->random_percentage))
10298         {
10299           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10300             RemoveMovingField(ex, ey);
10301
10302           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10303
10304           content_element = change->target_content.e[xx][yy];
10305           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10306                                               ce_value, ce_score);
10307
10308           CreateElementFromChange(ex, ey, target_element);
10309
10310           something_has_changed = TRUE;
10311
10312           /* for symmetry reasons, freeze newly created border elements */
10313           if (ex != x || ey != y)
10314             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10315         }
10316       }
10317
10318       if (something_has_changed)
10319       {
10320         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10321         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10322       }
10323     }
10324   }
10325   else
10326   {
10327     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10328                                         ce_value, ce_score);
10329
10330     if (element == EL_DIAGONAL_GROWING ||
10331         element == EL_DIAGONAL_SHRINKING)
10332     {
10333       target_element = Store[x][y];
10334
10335       Store[x][y] = EL_EMPTY;
10336     }
10337
10338     CreateElementFromChange(x, y, target_element);
10339
10340     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10341     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10342   }
10343
10344   /* this uses direct change before indirect change */
10345   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10346
10347   return TRUE;
10348 }
10349
10350 static void HandleElementChange(int x, int y, int page)
10351 {
10352   int element = MovingOrBlocked2Element(x, y);
10353   struct ElementInfo *ei = &element_info[element];
10354   struct ElementChangeInfo *change = &ei->change_page[page];
10355   boolean handle_action_before_change = FALSE;
10356
10357 #ifdef DEBUG
10358   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10359       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10360   {
10361     printf("\n\n");
10362     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10363            x, y, element, element_info[element].token_name);
10364     printf("HandleElementChange(): This should never happen!\n");
10365     printf("\n\n");
10366   }
10367 #endif
10368
10369   /* this can happen with classic bombs on walkable, changing elements */
10370   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10371   {
10372     return;
10373   }
10374
10375   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10376   {
10377     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10378
10379     if (change->can_change)
10380     {
10381       /* !!! not clear why graphic animation should be reset at all here !!! */
10382       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10383       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10384
10385       /*
10386         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10387
10388         When using an animation frame delay of 1 (this only happens with
10389         "sp_zonk.moving.left/right" in the classic graphics), the default
10390         (non-moving) animation shows wrong animation frames (while the
10391         moving animation, like "sp_zonk.moving.left/right", is correct,
10392         so this graphical bug never shows up with the classic graphics).
10393         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10394         be drawn instead of the correct frames 0,1,2,3. This is caused by
10395         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10396         an element change: First when the change delay ("ChangeDelay[][]")
10397         counter has reached zero after decrementing, then a second time in
10398         the next frame (after "GfxFrame[][]" was already incremented) when
10399         "ChangeDelay[][]" is reset to the initial delay value again.
10400
10401         This causes frame 0 to be drawn twice, while the last frame won't
10402         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10403
10404         As some animations may already be cleverly designed around this bug
10405         (at least the "Snake Bite" snake tail animation does this), it cannot
10406         simply be fixed here without breaking such existing animations.
10407         Unfortunately, it cannot easily be detected if a graphics set was
10408         designed "before" or "after" the bug was fixed. As a workaround,
10409         a new graphics set option "game.graphics_engine_version" was added
10410         to be able to specify the game's major release version for which the
10411         graphics set was designed, which can then be used to decide if the
10412         bugfix should be used (version 4 and above) or not (version 3 or
10413         below, or if no version was specified at all, as with old sets).
10414
10415         (The wrong/fixed animation frames can be tested with the test level set
10416         "test_gfxframe" and level "000", which contains a specially prepared
10417         custom element at level position (x/y) == (11/9) which uses the zonk
10418         animation mentioned above. Using "game.graphics_engine_version: 4"
10419         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10420         This can also be seen from the debug output for this test element.)
10421       */
10422
10423       /* when a custom element is about to change (for example by change delay),
10424          do not reset graphic animation when the custom element is moving */
10425       if (game.graphics_engine_version < 4 &&
10426           !IS_MOVING(x, y))
10427       {
10428         ResetGfxAnimation(x, y);
10429         ResetRandomAnimationValue(x, y);
10430       }
10431
10432       if (change->pre_change_function)
10433         change->pre_change_function(x, y);
10434     }
10435   }
10436
10437   ChangeDelay[x][y]--;
10438
10439   if (ChangeDelay[x][y] != 0)           /* continue element change */
10440   {
10441     if (change->can_change)
10442     {
10443       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10444
10445       if (IS_ANIMATED(graphic))
10446         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10447
10448       if (change->change_function)
10449         change->change_function(x, y);
10450     }
10451   }
10452   else                                  /* finish element change */
10453   {
10454     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10455     {
10456       page = ChangePage[x][y];
10457       ChangePage[x][y] = -1;
10458
10459       change = &ei->change_page[page];
10460     }
10461
10462     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10463     {
10464       ChangeDelay[x][y] = 1;            /* try change after next move step */
10465       ChangePage[x][y] = page;          /* remember page to use for change */
10466
10467       return;
10468     }
10469
10470     /* special case: set new level random seed before changing element */
10471     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10472       handle_action_before_change = TRUE;
10473
10474     if (change->has_action && handle_action_before_change)
10475       ExecuteCustomElementAction(x, y, element, page);
10476
10477     if (change->can_change)
10478     {
10479       if (ChangeElement(x, y, element, page))
10480       {
10481         if (change->post_change_function)
10482           change->post_change_function(x, y);
10483       }
10484     }
10485
10486     if (change->has_action && !handle_action_before_change)
10487       ExecuteCustomElementAction(x, y, element, page);
10488   }
10489 }
10490
10491 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10492                                               int trigger_element,
10493                                               int trigger_event,
10494                                               int trigger_player,
10495                                               int trigger_side,
10496                                               int trigger_page)
10497 {
10498   boolean change_done_any = FALSE;
10499   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10500   int i;
10501
10502   if (!(trigger_events[trigger_element][trigger_event]))
10503     return FALSE;
10504
10505   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10506
10507   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10508   {
10509     int element = EL_CUSTOM_START + i;
10510     boolean change_done = FALSE;
10511     int p;
10512
10513     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10514         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10515       continue;
10516
10517     for (p = 0; p < element_info[element].num_change_pages; p++)
10518     {
10519       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10520
10521       if (change->can_change_or_has_action &&
10522           change->has_event[trigger_event] &&
10523           change->trigger_side & trigger_side &&
10524           change->trigger_player & trigger_player &&
10525           change->trigger_page & trigger_page_bits &&
10526           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10527       {
10528         change->actual_trigger_element = trigger_element;
10529         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10530         change->actual_trigger_player_bits = trigger_player;
10531         change->actual_trigger_side = trigger_side;
10532         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10533         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10534
10535         if ((change->can_change && !change_done) || change->has_action)
10536         {
10537           int x, y;
10538
10539           SCAN_PLAYFIELD(x, y)
10540           {
10541             if (Feld[x][y] == element)
10542             {
10543               if (change->can_change && !change_done)
10544               {
10545                 /* if element already changed in this frame, not only prevent
10546                    another element change (checked in ChangeElement()), but
10547                    also prevent additional element actions for this element */
10548
10549                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10550                     !level.use_action_after_change_bug)
10551                   continue;
10552
10553                 ChangeDelay[x][y] = 1;
10554                 ChangeEvent[x][y] = trigger_event;
10555
10556                 HandleElementChange(x, y, p);
10557               }
10558               else if (change->has_action)
10559               {
10560                 /* if element already changed in this frame, not only prevent
10561                    another element change (checked in ChangeElement()), but
10562                    also prevent additional element actions for this element */
10563
10564                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10565                     !level.use_action_after_change_bug)
10566                   continue;
10567
10568                 ExecuteCustomElementAction(x, y, element, p);
10569                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10570               }
10571             }
10572           }
10573
10574           if (change->can_change)
10575           {
10576             change_done = TRUE;
10577             change_done_any = TRUE;
10578           }
10579         }
10580       }
10581     }
10582   }
10583
10584   RECURSION_LOOP_DETECTION_END();
10585
10586   return change_done_any;
10587 }
10588
10589 static boolean CheckElementChangeExt(int x, int y,
10590                                      int element,
10591                                      int trigger_element,
10592                                      int trigger_event,
10593                                      int trigger_player,
10594                                      int trigger_side)
10595 {
10596   boolean change_done = FALSE;
10597   int p;
10598
10599   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10600       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10601     return FALSE;
10602
10603   if (Feld[x][y] == EL_BLOCKED)
10604   {
10605     Blocked2Moving(x, y, &x, &y);
10606     element = Feld[x][y];
10607   }
10608
10609   /* check if element has already changed or is about to change after moving */
10610   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10611        Feld[x][y] != element) ||
10612
10613       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10614        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10615         ChangePage[x][y] != -1)))
10616     return FALSE;
10617
10618   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10619
10620   for (p = 0; p < element_info[element].num_change_pages; p++)
10621   {
10622     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10623
10624     /* check trigger element for all events where the element that is checked
10625        for changing interacts with a directly adjacent element -- this is
10626        different to element changes that affect other elements to change on the
10627        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10628     boolean check_trigger_element =
10629       (trigger_event == CE_TOUCHING_X ||
10630        trigger_event == CE_HITTING_X ||
10631        trigger_event == CE_HIT_BY_X ||
10632        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10633
10634     if (change->can_change_or_has_action &&
10635         change->has_event[trigger_event] &&
10636         change->trigger_side & trigger_side &&
10637         change->trigger_player & trigger_player &&
10638         (!check_trigger_element ||
10639          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10640     {
10641       change->actual_trigger_element = trigger_element;
10642       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10643       change->actual_trigger_player_bits = trigger_player;
10644       change->actual_trigger_side = trigger_side;
10645       change->actual_trigger_ce_value = CustomValue[x][y];
10646       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10647
10648       /* special case: trigger element not at (x,y) position for some events */
10649       if (check_trigger_element)
10650       {
10651         static struct
10652         {
10653           int dx, dy;
10654         } move_xy[] =
10655           {
10656             {  0,  0 },
10657             { -1,  0 },
10658             { +1,  0 },
10659             {  0,  0 },
10660             {  0, -1 },
10661             {  0,  0 }, { 0, 0 }, { 0, 0 },
10662             {  0, +1 }
10663           };
10664
10665         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10666         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10667
10668         change->actual_trigger_ce_value = CustomValue[xx][yy];
10669         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10670       }
10671
10672       if (change->can_change && !change_done)
10673       {
10674         ChangeDelay[x][y] = 1;
10675         ChangeEvent[x][y] = trigger_event;
10676
10677         HandleElementChange(x, y, p);
10678
10679         change_done = TRUE;
10680       }
10681       else if (change->has_action)
10682       {
10683         ExecuteCustomElementAction(x, y, element, p);
10684         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10685       }
10686     }
10687   }
10688
10689   RECURSION_LOOP_DETECTION_END();
10690
10691   return change_done;
10692 }
10693
10694 static void PlayPlayerSound(struct PlayerInfo *player)
10695 {
10696   int jx = player->jx, jy = player->jy;
10697   int sound_element = player->artwork_element;
10698   int last_action = player->last_action_waiting;
10699   int action = player->action_waiting;
10700
10701   if (player->is_waiting)
10702   {
10703     if (action != last_action)
10704       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10705     else
10706       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10707   }
10708   else
10709   {
10710     if (action != last_action)
10711       StopSound(element_info[sound_element].sound[last_action]);
10712
10713     if (last_action == ACTION_SLEEPING)
10714       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10715   }
10716 }
10717
10718 static void PlayAllPlayersSound()
10719 {
10720   int i;
10721
10722   for (i = 0; i < MAX_PLAYERS; i++)
10723     if (stored_player[i].active)
10724       PlayPlayerSound(&stored_player[i]);
10725 }
10726
10727 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10728 {
10729   boolean last_waiting = player->is_waiting;
10730   int move_dir = player->MovDir;
10731
10732   player->dir_waiting = move_dir;
10733   player->last_action_waiting = player->action_waiting;
10734
10735   if (is_waiting)
10736   {
10737     if (!last_waiting)          /* not waiting -> waiting */
10738     {
10739       player->is_waiting = TRUE;
10740
10741       player->frame_counter_bored =
10742         FrameCounter +
10743         game.player_boring_delay_fixed +
10744         GetSimpleRandom(game.player_boring_delay_random);
10745       player->frame_counter_sleeping =
10746         FrameCounter +
10747         game.player_sleeping_delay_fixed +
10748         GetSimpleRandom(game.player_sleeping_delay_random);
10749
10750       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10751     }
10752
10753     if (game.player_sleeping_delay_fixed +
10754         game.player_sleeping_delay_random > 0 &&
10755         player->anim_delay_counter == 0 &&
10756         player->post_delay_counter == 0 &&
10757         FrameCounter >= player->frame_counter_sleeping)
10758       player->is_sleeping = TRUE;
10759     else if (game.player_boring_delay_fixed +
10760              game.player_boring_delay_random > 0 &&
10761              FrameCounter >= player->frame_counter_bored)
10762       player->is_bored = TRUE;
10763
10764     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10765                               player->is_bored ? ACTION_BORING :
10766                               ACTION_WAITING);
10767
10768     if (player->is_sleeping && player->use_murphy)
10769     {
10770       /* special case for sleeping Murphy when leaning against non-free tile */
10771
10772       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10773           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10774            !IS_MOVING(player->jx - 1, player->jy)))
10775         move_dir = MV_LEFT;
10776       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10777                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10778                 !IS_MOVING(player->jx + 1, player->jy)))
10779         move_dir = MV_RIGHT;
10780       else
10781         player->is_sleeping = FALSE;
10782
10783       player->dir_waiting = move_dir;
10784     }
10785
10786     if (player->is_sleeping)
10787     {
10788       if (player->num_special_action_sleeping > 0)
10789       {
10790         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10791         {
10792           int last_special_action = player->special_action_sleeping;
10793           int num_special_action = player->num_special_action_sleeping;
10794           int special_action =
10795             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10796              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10797              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10798              last_special_action + 1 : ACTION_SLEEPING);
10799           int special_graphic =
10800             el_act_dir2img(player->artwork_element, special_action, move_dir);
10801
10802           player->anim_delay_counter =
10803             graphic_info[special_graphic].anim_delay_fixed +
10804             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10805           player->post_delay_counter =
10806             graphic_info[special_graphic].post_delay_fixed +
10807             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10808
10809           player->special_action_sleeping = special_action;
10810         }
10811
10812         if (player->anim_delay_counter > 0)
10813         {
10814           player->action_waiting = player->special_action_sleeping;
10815           player->anim_delay_counter--;
10816         }
10817         else if (player->post_delay_counter > 0)
10818         {
10819           player->post_delay_counter--;
10820         }
10821       }
10822     }
10823     else if (player->is_bored)
10824     {
10825       if (player->num_special_action_bored > 0)
10826       {
10827         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10828         {
10829           int special_action =
10830             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10831           int special_graphic =
10832             el_act_dir2img(player->artwork_element, special_action, move_dir);
10833
10834           player->anim_delay_counter =
10835             graphic_info[special_graphic].anim_delay_fixed +
10836             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10837           player->post_delay_counter =
10838             graphic_info[special_graphic].post_delay_fixed +
10839             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10840
10841           player->special_action_bored = special_action;
10842         }
10843
10844         if (player->anim_delay_counter > 0)
10845         {
10846           player->action_waiting = player->special_action_bored;
10847           player->anim_delay_counter--;
10848         }
10849         else if (player->post_delay_counter > 0)
10850         {
10851           player->post_delay_counter--;
10852         }
10853       }
10854     }
10855   }
10856   else if (last_waiting)        /* waiting -> not waiting */
10857   {
10858     player->is_waiting = FALSE;
10859     player->is_bored = FALSE;
10860     player->is_sleeping = FALSE;
10861
10862     player->frame_counter_bored = -1;
10863     player->frame_counter_sleeping = -1;
10864
10865     player->anim_delay_counter = 0;
10866     player->post_delay_counter = 0;
10867
10868     player->dir_waiting = player->MovDir;
10869     player->action_waiting = ACTION_DEFAULT;
10870
10871     player->special_action_bored = ACTION_DEFAULT;
10872     player->special_action_sleeping = ACTION_DEFAULT;
10873   }
10874 }
10875
10876 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10877 {
10878   if ((!player->is_moving  && player->was_moving) ||
10879       (player->MovPos == 0 && player->was_moving) ||
10880       (player->is_snapping && !player->was_snapping) ||
10881       (player->is_dropping && !player->was_dropping))
10882   {
10883     if (!CheckSaveEngineSnapshotToList())
10884       return;
10885
10886     player->was_moving = FALSE;
10887     player->was_snapping = TRUE;
10888     player->was_dropping = TRUE;
10889   }
10890   else
10891   {
10892     if (player->is_moving)
10893       player->was_moving = TRUE;
10894
10895     if (!player->is_snapping)
10896       player->was_snapping = FALSE;
10897
10898     if (!player->is_dropping)
10899       player->was_dropping = FALSE;
10900   }
10901 }
10902
10903 static void CheckSingleStepMode(struct PlayerInfo *player)
10904 {
10905   if (tape.single_step && tape.recording && !tape.pausing)
10906   {
10907     /* as it is called "single step mode", just return to pause mode when the
10908        player stopped moving after one tile (or never starts moving at all) */
10909     if (!player->is_moving &&
10910         !player->is_pushing &&
10911         !player->is_dropping_pressed)
10912     {
10913       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10914       SnapField(player, 0, 0);                  /* stop snapping */
10915     }
10916   }
10917
10918   CheckSaveEngineSnapshot(player);
10919 }
10920
10921 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10922 {
10923   int left      = player_action & JOY_LEFT;
10924   int right     = player_action & JOY_RIGHT;
10925   int up        = player_action & JOY_UP;
10926   int down      = player_action & JOY_DOWN;
10927   int button1   = player_action & JOY_BUTTON_1;
10928   int button2   = player_action & JOY_BUTTON_2;
10929   int dx        = (left ? -1 : right ? 1 : 0);
10930   int dy        = (up   ? -1 : down  ? 1 : 0);
10931
10932   if (!player->active || tape.pausing)
10933     return 0;
10934
10935   if (player_action)
10936   {
10937     if (button1)
10938       SnapField(player, dx, dy);
10939     else
10940     {
10941       if (button2)
10942         DropElement(player);
10943
10944       MovePlayer(player, dx, dy);
10945     }
10946
10947     CheckSingleStepMode(player);
10948
10949     SetPlayerWaiting(player, FALSE);
10950
10951     return player_action;
10952   }
10953   else
10954   {
10955     /* no actions for this player (no input at player's configured device) */
10956
10957     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10958     SnapField(player, 0, 0);
10959     CheckGravityMovementWhenNotMoving(player);
10960
10961     if (player->MovPos == 0)
10962       SetPlayerWaiting(player, TRUE);
10963
10964     if (player->MovPos == 0)    /* needed for tape.playing */
10965       player->is_moving = FALSE;
10966
10967     player->is_dropping = FALSE;
10968     player->is_dropping_pressed = FALSE;
10969     player->drop_pressed_delay = 0;
10970
10971     CheckSingleStepMode(player);
10972
10973     return 0;
10974   }
10975 }
10976
10977 static void CheckLevelTime()
10978 {
10979   int i;
10980
10981   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10982   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10983   {
10984     if (level.native_em_level->lev->home == 0)  /* all players at home */
10985     {
10986       PlayerWins(local_player);
10987
10988       AllPlayersGone = TRUE;
10989
10990       level.native_em_level->lev->home = -1;
10991     }
10992
10993     if (level.native_em_level->ply[0]->alive == 0 &&
10994         level.native_em_level->ply[1]->alive == 0 &&
10995         level.native_em_level->ply[2]->alive == 0 &&
10996         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10997       AllPlayersGone = TRUE;
10998   }
10999   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11000   {
11001     if (game_sp.LevelSolved &&
11002         !game_sp.GameOver)                              /* game won */
11003     {
11004       PlayerWins(local_player);
11005
11006       game_sp.GameOver = TRUE;
11007
11008       AllPlayersGone = TRUE;
11009     }
11010
11011     if (game_sp.GameOver)                               /* game lost */
11012       AllPlayersGone = TRUE;
11013   }
11014   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11015   {
11016     if (game_mm.level_solved &&
11017         !game_mm.game_over)                             /* game won */
11018     {
11019       PlayerWins(local_player);
11020
11021       game_mm.game_over = TRUE;
11022
11023       AllPlayersGone = TRUE;
11024     }
11025
11026     if (game_mm.game_over)                              /* game lost */
11027       AllPlayersGone = TRUE;
11028   }
11029
11030   if (TimeFrames >= FRAMES_PER_SECOND)
11031   {
11032     TimeFrames = 0;
11033     TapeTime++;
11034
11035     for (i = 0; i < MAX_PLAYERS; i++)
11036     {
11037       struct PlayerInfo *player = &stored_player[i];
11038
11039       if (SHIELD_ON(player))
11040       {
11041         player->shield_normal_time_left--;
11042
11043         if (player->shield_deadly_time_left > 0)
11044           player->shield_deadly_time_left--;
11045       }
11046     }
11047
11048     if (!local_player->LevelSolved && !level.use_step_counter)
11049     {
11050       TimePlayed++;
11051
11052       if (TimeLeft > 0)
11053       {
11054         TimeLeft--;
11055
11056         if (TimeLeft <= 10 && setup.time_limit)
11057           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11058
11059         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11060            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11061
11062         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11063
11064         if (!TimeLeft && setup.time_limit)
11065         {
11066           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11067             level.native_em_level->lev->killed_out_of_time = TRUE;
11068           else
11069             for (i = 0; i < MAX_PLAYERS; i++)
11070               KillPlayer(&stored_player[i]);
11071         }
11072       }
11073       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11074       {
11075         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11076       }
11077
11078       level.native_em_level->lev->time =
11079         (game.no_time_limit ? TimePlayed : TimeLeft);
11080     }
11081
11082     if (tape.recording || tape.playing)
11083       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11084   }
11085
11086   if (tape.recording || tape.playing)
11087     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11088
11089   UpdateAndDisplayGameControlValues();
11090 }
11091
11092 void AdvanceFrameAndPlayerCounters(int player_nr)
11093 {
11094   int i;
11095
11096   /* advance frame counters (global frame counter and time frame counter) */
11097   FrameCounter++;
11098   TimeFrames++;
11099
11100   /* advance player counters (counters for move delay, move animation etc.) */
11101   for (i = 0; i < MAX_PLAYERS; i++)
11102   {
11103     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11104     int move_delay_value = stored_player[i].move_delay_value;
11105     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11106
11107     if (!advance_player_counters)       /* not all players may be affected */
11108       continue;
11109
11110     if (move_frames == 0)       /* less than one move per game frame */
11111     {
11112       int stepsize = TILEX / move_delay_value;
11113       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11114       int count = (stored_player[i].is_moving ?
11115                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11116
11117       if (count % delay == 0)
11118         move_frames = 1;
11119     }
11120
11121     stored_player[i].Frame += move_frames;
11122
11123     if (stored_player[i].MovPos != 0)
11124       stored_player[i].StepFrame += move_frames;
11125
11126     if (stored_player[i].move_delay > 0)
11127       stored_player[i].move_delay--;
11128
11129     /* due to bugs in previous versions, counter must count up, not down */
11130     if (stored_player[i].push_delay != -1)
11131       stored_player[i].push_delay++;
11132
11133     if (stored_player[i].drop_delay > 0)
11134       stored_player[i].drop_delay--;
11135
11136     if (stored_player[i].is_dropping_pressed)
11137       stored_player[i].drop_pressed_delay++;
11138   }
11139 }
11140
11141 void StartGameActions(boolean init_network_game, boolean record_tape,
11142                       int random_seed)
11143 {
11144   unsigned int new_random_seed = InitRND(random_seed);
11145
11146   if (record_tape)
11147     TapeStartRecording(new_random_seed);
11148
11149 #if defined(NETWORK_AVALIABLE)
11150   if (init_network_game)
11151   {
11152     SendToServer_StartPlaying();
11153
11154     return;
11155   }
11156 #endif
11157
11158   InitGame();
11159 }
11160
11161 void GameActionsExt()
11162 {
11163 #if 0
11164   static unsigned int game_frame_delay = 0;
11165 #endif
11166   unsigned int game_frame_delay_value;
11167   byte *recorded_player_action;
11168   byte summarized_player_action = 0;
11169   byte tape_action[MAX_PLAYERS];
11170   int i;
11171
11172   /* detect endless loops, caused by custom element programming */
11173   if (recursion_loop_detected && recursion_loop_depth == 0)
11174   {
11175     char *message = getStringCat3("Internal Error! Element ",
11176                                   EL_NAME(recursion_loop_element),
11177                                   " caused endless loop! Quit the game?");
11178
11179     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11180           EL_NAME(recursion_loop_element));
11181
11182     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11183
11184     recursion_loop_detected = FALSE;    /* if game should be continued */
11185
11186     free(message);
11187
11188     return;
11189   }
11190
11191   if (game.restart_level)
11192     StartGameActions(options.network, setup.autorecord, level.random_seed);
11193
11194   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11195   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11196   {
11197     if (level.native_em_level->lev->home == 0)  /* all players at home */
11198     {
11199       PlayerWins(local_player);
11200
11201       AllPlayersGone = TRUE;
11202
11203       level.native_em_level->lev->home = -1;
11204     }
11205
11206     if (level.native_em_level->ply[0]->alive == 0 &&
11207         level.native_em_level->ply[1]->alive == 0 &&
11208         level.native_em_level->ply[2]->alive == 0 &&
11209         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11210       AllPlayersGone = TRUE;
11211   }
11212   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11213   {
11214     if (game_sp.LevelSolved &&
11215         !game_sp.GameOver)                              /* game won */
11216     {
11217       PlayerWins(local_player);
11218
11219       game_sp.GameOver = TRUE;
11220
11221       AllPlayersGone = TRUE;
11222     }
11223
11224     if (game_sp.GameOver)                               /* game lost */
11225       AllPlayersGone = TRUE;
11226   }
11227   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11228   {
11229     if (game_mm.level_solved &&
11230         !game_mm.game_over)                             /* game won */
11231     {
11232       PlayerWins(local_player);
11233
11234       game_mm.game_over = TRUE;
11235
11236       AllPlayersGone = TRUE;
11237     }
11238
11239     if (game_mm.game_over)                              /* game lost */
11240       AllPlayersGone = TRUE;
11241   }
11242
11243   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11244     GameWon();
11245
11246   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11247     TapeStop();
11248
11249   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11250     return;
11251
11252   game_frame_delay_value =
11253     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11254
11255   if (tape.playing && tape.warp_forward && !tape.pausing)
11256     game_frame_delay_value = 0;
11257
11258   SetVideoFrameDelay(game_frame_delay_value);
11259
11260 #if 0
11261 #if 0
11262   /* ---------- main game synchronization point ---------- */
11263
11264   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11265
11266   printf("::: skip == %d\n", skip);
11267
11268 #else
11269   /* ---------- main game synchronization point ---------- */
11270
11271   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11272 #endif
11273 #endif
11274
11275   if (network_playing && !network_player_action_received)
11276   {
11277     /* try to get network player actions in time */
11278
11279 #if defined(NETWORK_AVALIABLE)
11280     /* last chance to get network player actions without main loop delay */
11281     HandleNetworking();
11282 #endif
11283
11284     /* game was quit by network peer */
11285     if (game_status != GAME_MODE_PLAYING)
11286       return;
11287
11288     if (!network_player_action_received)
11289       return;           /* failed to get network player actions in time */
11290
11291     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11292   }
11293
11294   if (tape.pausing)
11295     return;
11296
11297   /* at this point we know that we really continue executing the game */
11298
11299   network_player_action_received = FALSE;
11300
11301   /* when playing tape, read previously recorded player input from tape data */
11302   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11303
11304   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11305   if (tape.pausing)
11306     return;
11307
11308   if (tape.set_centered_player)
11309   {
11310     game.centered_player_nr_next = tape.centered_player_nr_next;
11311     game.set_centered_player = TRUE;
11312   }
11313
11314   for (i = 0; i < MAX_PLAYERS; i++)
11315   {
11316     summarized_player_action |= stored_player[i].action;
11317
11318     if (!network_playing && (game.team_mode || tape.playing))
11319       stored_player[i].effective_action = stored_player[i].action;
11320   }
11321
11322 #if defined(NETWORK_AVALIABLE)
11323   if (network_playing)
11324     SendToServer_MovePlayer(summarized_player_action);
11325 #endif
11326
11327   // summarize all actions at local players mapped input device position
11328   // (this allows using different input devices in single player mode)
11329   if (!options.network && !game.team_mode)
11330     stored_player[map_player_action[local_player->index_nr]].effective_action =
11331       summarized_player_action;
11332
11333   if (tape.recording &&
11334       setup.team_mode &&
11335       setup.input_on_focus &&
11336       game.centered_player_nr != -1)
11337   {
11338     for (i = 0; i < MAX_PLAYERS; i++)
11339       stored_player[i].effective_action =
11340         (i == game.centered_player_nr ? summarized_player_action : 0);
11341   }
11342
11343   if (recorded_player_action != NULL)
11344     for (i = 0; i < MAX_PLAYERS; i++)
11345       stored_player[i].effective_action = recorded_player_action[i];
11346
11347   for (i = 0; i < MAX_PLAYERS; i++)
11348   {
11349     tape_action[i] = stored_player[i].effective_action;
11350
11351     /* (this may happen in the RND game engine if a player was not present on
11352        the playfield on level start, but appeared later from a custom element */
11353     if (setup.team_mode &&
11354         tape.recording &&
11355         tape_action[i] &&
11356         !tape.player_participates[i])
11357       tape.player_participates[i] = TRUE;
11358   }
11359
11360   /* only record actions from input devices, but not programmed actions */
11361   if (tape.recording)
11362     TapeRecordAction(tape_action);
11363
11364 #if USE_NEW_PLAYER_ASSIGNMENTS
11365   // !!! also map player actions in single player mode !!!
11366   // if (game.team_mode)
11367   if (1)
11368   {
11369     byte mapped_action[MAX_PLAYERS];
11370
11371 #if DEBUG_PLAYER_ACTIONS
11372     printf(":::");
11373     for (i = 0; i < MAX_PLAYERS; i++)
11374       printf(" %d, ", stored_player[i].effective_action);
11375 #endif
11376
11377     for (i = 0; i < MAX_PLAYERS; i++)
11378       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11379
11380     for (i = 0; i < MAX_PLAYERS; i++)
11381       stored_player[i].effective_action = mapped_action[i];
11382
11383 #if DEBUG_PLAYER_ACTIONS
11384     printf(" =>");
11385     for (i = 0; i < MAX_PLAYERS; i++)
11386       printf(" %d, ", stored_player[i].effective_action);
11387     printf("\n");
11388 #endif
11389   }
11390 #if DEBUG_PLAYER_ACTIONS
11391   else
11392   {
11393     printf(":::");
11394     for (i = 0; i < MAX_PLAYERS; i++)
11395       printf(" %d, ", stored_player[i].effective_action);
11396     printf("\n");
11397   }
11398 #endif
11399 #endif
11400
11401   for (i = 0; i < MAX_PLAYERS; i++)
11402   {
11403     // allow engine snapshot in case of changed movement attempt
11404     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11405         (stored_player[i].effective_action & KEY_MOTION))
11406       game.snapshot.changed_action = TRUE;
11407
11408     // allow engine snapshot in case of snapping/dropping attempt
11409     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11410         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11411       game.snapshot.changed_action = TRUE;
11412
11413     game.snapshot.last_action[i] = stored_player[i].effective_action;
11414   }
11415
11416   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11417   {
11418     GameActions_EM_Main();
11419   }
11420   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11421   {
11422     GameActions_SP_Main();
11423   }
11424   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11425   {
11426     GameActions_MM_Main();
11427   }
11428   else
11429   {
11430     GameActions_RND_Main();
11431   }
11432
11433   BlitScreenToBitmap(backbuffer);
11434
11435   CheckLevelTime();
11436
11437   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11438
11439   if (global.show_frames_per_second)
11440   {
11441     static unsigned int fps_counter = 0;
11442     static int fps_frames = 0;
11443     unsigned int fps_delay_ms = Counter() - fps_counter;
11444
11445     fps_frames++;
11446
11447     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11448     {
11449       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11450
11451       fps_frames = 0;
11452       fps_counter = Counter();
11453
11454       /* always draw FPS to screen after FPS value was updated */
11455       redraw_mask |= REDRAW_FPS;
11456     }
11457
11458     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11459     if (GetDrawDeactivationMask() == REDRAW_NONE)
11460       redraw_mask |= REDRAW_FPS;
11461   }
11462 }
11463
11464 static void GameActions_CheckSaveEngineSnapshot()
11465 {
11466   if (!game.snapshot.save_snapshot)
11467     return;
11468
11469   // clear flag for saving snapshot _before_ saving snapshot
11470   game.snapshot.save_snapshot = FALSE;
11471
11472   SaveEngineSnapshotToList();
11473 }
11474
11475 void GameActions()
11476 {
11477   GameActionsExt();
11478
11479   GameActions_CheckSaveEngineSnapshot();
11480 }
11481
11482 void GameActions_EM_Main()
11483 {
11484   byte effective_action[MAX_PLAYERS];
11485   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11486   int i;
11487
11488   for (i = 0; i < MAX_PLAYERS; i++)
11489     effective_action[i] = stored_player[i].effective_action;
11490
11491   GameActions_EM(effective_action, warp_mode);
11492 }
11493
11494 void GameActions_SP_Main()
11495 {
11496   byte effective_action[MAX_PLAYERS];
11497   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11498   int i;
11499
11500   for (i = 0; i < MAX_PLAYERS; i++)
11501     effective_action[i] = stored_player[i].effective_action;
11502
11503   GameActions_SP(effective_action, warp_mode);
11504
11505   for (i = 0; i < MAX_PLAYERS; i++)
11506   {
11507     if (stored_player[i].force_dropping)
11508       stored_player[i].action |= KEY_BUTTON_DROP;
11509
11510     stored_player[i].force_dropping = FALSE;
11511   }
11512 }
11513
11514 void GameActions_MM_Main()
11515 {
11516   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11517
11518   GameActions_MM(local_player->mouse_action, warp_mode);
11519 }
11520
11521 void GameActions_RND_Main()
11522 {
11523   GameActions_RND();
11524 }
11525
11526 void GameActions_RND()
11527 {
11528   int magic_wall_x = 0, magic_wall_y = 0;
11529   int i, x, y, element, graphic, last_gfx_frame;
11530
11531   InitPlayfieldScanModeVars();
11532
11533   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11534   {
11535     SCAN_PLAYFIELD(x, y)
11536     {
11537       ChangeCount[x][y] = 0;
11538       ChangeEvent[x][y] = -1;
11539     }
11540   }
11541
11542   if (game.set_centered_player)
11543   {
11544     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11545
11546     /* switching to "all players" only possible if all players fit to screen */
11547     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11548     {
11549       game.centered_player_nr_next = game.centered_player_nr;
11550       game.set_centered_player = FALSE;
11551     }
11552
11553     /* do not switch focus to non-existing (or non-active) player */
11554     if (game.centered_player_nr_next >= 0 &&
11555         !stored_player[game.centered_player_nr_next].active)
11556     {
11557       game.centered_player_nr_next = game.centered_player_nr;
11558       game.set_centered_player = FALSE;
11559     }
11560   }
11561
11562   if (game.set_centered_player &&
11563       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11564   {
11565     int sx, sy;
11566
11567     if (game.centered_player_nr_next == -1)
11568     {
11569       setScreenCenteredToAllPlayers(&sx, &sy);
11570     }
11571     else
11572     {
11573       sx = stored_player[game.centered_player_nr_next].jx;
11574       sy = stored_player[game.centered_player_nr_next].jy;
11575     }
11576
11577     game.centered_player_nr = game.centered_player_nr_next;
11578     game.set_centered_player = FALSE;
11579
11580     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11581     DrawGameDoorValues();
11582   }
11583
11584   for (i = 0; i < MAX_PLAYERS; i++)
11585   {
11586     int actual_player_action = stored_player[i].effective_action;
11587
11588 #if 1
11589     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11590        - rnd_equinox_tetrachloride 048
11591        - rnd_equinox_tetrachloride_ii 096
11592        - rnd_emanuel_schmieg 002
11593        - doctor_sloan_ww 001, 020
11594     */
11595     if (stored_player[i].MovPos == 0)
11596       CheckGravityMovement(&stored_player[i]);
11597 #endif
11598
11599     /* overwrite programmed action with tape action */
11600     if (stored_player[i].programmed_action)
11601       actual_player_action = stored_player[i].programmed_action;
11602
11603     PlayerActions(&stored_player[i], actual_player_action);
11604
11605     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11606   }
11607
11608   ScrollScreen(NULL, SCROLL_GO_ON);
11609
11610   /* for backwards compatibility, the following code emulates a fixed bug that
11611      occured when pushing elements (causing elements that just made their last
11612      pushing step to already (if possible) make their first falling step in the
11613      same game frame, which is bad); this code is also needed to use the famous
11614      "spring push bug" which is used in older levels and might be wanted to be
11615      used also in newer levels, but in this case the buggy pushing code is only
11616      affecting the "spring" element and no other elements */
11617
11618   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11619   {
11620     for (i = 0; i < MAX_PLAYERS; i++)
11621     {
11622       struct PlayerInfo *player = &stored_player[i];
11623       int x = player->jx;
11624       int y = player->jy;
11625
11626       if (player->active && player->is_pushing && player->is_moving &&
11627           IS_MOVING(x, y) &&
11628           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11629            Feld[x][y] == EL_SPRING))
11630       {
11631         ContinueMoving(x, y);
11632
11633         /* continue moving after pushing (this is actually a bug) */
11634         if (!IS_MOVING(x, y))
11635           Stop[x][y] = FALSE;
11636       }
11637     }
11638   }
11639
11640   SCAN_PLAYFIELD(x, y)
11641   {
11642     ChangeCount[x][y] = 0;
11643     ChangeEvent[x][y] = -1;
11644
11645     /* this must be handled before main playfield loop */
11646     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11647     {
11648       MovDelay[x][y]--;
11649       if (MovDelay[x][y] <= 0)
11650         RemoveField(x, y);
11651     }
11652
11653     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11654     {
11655       MovDelay[x][y]--;
11656       if (MovDelay[x][y] <= 0)
11657       {
11658         RemoveField(x, y);
11659         TEST_DrawLevelField(x, y);
11660
11661         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11662       }
11663     }
11664
11665 #if DEBUG
11666     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11667     {
11668       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11669       printf("GameActions(): This should never happen!\n");
11670
11671       ChangePage[x][y] = -1;
11672     }
11673 #endif
11674
11675     Stop[x][y] = FALSE;
11676     if (WasJustMoving[x][y] > 0)
11677       WasJustMoving[x][y]--;
11678     if (WasJustFalling[x][y] > 0)
11679       WasJustFalling[x][y]--;
11680     if (CheckCollision[x][y] > 0)
11681       CheckCollision[x][y]--;
11682     if (CheckImpact[x][y] > 0)
11683       CheckImpact[x][y]--;
11684
11685     GfxFrame[x][y]++;
11686
11687     /* reset finished pushing action (not done in ContinueMoving() to allow
11688        continuous pushing animation for elements with zero push delay) */
11689     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11690     {
11691       ResetGfxAnimation(x, y);
11692       TEST_DrawLevelField(x, y);
11693     }
11694
11695 #if DEBUG
11696     if (IS_BLOCKED(x, y))
11697     {
11698       int oldx, oldy;
11699
11700       Blocked2Moving(x, y, &oldx, &oldy);
11701       if (!IS_MOVING(oldx, oldy))
11702       {
11703         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11704         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11705         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11706         printf("GameActions(): This should never happen!\n");
11707       }
11708     }
11709 #endif
11710   }
11711
11712   SCAN_PLAYFIELD(x, y)
11713   {
11714     element = Feld[x][y];
11715     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11716     last_gfx_frame = GfxFrame[x][y];
11717
11718     ResetGfxFrame(x, y);
11719
11720     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11721       DrawLevelGraphicAnimation(x, y, graphic);
11722
11723     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11724         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11725       ResetRandomAnimationValue(x, y);
11726
11727     SetRandomAnimationValue(x, y);
11728
11729     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11730
11731     if (IS_INACTIVE(element))
11732     {
11733       if (IS_ANIMATED(graphic))
11734         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11735
11736       continue;
11737     }
11738
11739     /* this may take place after moving, so 'element' may have changed */
11740     if (IS_CHANGING(x, y) &&
11741         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11742     {
11743       int page = element_info[element].event_page_nr[CE_DELAY];
11744
11745       HandleElementChange(x, y, page);
11746
11747       element = Feld[x][y];
11748       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11749     }
11750
11751     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11752     {
11753       StartMoving(x, y);
11754
11755       element = Feld[x][y];
11756       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11757
11758       if (IS_ANIMATED(graphic) &&
11759           !IS_MOVING(x, y) &&
11760           !Stop[x][y])
11761         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11762
11763       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11764         TEST_DrawTwinkleOnField(x, y);
11765     }
11766     else if (element == EL_ACID)
11767     {
11768       if (!Stop[x][y])
11769         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11770     }
11771     else if ((element == EL_EXIT_OPEN ||
11772               element == EL_EM_EXIT_OPEN ||
11773               element == EL_SP_EXIT_OPEN ||
11774               element == EL_STEEL_EXIT_OPEN ||
11775               element == EL_EM_STEEL_EXIT_OPEN ||
11776               element == EL_SP_TERMINAL ||
11777               element == EL_SP_TERMINAL_ACTIVE ||
11778               element == EL_EXTRA_TIME ||
11779               element == EL_SHIELD_NORMAL ||
11780               element == EL_SHIELD_DEADLY) &&
11781              IS_ANIMATED(graphic))
11782       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11783     else if (IS_MOVING(x, y))
11784       ContinueMoving(x, y);
11785     else if (IS_ACTIVE_BOMB(element))
11786       CheckDynamite(x, y);
11787     else if (element == EL_AMOEBA_GROWING)
11788       AmoebeWaechst(x, y);
11789     else if (element == EL_AMOEBA_SHRINKING)
11790       AmoebaDisappearing(x, y);
11791
11792 #if !USE_NEW_AMOEBA_CODE
11793     else if (IS_AMOEBALIVE(element))
11794       AmoebeAbleger(x, y);
11795 #endif
11796
11797     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11798       Life(x, y);
11799     else if (element == EL_EXIT_CLOSED)
11800       CheckExit(x, y);
11801     else if (element == EL_EM_EXIT_CLOSED)
11802       CheckExitEM(x, y);
11803     else if (element == EL_STEEL_EXIT_CLOSED)
11804       CheckExitSteel(x, y);
11805     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11806       CheckExitSteelEM(x, y);
11807     else if (element == EL_SP_EXIT_CLOSED)
11808       CheckExitSP(x, y);
11809     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11810              element == EL_EXPANDABLE_STEELWALL_GROWING)
11811       MauerWaechst(x, y);
11812     else if (element == EL_EXPANDABLE_WALL ||
11813              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11814              element == EL_EXPANDABLE_WALL_VERTICAL ||
11815              element == EL_EXPANDABLE_WALL_ANY ||
11816              element == EL_BD_EXPANDABLE_WALL)
11817       MauerAbleger(x, y);
11818     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11819              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11820              element == EL_EXPANDABLE_STEELWALL_ANY)
11821       MauerAblegerStahl(x, y);
11822     else if (element == EL_FLAMES)
11823       CheckForDragon(x, y);
11824     else if (element == EL_EXPLOSION)
11825       ; /* drawing of correct explosion animation is handled separately */
11826     else if (element == EL_ELEMENT_SNAPPING ||
11827              element == EL_DIAGONAL_SHRINKING ||
11828              element == EL_DIAGONAL_GROWING)
11829     {
11830       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11831
11832       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11833     }
11834     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11835       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11836
11837     if (IS_BELT_ACTIVE(element))
11838       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11839
11840     if (game.magic_wall_active)
11841     {
11842       int jx = local_player->jx, jy = local_player->jy;
11843
11844       /* play the element sound at the position nearest to the player */
11845       if ((element == EL_MAGIC_WALL_FULL ||
11846            element == EL_MAGIC_WALL_ACTIVE ||
11847            element == EL_MAGIC_WALL_EMPTYING ||
11848            element == EL_BD_MAGIC_WALL_FULL ||
11849            element == EL_BD_MAGIC_WALL_ACTIVE ||
11850            element == EL_BD_MAGIC_WALL_EMPTYING ||
11851            element == EL_DC_MAGIC_WALL_FULL ||
11852            element == EL_DC_MAGIC_WALL_ACTIVE ||
11853            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11854           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11855       {
11856         magic_wall_x = x;
11857         magic_wall_y = y;
11858       }
11859     }
11860   }
11861
11862 #if USE_NEW_AMOEBA_CODE
11863   /* new experimental amoeba growth stuff */
11864   if (!(FrameCounter % 8))
11865   {
11866     static unsigned int random = 1684108901;
11867
11868     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11869     {
11870       x = RND(lev_fieldx);
11871       y = RND(lev_fieldy);
11872       element = Feld[x][y];
11873
11874       if (!IS_PLAYER(x,y) &&
11875           (element == EL_EMPTY ||
11876            CAN_GROW_INTO(element) ||
11877            element == EL_QUICKSAND_EMPTY ||
11878            element == EL_QUICKSAND_FAST_EMPTY ||
11879            element == EL_ACID_SPLASH_LEFT ||
11880            element == EL_ACID_SPLASH_RIGHT))
11881       {
11882         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11883             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11884             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11885             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11886           Feld[x][y] = EL_AMOEBA_DROP;
11887       }
11888
11889       random = random * 129 + 1;
11890     }
11891   }
11892 #endif
11893
11894   game.explosions_delayed = FALSE;
11895
11896   SCAN_PLAYFIELD(x, y)
11897   {
11898     element = Feld[x][y];
11899
11900     if (ExplodeField[x][y])
11901       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11902     else if (element == EL_EXPLOSION)
11903       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11904
11905     ExplodeField[x][y] = EX_TYPE_NONE;
11906   }
11907
11908   game.explosions_delayed = TRUE;
11909
11910   if (game.magic_wall_active)
11911   {
11912     if (!(game.magic_wall_time_left % 4))
11913     {
11914       int element = Feld[magic_wall_x][magic_wall_y];
11915
11916       if (element == EL_BD_MAGIC_WALL_FULL ||
11917           element == EL_BD_MAGIC_WALL_ACTIVE ||
11918           element == EL_BD_MAGIC_WALL_EMPTYING)
11919         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11920       else if (element == EL_DC_MAGIC_WALL_FULL ||
11921                element == EL_DC_MAGIC_WALL_ACTIVE ||
11922                element == EL_DC_MAGIC_WALL_EMPTYING)
11923         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11924       else
11925         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11926     }
11927
11928     if (game.magic_wall_time_left > 0)
11929     {
11930       game.magic_wall_time_left--;
11931
11932       if (!game.magic_wall_time_left)
11933       {
11934         SCAN_PLAYFIELD(x, y)
11935         {
11936           element = Feld[x][y];
11937
11938           if (element == EL_MAGIC_WALL_ACTIVE ||
11939               element == EL_MAGIC_WALL_FULL)
11940           {
11941             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11942             TEST_DrawLevelField(x, y);
11943           }
11944           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11945                    element == EL_BD_MAGIC_WALL_FULL)
11946           {
11947             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11948             TEST_DrawLevelField(x, y);
11949           }
11950           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11951                    element == EL_DC_MAGIC_WALL_FULL)
11952           {
11953             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11954             TEST_DrawLevelField(x, y);
11955           }
11956         }
11957
11958         game.magic_wall_active = FALSE;
11959       }
11960     }
11961   }
11962
11963   if (game.light_time_left > 0)
11964   {
11965     game.light_time_left--;
11966
11967     if (game.light_time_left == 0)
11968       RedrawAllLightSwitchesAndInvisibleElements();
11969   }
11970
11971   if (game.timegate_time_left > 0)
11972   {
11973     game.timegate_time_left--;
11974
11975     if (game.timegate_time_left == 0)
11976       CloseAllOpenTimegates();
11977   }
11978
11979   if (game.lenses_time_left > 0)
11980   {
11981     game.lenses_time_left--;
11982
11983     if (game.lenses_time_left == 0)
11984       RedrawAllInvisibleElementsForLenses();
11985   }
11986
11987   if (game.magnify_time_left > 0)
11988   {
11989     game.magnify_time_left--;
11990
11991     if (game.magnify_time_left == 0)
11992       RedrawAllInvisibleElementsForMagnifier();
11993   }
11994
11995   for (i = 0; i < MAX_PLAYERS; i++)
11996   {
11997     struct PlayerInfo *player = &stored_player[i];
11998
11999     if (SHIELD_ON(player))
12000     {
12001       if (player->shield_deadly_time_left)
12002         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12003       else if (player->shield_normal_time_left)
12004         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12005     }
12006   }
12007
12008 #if USE_DELAYED_GFX_REDRAW
12009   SCAN_PLAYFIELD(x, y)
12010   {
12011     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12012     {
12013       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12014          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12015
12016       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12017         DrawLevelField(x, y);
12018
12019       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12020         DrawLevelFieldCrumbled(x, y);
12021
12022       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12023         DrawLevelFieldCrumbledNeighbours(x, y);
12024
12025       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12026         DrawTwinkleOnField(x, y);
12027     }
12028
12029     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12030   }
12031 #endif
12032
12033   DrawAllPlayers();
12034   PlayAllPlayersSound();
12035
12036   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12037   {
12038     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12039
12040     local_player->show_envelope = 0;
12041   }
12042
12043   /* use random number generator in every frame to make it less predictable */
12044   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12045     RND(1);
12046 }
12047
12048 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12049 {
12050   int min_x = x, min_y = y, max_x = x, max_y = y;
12051   int i;
12052
12053   for (i = 0; i < MAX_PLAYERS; i++)
12054   {
12055     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12056
12057     if (!stored_player[i].active || &stored_player[i] == player)
12058       continue;
12059
12060     min_x = MIN(min_x, jx);
12061     min_y = MIN(min_y, jy);
12062     max_x = MAX(max_x, jx);
12063     max_y = MAX(max_y, jy);
12064   }
12065
12066   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12067 }
12068
12069 static boolean AllPlayersInVisibleScreen()
12070 {
12071   int i;
12072
12073   for (i = 0; i < MAX_PLAYERS; i++)
12074   {
12075     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12076
12077     if (!stored_player[i].active)
12078       continue;
12079
12080     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12081       return FALSE;
12082   }
12083
12084   return TRUE;
12085 }
12086
12087 void ScrollLevel(int dx, int dy)
12088 {
12089   int scroll_offset = 2 * TILEX_VAR;
12090   int x, y;
12091
12092   BlitBitmap(drawto_field, drawto_field,
12093              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12094              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12095              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12096              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12097              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12098              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12099
12100   if (dx != 0)
12101   {
12102     x = (dx == 1 ? BX1 : BX2);
12103     for (y = BY1; y <= BY2; y++)
12104       DrawScreenField(x, y);
12105   }
12106
12107   if (dy != 0)
12108   {
12109     y = (dy == 1 ? BY1 : BY2);
12110     for (x = BX1; x <= BX2; x++)
12111       DrawScreenField(x, y);
12112   }
12113
12114   redraw_mask |= REDRAW_FIELD;
12115 }
12116
12117 static boolean canFallDown(struct PlayerInfo *player)
12118 {
12119   int jx = player->jx, jy = player->jy;
12120
12121   return (IN_LEV_FIELD(jx, jy + 1) &&
12122           (IS_FREE(jx, jy + 1) ||
12123            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12124           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12125           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12126 }
12127
12128 static boolean canPassField(int x, int y, int move_dir)
12129 {
12130   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12131   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12132   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12133   int nextx = x + dx;
12134   int nexty = y + dy;
12135   int element = Feld[x][y];
12136
12137   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12138           !CAN_MOVE(element) &&
12139           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12140           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12141           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12142 }
12143
12144 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12145 {
12146   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12147   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12148   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12149   int newx = x + dx;
12150   int newy = y + dy;
12151
12152   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12153           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12154           (IS_DIGGABLE(Feld[newx][newy]) ||
12155            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12156            canPassField(newx, newy, move_dir)));
12157 }
12158
12159 static void CheckGravityMovement(struct PlayerInfo *player)
12160 {
12161   if (player->gravity && !player->programmed_action)
12162   {
12163     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12164     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12165     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12166     int jx = player->jx, jy = player->jy;
12167     boolean player_is_moving_to_valid_field =
12168       (!player_is_snapping &&
12169        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12170         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12171     boolean player_can_fall_down = canFallDown(player);
12172
12173     if (player_can_fall_down &&
12174         !player_is_moving_to_valid_field)
12175       player->programmed_action = MV_DOWN;
12176   }
12177 }
12178
12179 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12180 {
12181   return CheckGravityMovement(player);
12182
12183   if (player->gravity && !player->programmed_action)
12184   {
12185     int jx = player->jx, jy = player->jy;
12186     boolean field_under_player_is_free =
12187       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12188     boolean player_is_standing_on_valid_field =
12189       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12190        (IS_WALKABLE(Feld[jx][jy]) &&
12191         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12192
12193     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12194       player->programmed_action = MV_DOWN;
12195   }
12196 }
12197
12198 /*
12199   MovePlayerOneStep()
12200   -----------------------------------------------------------------------------
12201   dx, dy:               direction (non-diagonal) to try to move the player to
12202   real_dx, real_dy:     direction as read from input device (can be diagonal)
12203 */
12204
12205 boolean MovePlayerOneStep(struct PlayerInfo *player,
12206                           int dx, int dy, int real_dx, int real_dy)
12207 {
12208   int jx = player->jx, jy = player->jy;
12209   int new_jx = jx + dx, new_jy = jy + dy;
12210   int can_move;
12211   boolean player_can_move = !player->cannot_move;
12212
12213   if (!player->active || (!dx && !dy))
12214     return MP_NO_ACTION;
12215
12216   player->MovDir = (dx < 0 ? MV_LEFT :
12217                     dx > 0 ? MV_RIGHT :
12218                     dy < 0 ? MV_UP :
12219                     dy > 0 ? MV_DOWN :  MV_NONE);
12220
12221   if (!IN_LEV_FIELD(new_jx, new_jy))
12222     return MP_NO_ACTION;
12223
12224   if (!player_can_move)
12225   {
12226     if (player->MovPos == 0)
12227     {
12228       player->is_moving = FALSE;
12229       player->is_digging = FALSE;
12230       player->is_collecting = FALSE;
12231       player->is_snapping = FALSE;
12232       player->is_pushing = FALSE;
12233     }
12234   }
12235
12236   if (!options.network && game.centered_player_nr == -1 &&
12237       !AllPlayersInSight(player, new_jx, new_jy))
12238     return MP_NO_ACTION;
12239
12240   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12241   if (can_move != MP_MOVING)
12242     return can_move;
12243
12244   /* check if DigField() has caused relocation of the player */
12245   if (player->jx != jx || player->jy != jy)
12246     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12247
12248   StorePlayer[jx][jy] = 0;
12249   player->last_jx = jx;
12250   player->last_jy = jy;
12251   player->jx = new_jx;
12252   player->jy = new_jy;
12253   StorePlayer[new_jx][new_jy] = player->element_nr;
12254
12255   if (player->move_delay_value_next != -1)
12256   {
12257     player->move_delay_value = player->move_delay_value_next;
12258     player->move_delay_value_next = -1;
12259   }
12260
12261   player->MovPos =
12262     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12263
12264   player->step_counter++;
12265
12266   PlayerVisit[jx][jy] = FrameCounter;
12267
12268   player->is_moving = TRUE;
12269
12270 #if 1
12271   /* should better be called in MovePlayer(), but this breaks some tapes */
12272   ScrollPlayer(player, SCROLL_INIT);
12273 #endif
12274
12275   return MP_MOVING;
12276 }
12277
12278 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12279 {
12280   int jx = player->jx, jy = player->jy;
12281   int old_jx = jx, old_jy = jy;
12282   int moved = MP_NO_ACTION;
12283
12284   if (!player->active)
12285     return FALSE;
12286
12287   if (!dx && !dy)
12288   {
12289     if (player->MovPos == 0)
12290     {
12291       player->is_moving = FALSE;
12292       player->is_digging = FALSE;
12293       player->is_collecting = FALSE;
12294       player->is_snapping = FALSE;
12295       player->is_pushing = FALSE;
12296     }
12297
12298     return FALSE;
12299   }
12300
12301   if (player->move_delay > 0)
12302     return FALSE;
12303
12304   player->move_delay = -1;              /* set to "uninitialized" value */
12305
12306   /* store if player is automatically moved to next field */
12307   player->is_auto_moving = (player->programmed_action != MV_NONE);
12308
12309   /* remove the last programmed player action */
12310   player->programmed_action = 0;
12311
12312   if (player->MovPos)
12313   {
12314     /* should only happen if pre-1.2 tape recordings are played */
12315     /* this is only for backward compatibility */
12316
12317     int original_move_delay_value = player->move_delay_value;
12318
12319 #if DEBUG
12320     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12321            tape.counter);
12322 #endif
12323
12324     /* scroll remaining steps with finest movement resolution */
12325     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12326
12327     while (player->MovPos)
12328     {
12329       ScrollPlayer(player, SCROLL_GO_ON);
12330       ScrollScreen(NULL, SCROLL_GO_ON);
12331
12332       AdvanceFrameAndPlayerCounters(player->index_nr);
12333
12334       DrawAllPlayers();
12335       BackToFront_WithFrameDelay(0);
12336     }
12337
12338     player->move_delay_value = original_move_delay_value;
12339   }
12340
12341   player->is_active = FALSE;
12342
12343   if (player->last_move_dir & MV_HORIZONTAL)
12344   {
12345     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12346       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12347   }
12348   else
12349   {
12350     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12351       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12352   }
12353
12354   if (!moved && !player->is_active)
12355   {
12356     player->is_moving = FALSE;
12357     player->is_digging = FALSE;
12358     player->is_collecting = FALSE;
12359     player->is_snapping = FALSE;
12360     player->is_pushing = FALSE;
12361   }
12362
12363   jx = player->jx;
12364   jy = player->jy;
12365
12366   if (moved & MP_MOVING && !ScreenMovPos &&
12367       (player->index_nr == game.centered_player_nr ||
12368        game.centered_player_nr == -1))
12369   {
12370     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12371     int offset = game.scroll_delay_value;
12372
12373     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12374     {
12375       /* actual player has left the screen -- scroll in that direction */
12376       if (jx != old_jx)         /* player has moved horizontally */
12377         scroll_x += (jx - old_jx);
12378       else                      /* player has moved vertically */
12379         scroll_y += (jy - old_jy);
12380     }
12381     else
12382     {
12383       if (jx != old_jx)         /* player has moved horizontally */
12384       {
12385         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12386             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12387           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12388
12389         /* don't scroll over playfield boundaries */
12390         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12391           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12392
12393         /* don't scroll more than one field at a time */
12394         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12395
12396         /* don't scroll against the player's moving direction */
12397         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12398             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12399           scroll_x = old_scroll_x;
12400       }
12401       else                      /* player has moved vertically */
12402       {
12403         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12404             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12405           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12406
12407         /* don't scroll over playfield boundaries */
12408         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12409           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12410
12411         /* don't scroll more than one field at a time */
12412         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12413
12414         /* don't scroll against the player's moving direction */
12415         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12416             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12417           scroll_y = old_scroll_y;
12418       }
12419     }
12420
12421     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12422     {
12423       if (!options.network && game.centered_player_nr == -1 &&
12424           !AllPlayersInVisibleScreen())
12425       {
12426         scroll_x = old_scroll_x;
12427         scroll_y = old_scroll_y;
12428       }
12429       else
12430       {
12431         ScrollScreen(player, SCROLL_INIT);
12432         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12433       }
12434     }
12435   }
12436
12437   player->StepFrame = 0;
12438
12439   if (moved & MP_MOVING)
12440   {
12441     if (old_jx != jx && old_jy == jy)
12442       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12443     else if (old_jx == jx && old_jy != jy)
12444       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12445
12446     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12447
12448     player->last_move_dir = player->MovDir;
12449     player->is_moving = TRUE;
12450     player->is_snapping = FALSE;
12451     player->is_switching = FALSE;
12452     player->is_dropping = FALSE;
12453     player->is_dropping_pressed = FALSE;
12454     player->drop_pressed_delay = 0;
12455
12456 #if 0
12457     /* should better be called here than above, but this breaks some tapes */
12458     ScrollPlayer(player, SCROLL_INIT);
12459 #endif
12460   }
12461   else
12462   {
12463     CheckGravityMovementWhenNotMoving(player);
12464
12465     player->is_moving = FALSE;
12466
12467     /* at this point, the player is allowed to move, but cannot move right now
12468        (e.g. because of something blocking the way) -- ensure that the player
12469        is also allowed to move in the next frame (in old versions before 3.1.1,
12470        the player was forced to wait again for eight frames before next try) */
12471
12472     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12473       player->move_delay = 0;   /* allow direct movement in the next frame */
12474   }
12475
12476   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12477     player->move_delay = player->move_delay_value;
12478
12479   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12480   {
12481     TestIfPlayerTouchesBadThing(jx, jy);
12482     TestIfPlayerTouchesCustomElement(jx, jy);
12483   }
12484
12485   if (!player->active)
12486     RemovePlayer(player);
12487
12488   return moved;
12489 }
12490
12491 void ScrollPlayer(struct PlayerInfo *player, int mode)
12492 {
12493   int jx = player->jx, jy = player->jy;
12494   int last_jx = player->last_jx, last_jy = player->last_jy;
12495   int move_stepsize = TILEX / player->move_delay_value;
12496
12497   if (!player->active)
12498     return;
12499
12500   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12501     return;
12502
12503   if (mode == SCROLL_INIT)
12504   {
12505     player->actual_frame_counter = FrameCounter;
12506     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12507
12508     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12509         Feld[last_jx][last_jy] == EL_EMPTY)
12510     {
12511       int last_field_block_delay = 0;   /* start with no blocking at all */
12512       int block_delay_adjustment = player->block_delay_adjustment;
12513
12514       /* if player blocks last field, add delay for exactly one move */
12515       if (player->block_last_field)
12516       {
12517         last_field_block_delay += player->move_delay_value;
12518
12519         /* when blocking enabled, prevent moving up despite gravity */
12520         if (player->gravity && player->MovDir == MV_UP)
12521           block_delay_adjustment = -1;
12522       }
12523
12524       /* add block delay adjustment (also possible when not blocking) */
12525       last_field_block_delay += block_delay_adjustment;
12526
12527       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12528       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12529     }
12530
12531     if (player->MovPos != 0)    /* player has not yet reached destination */
12532       return;
12533   }
12534   else if (!FrameReached(&player->actual_frame_counter, 1))
12535     return;
12536
12537   if (player->MovPos != 0)
12538   {
12539     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12540     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12541
12542     /* before DrawPlayer() to draw correct player graphic for this case */
12543     if (player->MovPos == 0)
12544       CheckGravityMovement(player);
12545   }
12546
12547   if (player->MovPos == 0)      /* player reached destination field */
12548   {
12549     if (player->move_delay_reset_counter > 0)
12550     {
12551       player->move_delay_reset_counter--;
12552
12553       if (player->move_delay_reset_counter == 0)
12554       {
12555         /* continue with normal speed after quickly moving through gate */
12556         HALVE_PLAYER_SPEED(player);
12557
12558         /* be able to make the next move without delay */
12559         player->move_delay = 0;
12560       }
12561     }
12562
12563     player->last_jx = jx;
12564     player->last_jy = jy;
12565
12566     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12567         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12568         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12569         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12570         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12571         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12572         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12573         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12574     {
12575       DrawPlayer(player);       /* needed here only to cleanup last field */
12576       RemovePlayer(player);
12577
12578       if (local_player->friends_still_needed == 0 ||
12579           IS_SP_ELEMENT(Feld[jx][jy]))
12580         PlayerWins(player);
12581     }
12582
12583     /* this breaks one level: "machine", level 000 */
12584     {
12585       int move_direction = player->MovDir;
12586       int enter_side = MV_DIR_OPPOSITE(move_direction);
12587       int leave_side = move_direction;
12588       int old_jx = last_jx;
12589       int old_jy = last_jy;
12590       int old_element = Feld[old_jx][old_jy];
12591       int new_element = Feld[jx][jy];
12592
12593       if (IS_CUSTOM_ELEMENT(old_element))
12594         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12595                                    CE_LEFT_BY_PLAYER,
12596                                    player->index_bit, leave_side);
12597
12598       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12599                                           CE_PLAYER_LEAVES_X,
12600                                           player->index_bit, leave_side);
12601
12602       if (IS_CUSTOM_ELEMENT(new_element))
12603         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12604                                    player->index_bit, enter_side);
12605
12606       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12607                                           CE_PLAYER_ENTERS_X,
12608                                           player->index_bit, enter_side);
12609
12610       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12611                                         CE_MOVE_OF_X, move_direction);
12612     }
12613
12614     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12615     {
12616       TestIfPlayerTouchesBadThing(jx, jy);
12617       TestIfPlayerTouchesCustomElement(jx, jy);
12618
12619       /* needed because pushed element has not yet reached its destination,
12620          so it would trigger a change event at its previous field location */
12621       if (!player->is_pushing)
12622         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12623
12624       if (!player->active)
12625         RemovePlayer(player);
12626     }
12627
12628     if (!local_player->LevelSolved && level.use_step_counter)
12629     {
12630       int i;
12631
12632       TimePlayed++;
12633
12634       if (TimeLeft > 0)
12635       {
12636         TimeLeft--;
12637
12638         if (TimeLeft <= 10 && setup.time_limit)
12639           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12640
12641         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12642
12643         DisplayGameControlValues();
12644
12645         if (!TimeLeft && setup.time_limit)
12646           for (i = 0; i < MAX_PLAYERS; i++)
12647             KillPlayer(&stored_player[i]);
12648       }
12649       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12650       {
12651         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12652
12653         DisplayGameControlValues();
12654       }
12655     }
12656
12657     if (tape.single_step && tape.recording && !tape.pausing &&
12658         !player->programmed_action)
12659       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12660
12661     if (!player->programmed_action)
12662       CheckSaveEngineSnapshot(player);
12663   }
12664 }
12665
12666 void ScrollScreen(struct PlayerInfo *player, int mode)
12667 {
12668   static unsigned int screen_frame_counter = 0;
12669
12670   if (mode == SCROLL_INIT)
12671   {
12672     /* set scrolling step size according to actual player's moving speed */
12673     ScrollStepSize = TILEX / player->move_delay_value;
12674
12675     screen_frame_counter = FrameCounter;
12676     ScreenMovDir = player->MovDir;
12677     ScreenMovPos = player->MovPos;
12678     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12679     return;
12680   }
12681   else if (!FrameReached(&screen_frame_counter, 1))
12682     return;
12683
12684   if (ScreenMovPos)
12685   {
12686     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12687     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12688     redraw_mask |= REDRAW_FIELD;
12689   }
12690   else
12691     ScreenMovDir = MV_NONE;
12692 }
12693
12694 void TestIfPlayerTouchesCustomElement(int x, int y)
12695 {
12696   static int xy[4][2] =
12697   {
12698     { 0, -1 },
12699     { -1, 0 },
12700     { +1, 0 },
12701     { 0, +1 }
12702   };
12703   static int trigger_sides[4][2] =
12704   {
12705     /* center side       border side */
12706     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12707     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12708     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12709     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12710   };
12711   static int touch_dir[4] =
12712   {
12713     MV_LEFT | MV_RIGHT,
12714     MV_UP   | MV_DOWN,
12715     MV_UP   | MV_DOWN,
12716     MV_LEFT | MV_RIGHT
12717   };
12718   int center_element = Feld[x][y];      /* should always be non-moving! */
12719   int i;
12720
12721   for (i = 0; i < NUM_DIRECTIONS; i++)
12722   {
12723     int xx = x + xy[i][0];
12724     int yy = y + xy[i][1];
12725     int center_side = trigger_sides[i][0];
12726     int border_side = trigger_sides[i][1];
12727     int border_element;
12728
12729     if (!IN_LEV_FIELD(xx, yy))
12730       continue;
12731
12732     if (IS_PLAYER(x, y))                /* player found at center element */
12733     {
12734       struct PlayerInfo *player = PLAYERINFO(x, y);
12735
12736       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12737         border_element = Feld[xx][yy];          /* may be moving! */
12738       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12739         border_element = Feld[xx][yy];
12740       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12741         border_element = MovingOrBlocked2Element(xx, yy);
12742       else
12743         continue;               /* center and border element do not touch */
12744
12745       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12746                                  player->index_bit, border_side);
12747       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12748                                           CE_PLAYER_TOUCHES_X,
12749                                           player->index_bit, border_side);
12750
12751       {
12752         /* use player element that is initially defined in the level playfield,
12753            not the player element that corresponds to the runtime player number
12754            (example: a level that contains EL_PLAYER_3 as the only player would
12755            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12756         int player_element = PLAYERINFO(x, y)->initial_element;
12757
12758         CheckElementChangeBySide(xx, yy, border_element, player_element,
12759                                  CE_TOUCHING_X, border_side);
12760       }
12761     }
12762     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12763     {
12764       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12765
12766       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12767       {
12768         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12769           continue;             /* center and border element do not touch */
12770       }
12771
12772       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12773                                  player->index_bit, center_side);
12774       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12775                                           CE_PLAYER_TOUCHES_X,
12776                                           player->index_bit, center_side);
12777
12778       {
12779         /* use player element that is initially defined in the level playfield,
12780            not the player element that corresponds to the runtime player number
12781            (example: a level that contains EL_PLAYER_3 as the only player would
12782            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12783         int player_element = PLAYERINFO(xx, yy)->initial_element;
12784
12785         CheckElementChangeBySide(x, y, center_element, player_element,
12786                                  CE_TOUCHING_X, center_side);
12787       }
12788
12789       break;
12790     }
12791   }
12792 }
12793
12794 void TestIfElementTouchesCustomElement(int x, int y)
12795 {
12796   static int xy[4][2] =
12797   {
12798     { 0, -1 },
12799     { -1, 0 },
12800     { +1, 0 },
12801     { 0, +1 }
12802   };
12803   static int trigger_sides[4][2] =
12804   {
12805     /* center side      border side */
12806     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12807     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12808     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12809     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12810   };
12811   static int touch_dir[4] =
12812   {
12813     MV_LEFT | MV_RIGHT,
12814     MV_UP   | MV_DOWN,
12815     MV_UP   | MV_DOWN,
12816     MV_LEFT | MV_RIGHT
12817   };
12818   boolean change_center_element = FALSE;
12819   int center_element = Feld[x][y];      /* should always be non-moving! */
12820   int border_element_old[NUM_DIRECTIONS];
12821   int i;
12822
12823   for (i = 0; i < NUM_DIRECTIONS; i++)
12824   {
12825     int xx = x + xy[i][0];
12826     int yy = y + xy[i][1];
12827     int border_element;
12828
12829     border_element_old[i] = -1;
12830
12831     if (!IN_LEV_FIELD(xx, yy))
12832       continue;
12833
12834     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12835       border_element = Feld[xx][yy];    /* may be moving! */
12836     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12837       border_element = Feld[xx][yy];
12838     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12839       border_element = MovingOrBlocked2Element(xx, yy);
12840     else
12841       continue;                 /* center and border element do not touch */
12842
12843     border_element_old[i] = border_element;
12844   }
12845
12846   for (i = 0; i < NUM_DIRECTIONS; i++)
12847   {
12848     int xx = x + xy[i][0];
12849     int yy = y + xy[i][1];
12850     int center_side = trigger_sides[i][0];
12851     int border_element = border_element_old[i];
12852
12853     if (border_element == -1)
12854       continue;
12855
12856     /* check for change of border element */
12857     CheckElementChangeBySide(xx, yy, border_element, center_element,
12858                              CE_TOUCHING_X, center_side);
12859
12860     /* (center element cannot be player, so we dont have to check this here) */
12861   }
12862
12863   for (i = 0; i < NUM_DIRECTIONS; i++)
12864   {
12865     int xx = x + xy[i][0];
12866     int yy = y + xy[i][1];
12867     int border_side = trigger_sides[i][1];
12868     int border_element = border_element_old[i];
12869
12870     if (border_element == -1)
12871       continue;
12872
12873     /* check for change of center element (but change it only once) */
12874     if (!change_center_element)
12875       change_center_element =
12876         CheckElementChangeBySide(x, y, center_element, border_element,
12877                                  CE_TOUCHING_X, border_side);
12878
12879     if (IS_PLAYER(xx, yy))
12880     {
12881       /* use player element that is initially defined in the level playfield,
12882          not the player element that corresponds to the runtime player number
12883          (example: a level that contains EL_PLAYER_3 as the only player would
12884          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12885       int player_element = PLAYERINFO(xx, yy)->initial_element;
12886
12887       CheckElementChangeBySide(x, y, center_element, player_element,
12888                                CE_TOUCHING_X, border_side);
12889     }
12890   }
12891 }
12892
12893 void TestIfElementHitsCustomElement(int x, int y, int direction)
12894 {
12895   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12896   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12897   int hitx = x + dx, hity = y + dy;
12898   int hitting_element = Feld[x][y];
12899   int touched_element;
12900
12901   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12902     return;
12903
12904   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12905                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12906
12907   if (IN_LEV_FIELD(hitx, hity))
12908   {
12909     int opposite_direction = MV_DIR_OPPOSITE(direction);
12910     int hitting_side = direction;
12911     int touched_side = opposite_direction;
12912     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12913                           MovDir[hitx][hity] != direction ||
12914                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12915
12916     object_hit = TRUE;
12917
12918     if (object_hit)
12919     {
12920       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12921                                CE_HITTING_X, touched_side);
12922
12923       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12924                                CE_HIT_BY_X, hitting_side);
12925
12926       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12927                                CE_HIT_BY_SOMETHING, opposite_direction);
12928
12929       if (IS_PLAYER(hitx, hity))
12930       {
12931         /* use player element that is initially defined in the level playfield,
12932            not the player element that corresponds to the runtime player number
12933            (example: a level that contains EL_PLAYER_3 as the only player would
12934            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12935         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12936
12937         CheckElementChangeBySide(x, y, hitting_element, player_element,
12938                                  CE_HITTING_X, touched_side);
12939       }
12940     }
12941   }
12942
12943   /* "hitting something" is also true when hitting the playfield border */
12944   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12945                            CE_HITTING_SOMETHING, direction);
12946 }
12947
12948 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12949 {
12950   int i, kill_x = -1, kill_y = -1;
12951
12952   int bad_element = -1;
12953   static int test_xy[4][2] =
12954   {
12955     { 0, -1 },
12956     { -1, 0 },
12957     { +1, 0 },
12958     { 0, +1 }
12959   };
12960   static int test_dir[4] =
12961   {
12962     MV_UP,
12963     MV_LEFT,
12964     MV_RIGHT,
12965     MV_DOWN
12966   };
12967
12968   for (i = 0; i < NUM_DIRECTIONS; i++)
12969   {
12970     int test_x, test_y, test_move_dir, test_element;
12971
12972     test_x = good_x + test_xy[i][0];
12973     test_y = good_y + test_xy[i][1];
12974
12975     if (!IN_LEV_FIELD(test_x, test_y))
12976       continue;
12977
12978     test_move_dir =
12979       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12980
12981     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12982
12983     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12984        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12985     */
12986     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12987         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12988     {
12989       kill_x = test_x;
12990       kill_y = test_y;
12991       bad_element = test_element;
12992
12993       break;
12994     }
12995   }
12996
12997   if (kill_x != -1 || kill_y != -1)
12998   {
12999     if (IS_PLAYER(good_x, good_y))
13000     {
13001       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13002
13003       if (player->shield_deadly_time_left > 0 &&
13004           !IS_INDESTRUCTIBLE(bad_element))
13005         Bang(kill_x, kill_y);
13006       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13007         KillPlayer(player);
13008     }
13009     else
13010       Bang(good_x, good_y);
13011   }
13012 }
13013
13014 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13015 {
13016   int i, kill_x = -1, kill_y = -1;
13017   int bad_element = Feld[bad_x][bad_y];
13018   static int test_xy[4][2] =
13019   {
13020     { 0, -1 },
13021     { -1, 0 },
13022     { +1, 0 },
13023     { 0, +1 }
13024   };
13025   static int touch_dir[4] =
13026   {
13027     MV_LEFT | MV_RIGHT,
13028     MV_UP   | MV_DOWN,
13029     MV_UP   | MV_DOWN,
13030     MV_LEFT | MV_RIGHT
13031   };
13032   static int test_dir[4] =
13033   {
13034     MV_UP,
13035     MV_LEFT,
13036     MV_RIGHT,
13037     MV_DOWN
13038   };
13039
13040   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13041     return;
13042
13043   for (i = 0; i < NUM_DIRECTIONS; i++)
13044   {
13045     int test_x, test_y, test_move_dir, test_element;
13046
13047     test_x = bad_x + test_xy[i][0];
13048     test_y = bad_y + test_xy[i][1];
13049
13050     if (!IN_LEV_FIELD(test_x, test_y))
13051       continue;
13052
13053     test_move_dir =
13054       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13055
13056     test_element = Feld[test_x][test_y];
13057
13058     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13059        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13060     */
13061     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13062         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13063     {
13064       /* good thing is player or penguin that does not move away */
13065       if (IS_PLAYER(test_x, test_y))
13066       {
13067         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13068
13069         if (bad_element == EL_ROBOT && player->is_moving)
13070           continue;     /* robot does not kill player if he is moving */
13071
13072         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13073         {
13074           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13075             continue;           /* center and border element do not touch */
13076         }
13077
13078         kill_x = test_x;
13079         kill_y = test_y;
13080
13081         break;
13082       }
13083       else if (test_element == EL_PENGUIN)
13084       {
13085         kill_x = test_x;
13086         kill_y = test_y;
13087
13088         break;
13089       }
13090     }
13091   }
13092
13093   if (kill_x != -1 || kill_y != -1)
13094   {
13095     if (IS_PLAYER(kill_x, kill_y))
13096     {
13097       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13098
13099       if (player->shield_deadly_time_left > 0 &&
13100           !IS_INDESTRUCTIBLE(bad_element))
13101         Bang(bad_x, bad_y);
13102       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13103         KillPlayer(player);
13104     }
13105     else
13106       Bang(kill_x, kill_y);
13107   }
13108 }
13109
13110 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13111 {
13112   int bad_element = Feld[bad_x][bad_y];
13113   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13114   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13115   int test_x = bad_x + dx, test_y = bad_y + dy;
13116   int test_move_dir, test_element;
13117   int kill_x = -1, kill_y = -1;
13118
13119   if (!IN_LEV_FIELD(test_x, test_y))
13120     return;
13121
13122   test_move_dir =
13123     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13124
13125   test_element = Feld[test_x][test_y];
13126
13127   if (test_move_dir != bad_move_dir)
13128   {
13129     /* good thing can be player or penguin that does not move away */
13130     if (IS_PLAYER(test_x, test_y))
13131     {
13132       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13133
13134       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13135          player as being hit when he is moving towards the bad thing, because
13136          the "get hit by" condition would be lost after the player stops) */
13137       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13138         return;         /* player moves away from bad thing */
13139
13140       kill_x = test_x;
13141       kill_y = test_y;
13142     }
13143     else if (test_element == EL_PENGUIN)
13144     {
13145       kill_x = test_x;
13146       kill_y = test_y;
13147     }
13148   }
13149
13150   if (kill_x != -1 || kill_y != -1)
13151   {
13152     if (IS_PLAYER(kill_x, kill_y))
13153     {
13154       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13155
13156       if (player->shield_deadly_time_left > 0 &&
13157           !IS_INDESTRUCTIBLE(bad_element))
13158         Bang(bad_x, bad_y);
13159       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13160         KillPlayer(player);
13161     }
13162     else
13163       Bang(kill_x, kill_y);
13164   }
13165 }
13166
13167 void TestIfPlayerTouchesBadThing(int x, int y)
13168 {
13169   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13170 }
13171
13172 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13173 {
13174   TestIfGoodThingHitsBadThing(x, y, move_dir);
13175 }
13176
13177 void TestIfBadThingTouchesPlayer(int x, int y)
13178 {
13179   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13180 }
13181
13182 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13183 {
13184   TestIfBadThingHitsGoodThing(x, y, move_dir);
13185 }
13186
13187 void TestIfFriendTouchesBadThing(int x, int y)
13188 {
13189   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13190 }
13191
13192 void TestIfBadThingTouchesFriend(int x, int y)
13193 {
13194   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13195 }
13196
13197 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13198 {
13199   int i, kill_x = bad_x, kill_y = bad_y;
13200   static int xy[4][2] =
13201   {
13202     { 0, -1 },
13203     { -1, 0 },
13204     { +1, 0 },
13205     { 0, +1 }
13206   };
13207
13208   for (i = 0; i < NUM_DIRECTIONS; i++)
13209   {
13210     int x, y, element;
13211
13212     x = bad_x + xy[i][0];
13213     y = bad_y + xy[i][1];
13214     if (!IN_LEV_FIELD(x, y))
13215       continue;
13216
13217     element = Feld[x][y];
13218     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13219         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13220     {
13221       kill_x = x;
13222       kill_y = y;
13223       break;
13224     }
13225   }
13226
13227   if (kill_x != bad_x || kill_y != bad_y)
13228     Bang(bad_x, bad_y);
13229 }
13230
13231 void KillPlayer(struct PlayerInfo *player)
13232 {
13233   int jx = player->jx, jy = player->jy;
13234
13235   if (!player->active)
13236     return;
13237
13238 #if 0
13239   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13240          player->killed, player->active, player->reanimated);
13241 #endif
13242
13243   /* the following code was introduced to prevent an infinite loop when calling
13244      -> Bang()
13245      -> CheckTriggeredElementChangeExt()
13246      -> ExecuteCustomElementAction()
13247      -> KillPlayer()
13248      -> (infinitely repeating the above sequence of function calls)
13249      which occurs when killing the player while having a CE with the setting
13250      "kill player X when explosion of <player X>"; the solution using a new
13251      field "player->killed" was chosen for backwards compatibility, although
13252      clever use of the fields "player->active" etc. would probably also work */
13253 #if 1
13254   if (player->killed)
13255     return;
13256 #endif
13257
13258   player->killed = TRUE;
13259
13260   /* remove accessible field at the player's position */
13261   Feld[jx][jy] = EL_EMPTY;
13262
13263   /* deactivate shield (else Bang()/Explode() would not work right) */
13264   player->shield_normal_time_left = 0;
13265   player->shield_deadly_time_left = 0;
13266
13267 #if 0
13268   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13269          player->killed, player->active, player->reanimated);
13270 #endif
13271
13272   Bang(jx, jy);
13273
13274 #if 0
13275   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13276          player->killed, player->active, player->reanimated);
13277 #endif
13278
13279   if (player->reanimated)       /* killed player may have been reanimated */
13280     player->killed = player->reanimated = FALSE;
13281   else
13282     BuryPlayer(player);
13283 }
13284
13285 static void KillPlayerUnlessEnemyProtected(int x, int y)
13286 {
13287   if (!PLAYER_ENEMY_PROTECTED(x, y))
13288     KillPlayer(PLAYERINFO(x, y));
13289 }
13290
13291 static void KillPlayerUnlessExplosionProtected(int x, int y)
13292 {
13293   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13294     KillPlayer(PLAYERINFO(x, y));
13295 }
13296
13297 void BuryPlayer(struct PlayerInfo *player)
13298 {
13299   int jx = player->jx, jy = player->jy;
13300
13301   if (!player->active)
13302     return;
13303
13304   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13305   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13306
13307   player->GameOver = TRUE;
13308   RemovePlayer(player);
13309 }
13310
13311 void RemovePlayer(struct PlayerInfo *player)
13312 {
13313   int jx = player->jx, jy = player->jy;
13314   int i, found = FALSE;
13315
13316   player->present = FALSE;
13317   player->active = FALSE;
13318
13319   if (!ExplodeField[jx][jy])
13320     StorePlayer[jx][jy] = 0;
13321
13322   if (player->is_moving)
13323     TEST_DrawLevelField(player->last_jx, player->last_jy);
13324
13325   for (i = 0; i < MAX_PLAYERS; i++)
13326     if (stored_player[i].active)
13327       found = TRUE;
13328
13329   if (!found)
13330     AllPlayersGone = TRUE;
13331
13332   ExitX = ZX = jx;
13333   ExitY = ZY = jy;
13334 }
13335
13336 static void setFieldForSnapping(int x, int y, int element, int direction)
13337 {
13338   struct ElementInfo *ei = &element_info[element];
13339   int direction_bit = MV_DIR_TO_BIT(direction);
13340   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13341   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13342                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13343
13344   Feld[x][y] = EL_ELEMENT_SNAPPING;
13345   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13346
13347   ResetGfxAnimation(x, y);
13348
13349   GfxElement[x][y] = element;
13350   GfxAction[x][y] = action;
13351   GfxDir[x][y] = direction;
13352   GfxFrame[x][y] = -1;
13353 }
13354
13355 /*
13356   =============================================================================
13357   checkDiagonalPushing()
13358   -----------------------------------------------------------------------------
13359   check if diagonal input device direction results in pushing of object
13360   (by checking if the alternative direction is walkable, diggable, ...)
13361   =============================================================================
13362 */
13363
13364 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13365                                     int x, int y, int real_dx, int real_dy)
13366 {
13367   int jx, jy, dx, dy, xx, yy;
13368
13369   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13370     return TRUE;
13371
13372   /* diagonal direction: check alternative direction */
13373   jx = player->jx;
13374   jy = player->jy;
13375   dx = x - jx;
13376   dy = y - jy;
13377   xx = jx + (dx == 0 ? real_dx : 0);
13378   yy = jy + (dy == 0 ? real_dy : 0);
13379
13380   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13381 }
13382
13383 /*
13384   =============================================================================
13385   DigField()
13386   -----------------------------------------------------------------------------
13387   x, y:                 field next to player (non-diagonal) to try to dig to
13388   real_dx, real_dy:     direction as read from input device (can be diagonal)
13389   =============================================================================
13390 */
13391
13392 static int DigField(struct PlayerInfo *player,
13393                     int oldx, int oldy, int x, int y,
13394                     int real_dx, int real_dy, int mode)
13395 {
13396   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13397   boolean player_was_pushing = player->is_pushing;
13398   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13399   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13400   int jx = oldx, jy = oldy;
13401   int dx = x - jx, dy = y - jy;
13402   int nextx = x + dx, nexty = y + dy;
13403   int move_direction = (dx == -1 ? MV_LEFT  :
13404                         dx == +1 ? MV_RIGHT :
13405                         dy == -1 ? MV_UP    :
13406                         dy == +1 ? MV_DOWN  : MV_NONE);
13407   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13408   int dig_side = MV_DIR_OPPOSITE(move_direction);
13409   int old_element = Feld[jx][jy];
13410   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13411   int collect_count;
13412
13413   if (is_player)                /* function can also be called by EL_PENGUIN */
13414   {
13415     if (player->MovPos == 0)
13416     {
13417       player->is_digging = FALSE;
13418       player->is_collecting = FALSE;
13419     }
13420
13421     if (player->MovPos == 0)    /* last pushing move finished */
13422       player->is_pushing = FALSE;
13423
13424     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13425     {
13426       player->is_switching = FALSE;
13427       player->push_delay = -1;
13428
13429       return MP_NO_ACTION;
13430     }
13431   }
13432
13433   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13434     old_element = Back[jx][jy];
13435
13436   /* in case of element dropped at player position, check background */
13437   else if (Back[jx][jy] != EL_EMPTY &&
13438            game.engine_version >= VERSION_IDENT(2,2,0,0))
13439     old_element = Back[jx][jy];
13440
13441   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13442     return MP_NO_ACTION;        /* field has no opening in this direction */
13443
13444   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13445     return MP_NO_ACTION;        /* field has no opening in this direction */
13446
13447   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13448   {
13449     SplashAcid(x, y);
13450
13451     Feld[jx][jy] = player->artwork_element;
13452     InitMovingField(jx, jy, MV_DOWN);
13453     Store[jx][jy] = EL_ACID;
13454     ContinueMoving(jx, jy);
13455     BuryPlayer(player);
13456
13457     return MP_DONT_RUN_INTO;
13458   }
13459
13460   if (player_can_move && DONT_RUN_INTO(element))
13461   {
13462     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13463
13464     return MP_DONT_RUN_INTO;
13465   }
13466
13467   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13468     return MP_NO_ACTION;
13469
13470   collect_count = element_info[element].collect_count_initial;
13471
13472   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13473     return MP_NO_ACTION;
13474
13475   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13476     player_can_move = player_can_move_or_snap;
13477
13478   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13479       game.engine_version >= VERSION_IDENT(2,2,0,0))
13480   {
13481     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13482                                player->index_bit, dig_side);
13483     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13484                                         player->index_bit, dig_side);
13485
13486     if (element == EL_DC_LANDMINE)
13487       Bang(x, y);
13488
13489     if (Feld[x][y] != element)          /* field changed by snapping */
13490       return MP_ACTION;
13491
13492     return MP_NO_ACTION;
13493   }
13494
13495   if (player->gravity && is_player && !player->is_auto_moving &&
13496       canFallDown(player) && move_direction != MV_DOWN &&
13497       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13498     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13499
13500   if (player_can_move &&
13501       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13502   {
13503     int sound_element = SND_ELEMENT(element);
13504     int sound_action = ACTION_WALKING;
13505
13506     if (IS_RND_GATE(element))
13507     {
13508       if (!player->key[RND_GATE_NR(element)])
13509         return MP_NO_ACTION;
13510     }
13511     else if (IS_RND_GATE_GRAY(element))
13512     {
13513       if (!player->key[RND_GATE_GRAY_NR(element)])
13514         return MP_NO_ACTION;
13515     }
13516     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13517     {
13518       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13519         return MP_NO_ACTION;
13520     }
13521     else if (element == EL_EXIT_OPEN ||
13522              element == EL_EM_EXIT_OPEN ||
13523              element == EL_EM_EXIT_OPENING ||
13524              element == EL_STEEL_EXIT_OPEN ||
13525              element == EL_EM_STEEL_EXIT_OPEN ||
13526              element == EL_EM_STEEL_EXIT_OPENING ||
13527              element == EL_SP_EXIT_OPEN ||
13528              element == EL_SP_EXIT_OPENING)
13529     {
13530       sound_action = ACTION_PASSING;    /* player is passing exit */
13531     }
13532     else if (element == EL_EMPTY)
13533     {
13534       sound_action = ACTION_MOVING;             /* nothing to walk on */
13535     }
13536
13537     /* play sound from background or player, whatever is available */
13538     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13539       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13540     else
13541       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13542   }
13543   else if (player_can_move &&
13544            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13545   {
13546     if (!ACCESS_FROM(element, opposite_direction))
13547       return MP_NO_ACTION;      /* field not accessible from this direction */
13548
13549     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13550       return MP_NO_ACTION;
13551
13552     if (IS_EM_GATE(element))
13553     {
13554       if (!player->key[EM_GATE_NR(element)])
13555         return MP_NO_ACTION;
13556     }
13557     else if (IS_EM_GATE_GRAY(element))
13558     {
13559       if (!player->key[EM_GATE_GRAY_NR(element)])
13560         return MP_NO_ACTION;
13561     }
13562     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13563     {
13564       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13565         return MP_NO_ACTION;
13566     }
13567     else if (IS_EMC_GATE(element))
13568     {
13569       if (!player->key[EMC_GATE_NR(element)])
13570         return MP_NO_ACTION;
13571     }
13572     else if (IS_EMC_GATE_GRAY(element))
13573     {
13574       if (!player->key[EMC_GATE_GRAY_NR(element)])
13575         return MP_NO_ACTION;
13576     }
13577     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13578     {
13579       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13580         return MP_NO_ACTION;
13581     }
13582     else if (element == EL_DC_GATE_WHITE ||
13583              element == EL_DC_GATE_WHITE_GRAY ||
13584              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13585     {
13586       if (player->num_white_keys == 0)
13587         return MP_NO_ACTION;
13588
13589       player->num_white_keys--;
13590     }
13591     else if (IS_SP_PORT(element))
13592     {
13593       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13594           element == EL_SP_GRAVITY_PORT_RIGHT ||
13595           element == EL_SP_GRAVITY_PORT_UP ||
13596           element == EL_SP_GRAVITY_PORT_DOWN)
13597         player->gravity = !player->gravity;
13598       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13599                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13600                element == EL_SP_GRAVITY_ON_PORT_UP ||
13601                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13602         player->gravity = TRUE;
13603       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13604                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13605                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13606                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13607         player->gravity = FALSE;
13608     }
13609
13610     /* automatically move to the next field with double speed */
13611     player->programmed_action = move_direction;
13612
13613     if (player->move_delay_reset_counter == 0)
13614     {
13615       player->move_delay_reset_counter = 2;     /* two double speed steps */
13616
13617       DOUBLE_PLAYER_SPEED(player);
13618     }
13619
13620     PlayLevelSoundAction(x, y, ACTION_PASSING);
13621   }
13622   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13623   {
13624     RemoveField(x, y);
13625
13626     if (mode != DF_SNAP)
13627     {
13628       GfxElement[x][y] = GFX_ELEMENT(element);
13629       player->is_digging = TRUE;
13630     }
13631
13632     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13633
13634     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13635                                         player->index_bit, dig_side);
13636
13637     if (mode == DF_SNAP)
13638     {
13639       if (level.block_snap_field)
13640         setFieldForSnapping(x, y, element, move_direction);
13641       else
13642         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13643
13644       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13645                                           player->index_bit, dig_side);
13646     }
13647   }
13648   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13649   {
13650     RemoveField(x, y);
13651
13652     if (is_player && mode != DF_SNAP)
13653     {
13654       GfxElement[x][y] = element;
13655       player->is_collecting = TRUE;
13656     }
13657
13658     if (element == EL_SPEED_PILL)
13659     {
13660       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13661     }
13662     else if (element == EL_EXTRA_TIME && level.time > 0)
13663     {
13664       TimeLeft += level.extra_time;
13665
13666       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13667
13668       DisplayGameControlValues();
13669     }
13670     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13671     {
13672       player->shield_normal_time_left += level.shield_normal_time;
13673       if (element == EL_SHIELD_DEADLY)
13674         player->shield_deadly_time_left += level.shield_deadly_time;
13675     }
13676     else if (element == EL_DYNAMITE ||
13677              element == EL_EM_DYNAMITE ||
13678              element == EL_SP_DISK_RED)
13679     {
13680       if (player->inventory_size < MAX_INVENTORY_SIZE)
13681         player->inventory_element[player->inventory_size++] = element;
13682
13683       DrawGameDoorValues();
13684     }
13685     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13686     {
13687       player->dynabomb_count++;
13688       player->dynabombs_left++;
13689     }
13690     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13691     {
13692       player->dynabomb_size++;
13693     }
13694     else if (element == EL_DYNABOMB_INCREASE_POWER)
13695     {
13696       player->dynabomb_xl = TRUE;
13697     }
13698     else if (IS_KEY(element))
13699     {
13700       player->key[KEY_NR(element)] = TRUE;
13701
13702       DrawGameDoorValues();
13703     }
13704     else if (element == EL_DC_KEY_WHITE)
13705     {
13706       player->num_white_keys++;
13707
13708       /* display white keys? */
13709       /* DrawGameDoorValues(); */
13710     }
13711     else if (IS_ENVELOPE(element))
13712     {
13713       player->show_envelope = element;
13714     }
13715     else if (element == EL_EMC_LENSES)
13716     {
13717       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13718
13719       RedrawAllInvisibleElementsForLenses();
13720     }
13721     else if (element == EL_EMC_MAGNIFIER)
13722     {
13723       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13724
13725       RedrawAllInvisibleElementsForMagnifier();
13726     }
13727     else if (IS_DROPPABLE(element) ||
13728              IS_THROWABLE(element))     /* can be collected and dropped */
13729     {
13730       int i;
13731
13732       if (collect_count == 0)
13733         player->inventory_infinite_element = element;
13734       else
13735         for (i = 0; i < collect_count; i++)
13736           if (player->inventory_size < MAX_INVENTORY_SIZE)
13737             player->inventory_element[player->inventory_size++] = element;
13738
13739       DrawGameDoorValues();
13740     }
13741     else if (collect_count > 0)
13742     {
13743       local_player->gems_still_needed -= collect_count;
13744       if (local_player->gems_still_needed < 0)
13745         local_player->gems_still_needed = 0;
13746
13747       game.snapshot.collected_item = TRUE;
13748
13749       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13750
13751       DisplayGameControlValues();
13752     }
13753
13754     RaiseScoreElement(element);
13755     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13756
13757     if (is_player)
13758       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13759                                           player->index_bit, dig_side);
13760
13761     if (mode == DF_SNAP)
13762     {
13763       if (level.block_snap_field)
13764         setFieldForSnapping(x, y, element, move_direction);
13765       else
13766         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13767
13768       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13769                                           player->index_bit, dig_side);
13770     }
13771   }
13772   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13773   {
13774     if (mode == DF_SNAP && element != EL_BD_ROCK)
13775       return MP_NO_ACTION;
13776
13777     if (CAN_FALL(element) && dy)
13778       return MP_NO_ACTION;
13779
13780     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13781         !(element == EL_SPRING && level.use_spring_bug))
13782       return MP_NO_ACTION;
13783
13784     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13785         ((move_direction & MV_VERTICAL &&
13786           ((element_info[element].move_pattern & MV_LEFT &&
13787             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13788            (element_info[element].move_pattern & MV_RIGHT &&
13789             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13790          (move_direction & MV_HORIZONTAL &&
13791           ((element_info[element].move_pattern & MV_UP &&
13792             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13793            (element_info[element].move_pattern & MV_DOWN &&
13794             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13795       return MP_NO_ACTION;
13796
13797     /* do not push elements already moving away faster than player */
13798     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13799         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13800       return MP_NO_ACTION;
13801
13802     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13803     {
13804       if (player->push_delay_value == -1 || !player_was_pushing)
13805         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13806     }
13807     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13808     {
13809       if (player->push_delay_value == -1)
13810         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13811     }
13812     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13813     {
13814       if (!player->is_pushing)
13815         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13816     }
13817
13818     player->is_pushing = TRUE;
13819     player->is_active = TRUE;
13820
13821     if (!(IN_LEV_FIELD(nextx, nexty) &&
13822           (IS_FREE(nextx, nexty) ||
13823            (IS_SB_ELEMENT(element) &&
13824             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13825            (IS_CUSTOM_ELEMENT(element) &&
13826             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13827       return MP_NO_ACTION;
13828
13829     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13830       return MP_NO_ACTION;
13831
13832     if (player->push_delay == -1)       /* new pushing; restart delay */
13833       player->push_delay = 0;
13834
13835     if (player->push_delay < player->push_delay_value &&
13836         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13837         element != EL_SPRING && element != EL_BALLOON)
13838     {
13839       /* make sure that there is no move delay before next try to push */
13840       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13841         player->move_delay = 0;
13842
13843       return MP_NO_ACTION;
13844     }
13845
13846     if (IS_CUSTOM_ELEMENT(element) &&
13847         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13848     {
13849       if (!DigFieldByCE(nextx, nexty, element))
13850         return MP_NO_ACTION;
13851     }
13852
13853     if (IS_SB_ELEMENT(element))
13854     {
13855       if (element == EL_SOKOBAN_FIELD_FULL)
13856       {
13857         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13858         local_player->sokobanfields_still_needed++;
13859       }
13860
13861       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13862       {
13863         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13864         local_player->sokobanfields_still_needed--;
13865       }
13866
13867       Feld[x][y] = EL_SOKOBAN_OBJECT;
13868
13869       if (Back[x][y] == Back[nextx][nexty])
13870         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13871       else if (Back[x][y] != 0)
13872         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13873                                     ACTION_EMPTYING);
13874       else
13875         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13876                                     ACTION_FILLING);
13877
13878       if (local_player->sokobanfields_still_needed == 0 &&
13879           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13880       {
13881         PlayerWins(player);
13882
13883         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13884       }
13885     }
13886     else
13887       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13888
13889     InitMovingField(x, y, move_direction);
13890     GfxAction[x][y] = ACTION_PUSHING;
13891
13892     if (mode == DF_SNAP)
13893       ContinueMoving(x, y);
13894     else
13895       MovPos[x][y] = (dx != 0 ? dx : dy);
13896
13897     Pushed[x][y] = TRUE;
13898     Pushed[nextx][nexty] = TRUE;
13899
13900     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13901       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13902     else
13903       player->push_delay_value = -1;    /* get new value later */
13904
13905     /* check for element change _after_ element has been pushed */
13906     if (game.use_change_when_pushing_bug)
13907     {
13908       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13909                                  player->index_bit, dig_side);
13910       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13911                                           player->index_bit, dig_side);
13912     }
13913   }
13914   else if (IS_SWITCHABLE(element))
13915   {
13916     if (PLAYER_SWITCHING(player, x, y))
13917     {
13918       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13919                                           player->index_bit, dig_side);
13920
13921       return MP_ACTION;
13922     }
13923
13924     player->is_switching = TRUE;
13925     player->switch_x = x;
13926     player->switch_y = y;
13927
13928     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13929
13930     if (element == EL_ROBOT_WHEEL)
13931     {
13932       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13933       ZX = x;
13934       ZY = y;
13935
13936       game.robot_wheel_active = TRUE;
13937
13938       TEST_DrawLevelField(x, y);
13939     }
13940     else if (element == EL_SP_TERMINAL)
13941     {
13942       int xx, yy;
13943
13944       SCAN_PLAYFIELD(xx, yy)
13945       {
13946         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13947         {
13948           Bang(xx, yy);
13949         }
13950         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13951         {
13952           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13953
13954           ResetGfxAnimation(xx, yy);
13955           TEST_DrawLevelField(xx, yy);
13956         }
13957       }
13958     }
13959     else if (IS_BELT_SWITCH(element))
13960     {
13961       ToggleBeltSwitch(x, y);
13962     }
13963     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13964              element == EL_SWITCHGATE_SWITCH_DOWN ||
13965              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13966              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13967     {
13968       ToggleSwitchgateSwitch(x, y);
13969     }
13970     else if (element == EL_LIGHT_SWITCH ||
13971              element == EL_LIGHT_SWITCH_ACTIVE)
13972     {
13973       ToggleLightSwitch(x, y);
13974     }
13975     else if (element == EL_TIMEGATE_SWITCH ||
13976              element == EL_DC_TIMEGATE_SWITCH)
13977     {
13978       ActivateTimegateSwitch(x, y);
13979     }
13980     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13981              element == EL_BALLOON_SWITCH_RIGHT ||
13982              element == EL_BALLOON_SWITCH_UP    ||
13983              element == EL_BALLOON_SWITCH_DOWN  ||
13984              element == EL_BALLOON_SWITCH_NONE  ||
13985              element == EL_BALLOON_SWITCH_ANY)
13986     {
13987       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13988                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13989                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13990                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13991                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13992                              move_direction);
13993     }
13994     else if (element == EL_LAMP)
13995     {
13996       Feld[x][y] = EL_LAMP_ACTIVE;
13997       local_player->lights_still_needed--;
13998
13999       ResetGfxAnimation(x, y);
14000       TEST_DrawLevelField(x, y);
14001     }
14002     else if (element == EL_TIME_ORB_FULL)
14003     {
14004       Feld[x][y] = EL_TIME_ORB_EMPTY;
14005
14006       if (level.time > 0 || level.use_time_orb_bug)
14007       {
14008         TimeLeft += level.time_orb_time;
14009         game.no_time_limit = FALSE;
14010
14011         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14012
14013         DisplayGameControlValues();
14014       }
14015
14016       ResetGfxAnimation(x, y);
14017       TEST_DrawLevelField(x, y);
14018     }
14019     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14020              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14021     {
14022       int xx, yy;
14023
14024       game.ball_state = !game.ball_state;
14025
14026       SCAN_PLAYFIELD(xx, yy)
14027       {
14028         int e = Feld[xx][yy];
14029
14030         if (game.ball_state)
14031         {
14032           if (e == EL_EMC_MAGIC_BALL)
14033             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14034           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14035             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14036         }
14037         else
14038         {
14039           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14040             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14041           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14042             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14043         }
14044       }
14045     }
14046
14047     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14048                                         player->index_bit, dig_side);
14049
14050     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14051                                         player->index_bit, dig_side);
14052
14053     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14054                                         player->index_bit, dig_side);
14055
14056     return MP_ACTION;
14057   }
14058   else
14059   {
14060     if (!PLAYER_SWITCHING(player, x, y))
14061     {
14062       player->is_switching = TRUE;
14063       player->switch_x = x;
14064       player->switch_y = y;
14065
14066       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14067                                  player->index_bit, dig_side);
14068       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14069                                           player->index_bit, dig_side);
14070
14071       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14072                                  player->index_bit, dig_side);
14073       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14074                                           player->index_bit, dig_side);
14075     }
14076
14077     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14078                                player->index_bit, dig_side);
14079     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14080                                         player->index_bit, dig_side);
14081
14082     return MP_NO_ACTION;
14083   }
14084
14085   player->push_delay = -1;
14086
14087   if (is_player)                /* function can also be called by EL_PENGUIN */
14088   {
14089     if (Feld[x][y] != element)          /* really digged/collected something */
14090     {
14091       player->is_collecting = !player->is_digging;
14092       player->is_active = TRUE;
14093     }
14094   }
14095
14096   return MP_MOVING;
14097 }
14098
14099 static boolean DigFieldByCE(int x, int y, int digging_element)
14100 {
14101   int element = Feld[x][y];
14102
14103   if (!IS_FREE(x, y))
14104   {
14105     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14106                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14107                   ACTION_BREAKING);
14108
14109     /* no element can dig solid indestructible elements */
14110     if (IS_INDESTRUCTIBLE(element) &&
14111         !IS_DIGGABLE(element) &&
14112         !IS_COLLECTIBLE(element))
14113       return FALSE;
14114
14115     if (AmoebaNr[x][y] &&
14116         (element == EL_AMOEBA_FULL ||
14117          element == EL_BD_AMOEBA ||
14118          element == EL_AMOEBA_GROWING))
14119     {
14120       AmoebaCnt[AmoebaNr[x][y]]--;
14121       AmoebaCnt2[AmoebaNr[x][y]]--;
14122     }
14123
14124     if (IS_MOVING(x, y))
14125       RemoveMovingField(x, y);
14126     else
14127     {
14128       RemoveField(x, y);
14129       TEST_DrawLevelField(x, y);
14130     }
14131
14132     /* if digged element was about to explode, prevent the explosion */
14133     ExplodeField[x][y] = EX_TYPE_NONE;
14134
14135     PlayLevelSoundAction(x, y, action);
14136   }
14137
14138   Store[x][y] = EL_EMPTY;
14139
14140   /* this makes it possible to leave the removed element again */
14141   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14142     Store[x][y] = element;
14143
14144   return TRUE;
14145 }
14146
14147 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14148 {
14149   int jx = player->jx, jy = player->jy;
14150   int x = jx + dx, y = jy + dy;
14151   int snap_direction = (dx == -1 ? MV_LEFT  :
14152                         dx == +1 ? MV_RIGHT :
14153                         dy == -1 ? MV_UP    :
14154                         dy == +1 ? MV_DOWN  : MV_NONE);
14155   boolean can_continue_snapping = (level.continuous_snapping &&
14156                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14157
14158   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14159     return FALSE;
14160
14161   if (!player->active || !IN_LEV_FIELD(x, y))
14162     return FALSE;
14163
14164   if (dx && dy)
14165     return FALSE;
14166
14167   if (!dx && !dy)
14168   {
14169     if (player->MovPos == 0)
14170       player->is_pushing = FALSE;
14171
14172     player->is_snapping = FALSE;
14173
14174     if (player->MovPos == 0)
14175     {
14176       player->is_moving = FALSE;
14177       player->is_digging = FALSE;
14178       player->is_collecting = FALSE;
14179     }
14180
14181     return FALSE;
14182   }
14183
14184   /* prevent snapping with already pressed snap key when not allowed */
14185   if (player->is_snapping && !can_continue_snapping)
14186     return FALSE;
14187
14188   player->MovDir = snap_direction;
14189
14190   if (player->MovPos == 0)
14191   {
14192     player->is_moving = FALSE;
14193     player->is_digging = FALSE;
14194     player->is_collecting = FALSE;
14195   }
14196
14197   player->is_dropping = FALSE;
14198   player->is_dropping_pressed = FALSE;
14199   player->drop_pressed_delay = 0;
14200
14201   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14202     return FALSE;
14203
14204   player->is_snapping = TRUE;
14205   player->is_active = TRUE;
14206
14207   if (player->MovPos == 0)
14208   {
14209     player->is_moving = FALSE;
14210     player->is_digging = FALSE;
14211     player->is_collecting = FALSE;
14212   }
14213
14214   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14215     TEST_DrawLevelField(player->last_jx, player->last_jy);
14216
14217   TEST_DrawLevelField(x, y);
14218
14219   return TRUE;
14220 }
14221
14222 static boolean DropElement(struct PlayerInfo *player)
14223 {
14224   int old_element, new_element;
14225   int dropx = player->jx, dropy = player->jy;
14226   int drop_direction = player->MovDir;
14227   int drop_side = drop_direction;
14228   int drop_element = get_next_dropped_element(player);
14229
14230   /* do not drop an element on top of another element; when holding drop key
14231      pressed without moving, dropped element must move away before the next
14232      element can be dropped (this is especially important if the next element
14233      is dynamite, which can be placed on background for historical reasons) */
14234   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14235     return MP_ACTION;
14236
14237   if (IS_THROWABLE(drop_element))
14238   {
14239     dropx += GET_DX_FROM_DIR(drop_direction);
14240     dropy += GET_DY_FROM_DIR(drop_direction);
14241
14242     if (!IN_LEV_FIELD(dropx, dropy))
14243       return FALSE;
14244   }
14245
14246   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14247   new_element = drop_element;           /* default: no change when dropping */
14248
14249   /* check if player is active, not moving and ready to drop */
14250   if (!player->active || player->MovPos || player->drop_delay > 0)
14251     return FALSE;
14252
14253   /* check if player has anything that can be dropped */
14254   if (new_element == EL_UNDEFINED)
14255     return FALSE;
14256
14257   /* only set if player has anything that can be dropped */
14258   player->is_dropping_pressed = TRUE;
14259
14260   /* check if drop key was pressed long enough for EM style dynamite */
14261   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14262     return FALSE;
14263
14264   /* check if anything can be dropped at the current position */
14265   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14266     return FALSE;
14267
14268   /* collected custom elements can only be dropped on empty fields */
14269   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14270     return FALSE;
14271
14272   if (old_element != EL_EMPTY)
14273     Back[dropx][dropy] = old_element;   /* store old element on this field */
14274
14275   ResetGfxAnimation(dropx, dropy);
14276   ResetRandomAnimationValue(dropx, dropy);
14277
14278   if (player->inventory_size > 0 ||
14279       player->inventory_infinite_element != EL_UNDEFINED)
14280   {
14281     if (player->inventory_size > 0)
14282     {
14283       player->inventory_size--;
14284
14285       DrawGameDoorValues();
14286
14287       if (new_element == EL_DYNAMITE)
14288         new_element = EL_DYNAMITE_ACTIVE;
14289       else if (new_element == EL_EM_DYNAMITE)
14290         new_element = EL_EM_DYNAMITE_ACTIVE;
14291       else if (new_element == EL_SP_DISK_RED)
14292         new_element = EL_SP_DISK_RED_ACTIVE;
14293     }
14294
14295     Feld[dropx][dropy] = new_element;
14296
14297     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14298       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14299                           el2img(Feld[dropx][dropy]), 0);
14300
14301     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14302
14303     /* needed if previous element just changed to "empty" in the last frame */
14304     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14305
14306     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14307                                player->index_bit, drop_side);
14308     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14309                                         CE_PLAYER_DROPS_X,
14310                                         player->index_bit, drop_side);
14311
14312     TestIfElementTouchesCustomElement(dropx, dropy);
14313   }
14314   else          /* player is dropping a dyna bomb */
14315   {
14316     player->dynabombs_left--;
14317
14318     Feld[dropx][dropy] = new_element;
14319
14320     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14321       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14322                           el2img(Feld[dropx][dropy]), 0);
14323
14324     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14325   }
14326
14327   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14328     InitField_WithBug1(dropx, dropy, FALSE);
14329
14330   new_element = Feld[dropx][dropy];     /* element might have changed */
14331
14332   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14333       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14334   {
14335     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14336       MovDir[dropx][dropy] = drop_direction;
14337
14338     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14339
14340     /* do not cause impact style collision by dropping elements that can fall */
14341     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14342   }
14343
14344   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14345   player->is_dropping = TRUE;
14346
14347   player->drop_pressed_delay = 0;
14348   player->is_dropping_pressed = FALSE;
14349
14350   player->drop_x = dropx;
14351   player->drop_y = dropy;
14352
14353   return TRUE;
14354 }
14355
14356 /* ------------------------------------------------------------------------- */
14357 /* game sound playing functions                                              */
14358 /* ------------------------------------------------------------------------- */
14359
14360 static int *loop_sound_frame = NULL;
14361 static int *loop_sound_volume = NULL;
14362
14363 void InitPlayLevelSound()
14364 {
14365   int num_sounds = getSoundListSize();
14366
14367   checked_free(loop_sound_frame);
14368   checked_free(loop_sound_volume);
14369
14370   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14371   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14372 }
14373
14374 static void PlayLevelSound(int x, int y, int nr)
14375 {
14376   int sx = SCREENX(x), sy = SCREENY(y);
14377   int volume, stereo_position;
14378   int max_distance = 8;
14379   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14380
14381   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14382       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14383     return;
14384
14385   if (!IN_LEV_FIELD(x, y) ||
14386       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14387       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14388     return;
14389
14390   volume = SOUND_MAX_VOLUME;
14391
14392   if (!IN_SCR_FIELD(sx, sy))
14393   {
14394     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14395     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14396
14397     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14398   }
14399
14400   stereo_position = (SOUND_MAX_LEFT +
14401                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14402                      (SCR_FIELDX + 2 * max_distance));
14403
14404   if (IS_LOOP_SOUND(nr))
14405   {
14406     /* This assures that quieter loop sounds do not overwrite louder ones,
14407        while restarting sound volume comparison with each new game frame. */
14408
14409     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14410       return;
14411
14412     loop_sound_volume[nr] = volume;
14413     loop_sound_frame[nr] = FrameCounter;
14414   }
14415
14416   PlaySoundExt(nr, volume, stereo_position, type);
14417 }
14418
14419 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14420 {
14421   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14422                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14423                  y < LEVELY(BY1) ? LEVELY(BY1) :
14424                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14425                  sound_action);
14426 }
14427
14428 static void PlayLevelSoundAction(int x, int y, int action)
14429 {
14430   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14431 }
14432
14433 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14434 {
14435   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14436
14437   if (sound_effect != SND_UNDEFINED)
14438     PlayLevelSound(x, y, sound_effect);
14439 }
14440
14441 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14442                                               int action)
14443 {
14444   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14445
14446   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14447     PlayLevelSound(x, y, sound_effect);
14448 }
14449
14450 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14451 {
14452   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14453
14454   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14455     PlayLevelSound(x, y, sound_effect);
14456 }
14457
14458 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14459 {
14460   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14461
14462   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14463     StopSound(sound_effect);
14464 }
14465
14466 static int getLevelMusicNr()
14467 {
14468   if (levelset.music[level_nr] != MUS_UNDEFINED)
14469     return levelset.music[level_nr];            /* from config file */
14470   else
14471     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14472 }
14473
14474 static void FadeLevelSounds()
14475 {
14476   FadeSounds();
14477 }
14478
14479 static void FadeLevelMusic()
14480 {
14481   int music_nr = getLevelMusicNr();
14482   char *curr_music = getCurrentlyPlayingMusicFilename();
14483   char *next_music = getMusicInfoEntryFilename(music_nr);
14484
14485   if (!strEqual(curr_music, next_music))
14486     FadeMusic();
14487 }
14488
14489 void FadeLevelSoundsAndMusic()
14490 {
14491   FadeLevelSounds();
14492   FadeLevelMusic();
14493 }
14494
14495 static void PlayLevelMusic()
14496 {
14497   int music_nr = getLevelMusicNr();
14498   char *curr_music = getCurrentlyPlayingMusicFilename();
14499   char *next_music = getMusicInfoEntryFilename(music_nr);
14500
14501   if (!strEqual(curr_music, next_music))
14502     PlayMusic(music_nr);
14503 }
14504
14505 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14506 {
14507   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14508   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14509   int x = xx - 1 - offset;
14510   int y = yy - 1 - offset;
14511
14512   switch (sample)
14513   {
14514     case SAMPLE_blank:
14515       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14516       break;
14517
14518     case SAMPLE_roll:
14519       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14520       break;
14521
14522     case SAMPLE_stone:
14523       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14524       break;
14525
14526     case SAMPLE_nut:
14527       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14528       break;
14529
14530     case SAMPLE_crack:
14531       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14532       break;
14533
14534     case SAMPLE_bug:
14535       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14536       break;
14537
14538     case SAMPLE_tank:
14539       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14540       break;
14541
14542     case SAMPLE_android_clone:
14543       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14544       break;
14545
14546     case SAMPLE_android_move:
14547       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14548       break;
14549
14550     case SAMPLE_spring:
14551       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14552       break;
14553
14554     case SAMPLE_slurp:
14555       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14556       break;
14557
14558     case SAMPLE_eater:
14559       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14560       break;
14561
14562     case SAMPLE_eater_eat:
14563       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14564       break;
14565
14566     case SAMPLE_alien:
14567       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14568       break;
14569
14570     case SAMPLE_collect:
14571       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14572       break;
14573
14574     case SAMPLE_diamond:
14575       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14576       break;
14577
14578     case SAMPLE_squash:
14579       /* !!! CHECK THIS !!! */
14580 #if 1
14581       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14582 #else
14583       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14584 #endif
14585       break;
14586
14587     case SAMPLE_wonderfall:
14588       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14589       break;
14590
14591     case SAMPLE_drip:
14592       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14593       break;
14594
14595     case SAMPLE_push:
14596       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14597       break;
14598
14599     case SAMPLE_dirt:
14600       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14601       break;
14602
14603     case SAMPLE_acid:
14604       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14605       break;
14606
14607     case SAMPLE_ball:
14608       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14609       break;
14610
14611     case SAMPLE_grow:
14612       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14613       break;
14614
14615     case SAMPLE_wonder:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14617       break;
14618
14619     case SAMPLE_door:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14621       break;
14622
14623     case SAMPLE_exit_open:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14625       break;
14626
14627     case SAMPLE_exit_leave:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14629       break;
14630
14631     case SAMPLE_dynamite:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14633       break;
14634
14635     case SAMPLE_tick:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14637       break;
14638
14639     case SAMPLE_press:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14641       break;
14642
14643     case SAMPLE_wheel:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14645       break;
14646
14647     case SAMPLE_boom:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14649       break;
14650
14651     case SAMPLE_die:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14653       break;
14654
14655     case SAMPLE_time:
14656       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14657       break;
14658
14659     default:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14661       break;
14662   }
14663 }
14664
14665 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14666 {
14667   int element = map_element_SP_to_RND(element_sp);
14668   int action = map_action_SP_to_RND(action_sp);
14669   int offset = (setup.sp_show_border_elements ? 0 : 1);
14670   int x = xx - offset;
14671   int y = yy - offset;
14672
14673   PlayLevelSoundElementAction(x, y, element, action);
14674 }
14675
14676 void RaiseScore(int value)
14677 {
14678   local_player->score += value;
14679
14680   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14681
14682   DisplayGameControlValues();
14683 }
14684
14685 void RaiseScoreElement(int element)
14686 {
14687   switch (element)
14688   {
14689     case EL_EMERALD:
14690     case EL_BD_DIAMOND:
14691     case EL_EMERALD_YELLOW:
14692     case EL_EMERALD_RED:
14693     case EL_EMERALD_PURPLE:
14694     case EL_SP_INFOTRON:
14695       RaiseScore(level.score[SC_EMERALD]);
14696       break;
14697     case EL_DIAMOND:
14698       RaiseScore(level.score[SC_DIAMOND]);
14699       break;
14700     case EL_CRYSTAL:
14701       RaiseScore(level.score[SC_CRYSTAL]);
14702       break;
14703     case EL_PEARL:
14704       RaiseScore(level.score[SC_PEARL]);
14705       break;
14706     case EL_BUG:
14707     case EL_BD_BUTTERFLY:
14708     case EL_SP_ELECTRON:
14709       RaiseScore(level.score[SC_BUG]);
14710       break;
14711     case EL_SPACESHIP:
14712     case EL_BD_FIREFLY:
14713     case EL_SP_SNIKSNAK:
14714       RaiseScore(level.score[SC_SPACESHIP]);
14715       break;
14716     case EL_YAMYAM:
14717     case EL_DARK_YAMYAM:
14718       RaiseScore(level.score[SC_YAMYAM]);
14719       break;
14720     case EL_ROBOT:
14721       RaiseScore(level.score[SC_ROBOT]);
14722       break;
14723     case EL_PACMAN:
14724       RaiseScore(level.score[SC_PACMAN]);
14725       break;
14726     case EL_NUT:
14727       RaiseScore(level.score[SC_NUT]);
14728       break;
14729     case EL_DYNAMITE:
14730     case EL_EM_DYNAMITE:
14731     case EL_SP_DISK_RED:
14732     case EL_DYNABOMB_INCREASE_NUMBER:
14733     case EL_DYNABOMB_INCREASE_SIZE:
14734     case EL_DYNABOMB_INCREASE_POWER:
14735       RaiseScore(level.score[SC_DYNAMITE]);
14736       break;
14737     case EL_SHIELD_NORMAL:
14738     case EL_SHIELD_DEADLY:
14739       RaiseScore(level.score[SC_SHIELD]);
14740       break;
14741     case EL_EXTRA_TIME:
14742       RaiseScore(level.extra_time_score);
14743       break;
14744     case EL_KEY_1:
14745     case EL_KEY_2:
14746     case EL_KEY_3:
14747     case EL_KEY_4:
14748     case EL_EM_KEY_1:
14749     case EL_EM_KEY_2:
14750     case EL_EM_KEY_3:
14751     case EL_EM_KEY_4:
14752     case EL_EMC_KEY_5:
14753     case EL_EMC_KEY_6:
14754     case EL_EMC_KEY_7:
14755     case EL_EMC_KEY_8:
14756     case EL_DC_KEY_WHITE:
14757       RaiseScore(level.score[SC_KEY]);
14758       break;
14759     default:
14760       RaiseScore(element_info[element].collect_score);
14761       break;
14762   }
14763 }
14764
14765 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14766 {
14767   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14768   {
14769     /* closing door required in case of envelope style request dialogs */
14770     if (!skip_request)
14771       CloseDoor(DOOR_CLOSE_1);
14772
14773 #if defined(NETWORK_AVALIABLE)
14774     if (options.network)
14775       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14776     else
14777 #endif
14778     {
14779       if (quick_quit)
14780         FadeSkipNextFadeIn();
14781
14782       SetGameStatus(GAME_MODE_MAIN);
14783
14784       DrawMainMenu();
14785     }
14786   }
14787   else          /* continue playing the game */
14788   {
14789     if (tape.playing && tape.deactivate_display)
14790       TapeDeactivateDisplayOff(TRUE);
14791
14792     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14793
14794     if (tape.playing && tape.deactivate_display)
14795       TapeDeactivateDisplayOn();
14796   }
14797 }
14798
14799 void RequestQuitGame(boolean ask_if_really_quit)
14800 {
14801   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14802   boolean skip_request = AllPlayersGone || quick_quit;
14803
14804   RequestQuitGameExt(skip_request, quick_quit,
14805                      "Do you really want to quit the game?");
14806 }
14807
14808
14809 /* ------------------------------------------------------------------------- */
14810 /* random generator functions                                                */
14811 /* ------------------------------------------------------------------------- */
14812
14813 unsigned int InitEngineRandom_RND(int seed)
14814 {
14815   game.num_random_calls = 0;
14816
14817   return InitEngineRandom(seed);
14818 }
14819
14820 unsigned int RND(int max)
14821 {
14822   if (max > 0)
14823   {
14824     game.num_random_calls++;
14825
14826     return GetEngineRandom(max);
14827   }
14828
14829   return 0;
14830 }
14831
14832
14833 /* ------------------------------------------------------------------------- */
14834 /* game engine snapshot handling functions                                   */
14835 /* ------------------------------------------------------------------------- */
14836
14837 struct EngineSnapshotInfo
14838 {
14839   /* runtime values for custom element collect score */
14840   int collect_score[NUM_CUSTOM_ELEMENTS];
14841
14842   /* runtime values for group element choice position */
14843   int choice_pos[NUM_GROUP_ELEMENTS];
14844
14845   /* runtime values for belt position animations */
14846   int belt_graphic[4][NUM_BELT_PARTS];
14847   int belt_anim_mode[4][NUM_BELT_PARTS];
14848 };
14849
14850 static struct EngineSnapshotInfo engine_snapshot_rnd;
14851 static char *snapshot_level_identifier = NULL;
14852 static int snapshot_level_nr = -1;
14853
14854 static void SaveEngineSnapshotValues_RND()
14855 {
14856   static int belt_base_active_element[4] =
14857   {
14858     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14859     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14860     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14861     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14862   };
14863   int i, j;
14864
14865   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14866   {
14867     int element = EL_CUSTOM_START + i;
14868
14869     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14870   }
14871
14872   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14873   {
14874     int element = EL_GROUP_START + i;
14875
14876     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14877   }
14878
14879   for (i = 0; i < 4; i++)
14880   {
14881     for (j = 0; j < NUM_BELT_PARTS; j++)
14882     {
14883       int element = belt_base_active_element[i] + j;
14884       int graphic = el2img(element);
14885       int anim_mode = graphic_info[graphic].anim_mode;
14886
14887       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14888       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14889     }
14890   }
14891 }
14892
14893 static void LoadEngineSnapshotValues_RND()
14894 {
14895   unsigned int num_random_calls = game.num_random_calls;
14896   int i, j;
14897
14898   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14899   {
14900     int element = EL_CUSTOM_START + i;
14901
14902     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14903   }
14904
14905   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14906   {
14907     int element = EL_GROUP_START + i;
14908
14909     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14910   }
14911
14912   for (i = 0; i < 4; i++)
14913   {
14914     for (j = 0; j < NUM_BELT_PARTS; j++)
14915     {
14916       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14917       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14918
14919       graphic_info[graphic].anim_mode = anim_mode;
14920     }
14921   }
14922
14923   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14924   {
14925     InitRND(tape.random_seed);
14926     for (i = 0; i < num_random_calls; i++)
14927       RND(1);
14928   }
14929
14930   if (game.num_random_calls != num_random_calls)
14931   {
14932     Error(ERR_INFO, "number of random calls out of sync");
14933     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14934     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14935     Error(ERR_EXIT, "this should not happen -- please debug");
14936   }
14937 }
14938
14939 void FreeEngineSnapshotSingle()
14940 {
14941   FreeSnapshotSingle();
14942
14943   setString(&snapshot_level_identifier, NULL);
14944   snapshot_level_nr = -1;
14945 }
14946
14947 void FreeEngineSnapshotList()
14948 {
14949   FreeSnapshotList();
14950 }
14951
14952 ListNode *SaveEngineSnapshotBuffers()
14953 {
14954   ListNode *buffers = NULL;
14955
14956   /* copy some special values to a structure better suited for the snapshot */
14957
14958   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14959     SaveEngineSnapshotValues_RND();
14960   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14961     SaveEngineSnapshotValues_EM();
14962   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14963     SaveEngineSnapshotValues_SP(&buffers);
14964   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
14965     SaveEngineSnapshotValues_MM(&buffers);
14966
14967   /* save values stored in special snapshot structure */
14968
14969   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14970     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14971   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14972     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14973   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14974     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14975   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
14976     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
14977
14978   /* save further RND engine values */
14979
14980   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14981   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14982   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14983
14984   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14985   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14986   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14987   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14988
14989   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14990   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14991   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14992   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14993   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14994
14995   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14996   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14997   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14998
14999   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15000
15001   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15002
15003   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15004   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15005
15006   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15007   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15008   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15009   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15010   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15011   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15012   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15013   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15014   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15015   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15016   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15017   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15018   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15019   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15020   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15021   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15022   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15023   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15024
15025   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15026   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15027
15028   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15029   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15030   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15031
15032   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15033   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15034
15035   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15036   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15037   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15038   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15039   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15040
15041   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15042   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15043
15044 #if 0
15045   ListNode *node = engine_snapshot_list_rnd;
15046   int num_bytes = 0;
15047
15048   while (node != NULL)
15049   {
15050     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15051
15052     node = node->next;
15053   }
15054
15055   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15056 #endif
15057
15058   return buffers;
15059 }
15060
15061 void SaveEngineSnapshotSingle()
15062 {
15063   ListNode *buffers = SaveEngineSnapshotBuffers();
15064
15065   /* finally save all snapshot buffers to single snapshot */
15066   SaveSnapshotSingle(buffers);
15067
15068   /* save level identification information */
15069   setString(&snapshot_level_identifier, leveldir_current->identifier);
15070   snapshot_level_nr = level_nr;
15071 }
15072
15073 boolean CheckSaveEngineSnapshotToList()
15074 {
15075   boolean save_snapshot =
15076     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15077      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15078       game.snapshot.changed_action) ||
15079      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15080       game.snapshot.collected_item));
15081
15082   game.snapshot.changed_action = FALSE;
15083   game.snapshot.collected_item = FALSE;
15084   game.snapshot.save_snapshot = save_snapshot;
15085
15086   return save_snapshot;
15087 }
15088
15089 void SaveEngineSnapshotToList()
15090 {
15091   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15092       tape.quick_resume)
15093     return;
15094
15095   ListNode *buffers = SaveEngineSnapshotBuffers();
15096
15097   /* finally save all snapshot buffers to snapshot list */
15098   SaveSnapshotToList(buffers);
15099 }
15100
15101 void SaveEngineSnapshotToListInitial()
15102 {
15103   FreeEngineSnapshotList();
15104
15105   SaveEngineSnapshotToList();
15106 }
15107
15108 void LoadEngineSnapshotValues()
15109 {
15110   /* restore special values from snapshot structure */
15111
15112   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15113     LoadEngineSnapshotValues_RND();
15114   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15115     LoadEngineSnapshotValues_EM();
15116   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15117     LoadEngineSnapshotValues_SP();
15118   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15119     LoadEngineSnapshotValues_MM();
15120 }
15121
15122 void LoadEngineSnapshotSingle()
15123 {
15124   LoadSnapshotSingle();
15125
15126   LoadEngineSnapshotValues();
15127 }
15128
15129 void LoadEngineSnapshot_Undo(int steps)
15130 {
15131   LoadSnapshotFromList_Older(steps);
15132
15133   LoadEngineSnapshotValues();
15134 }
15135
15136 void LoadEngineSnapshot_Redo(int steps)
15137 {
15138   LoadSnapshotFromList_Newer(steps);
15139
15140   LoadEngineSnapshotValues();
15141 }
15142
15143 boolean CheckEngineSnapshotSingle()
15144 {
15145   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15146           snapshot_level_nr == level_nr);
15147 }
15148
15149 boolean CheckEngineSnapshotList()
15150 {
15151   return CheckSnapshotList();
15152 }
15153
15154
15155 /* ---------- new game button stuff ---------------------------------------- */
15156
15157 static struct
15158 {
15159   int graphic;
15160   struct XY *pos;
15161   int gadget_id;
15162   char *infotext;
15163 } gamebutton_info[NUM_GAME_BUTTONS] =
15164 {
15165   {
15166     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
15167     GAME_CTRL_ID_STOP,                  "stop game"
15168   },
15169   {
15170     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
15171     GAME_CTRL_ID_PAUSE,                 "pause game"
15172   },
15173   {
15174     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
15175     GAME_CTRL_ID_PLAY,                  "play game"
15176   },
15177   {
15178     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
15179     GAME_CTRL_ID_UNDO,                  "undo step"
15180   },
15181   {
15182     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
15183     GAME_CTRL_ID_REDO,                  "redo step"
15184   },
15185   {
15186     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
15187     GAME_CTRL_ID_SAVE,                  "save game"
15188   },
15189   {
15190     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
15191     GAME_CTRL_ID_PAUSE2,                "pause game"
15192   },
15193   {
15194     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
15195     GAME_CTRL_ID_LOAD,                  "load game"
15196   },
15197   {
15198     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
15199     SOUND_CTRL_ID_MUSIC,                "background music on/off"
15200   },
15201   {
15202     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
15203     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
15204   },
15205   {
15206     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
15207     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
15208   }
15209 };
15210
15211 void CreateGameButtons()
15212 {
15213   int i;
15214
15215   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15216   {
15217     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15218     struct XY *pos = gamebutton_info[i].pos;
15219     struct GadgetInfo *gi;
15220     int button_type;
15221     boolean checked;
15222     unsigned int event_mask;
15223     int base_x = (tape.show_game_buttons ? VX : DX);
15224     int base_y = (tape.show_game_buttons ? VY : DY);
15225     int gd_x   = gfx->src_x;
15226     int gd_y   = gfx->src_y;
15227     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15228     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15229     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15230     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15231     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15232     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15233     int id = i;
15234
15235     if (gfx->bitmap == NULL)
15236     {
15237       game_gadget[id] = NULL;
15238
15239       continue;
15240     }
15241
15242     if (id == GAME_CTRL_ID_STOP ||
15243         id == GAME_CTRL_ID_PLAY ||
15244         id == GAME_CTRL_ID_SAVE ||
15245         id == GAME_CTRL_ID_LOAD)
15246     {
15247       button_type = GD_TYPE_NORMAL_BUTTON;
15248       checked = FALSE;
15249       event_mask = GD_EVENT_RELEASED;
15250     }
15251     else if (id == GAME_CTRL_ID_UNDO ||
15252              id == GAME_CTRL_ID_REDO)
15253     {
15254       button_type = GD_TYPE_NORMAL_BUTTON;
15255       checked = FALSE;
15256       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15257     }
15258     else
15259     {
15260       button_type = GD_TYPE_CHECK_BUTTON;
15261       checked =
15262         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15263          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15264          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15265       event_mask = GD_EVENT_PRESSED;
15266     }
15267
15268     gi = CreateGadget(GDI_CUSTOM_ID, id,
15269                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15270                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15271                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15272                       GDI_WIDTH, gfx->width,
15273                       GDI_HEIGHT, gfx->height,
15274                       GDI_TYPE, button_type,
15275                       GDI_STATE, GD_BUTTON_UNPRESSED,
15276                       GDI_CHECKED, checked,
15277                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15278                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15279                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15280                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15281                       GDI_DIRECT_DRAW, FALSE,
15282                       GDI_EVENT_MASK, event_mask,
15283                       GDI_CALLBACK_ACTION, HandleGameButtons,
15284                       GDI_END);
15285
15286     if (gi == NULL)
15287       Error(ERR_EXIT, "cannot create gadget");
15288
15289     game_gadget[id] = gi;
15290   }
15291 }
15292
15293 void FreeGameButtons()
15294 {
15295   int i;
15296
15297   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15298     FreeGadget(game_gadget[i]);
15299 }
15300
15301 static void UnmapGameButtonsAtSamePosition(int id)
15302 {
15303   int i;
15304
15305   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15306     if (i != id &&
15307         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15308         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15309       UnmapGadget(game_gadget[i]);
15310 }
15311
15312 static void UnmapGameButtonsAtSamePosition_All()
15313 {
15314   if (setup.show_snapshot_buttons)
15315   {
15316     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15317     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15318     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15319   }
15320   else
15321   {
15322     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15323     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15324     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15325   }
15326 }
15327
15328 static void MapGameButtonsAtSamePosition(int id)
15329 {
15330   int i;
15331
15332   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15333     if (i != id &&
15334         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15335         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15336       MapGadget(game_gadget[i]);
15337
15338   UnmapGameButtonsAtSamePosition_All();
15339 }
15340
15341 void MapUndoRedoButtons()
15342 {
15343   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15344   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15345
15346   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15347   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15348
15349   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15350 }
15351
15352 void UnmapUndoRedoButtons()
15353 {
15354   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15355   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15356
15357   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15358   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15359
15360   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15361 }
15362
15363 void MapGameButtons()
15364 {
15365   int i;
15366
15367   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15368     if (i != GAME_CTRL_ID_UNDO &&
15369         i != GAME_CTRL_ID_REDO)
15370       MapGadget(game_gadget[i]);
15371
15372   UnmapGameButtonsAtSamePosition_All();
15373
15374   RedrawGameButtons();
15375 }
15376
15377 void UnmapGameButtons()
15378 {
15379   int i;
15380
15381   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15382     UnmapGadget(game_gadget[i]);
15383 }
15384
15385 void RedrawGameButtons()
15386 {
15387   int i;
15388
15389   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15390     RedrawGadget(game_gadget[i]);
15391
15392   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15393   redraw_mask &= ~REDRAW_ALL;
15394 }
15395
15396 void GameUndoRedoExt()
15397 {
15398   ClearPlayerAction();
15399
15400   tape.pausing = TRUE;
15401
15402   RedrawPlayfield();
15403   UpdateAndDisplayGameControlValues();
15404
15405   DrawCompleteVideoDisplay();
15406   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15407   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15408   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15409
15410   BackToFront();
15411 }
15412
15413 void GameUndo(int steps)
15414 {
15415   if (!CheckEngineSnapshotList())
15416     return;
15417
15418   LoadEngineSnapshot_Undo(steps);
15419
15420   GameUndoRedoExt();
15421 }
15422
15423 void GameRedo(int steps)
15424 {
15425   if (!CheckEngineSnapshotList())
15426     return;
15427
15428   LoadEngineSnapshot_Redo(steps);
15429
15430   GameUndoRedoExt();
15431 }
15432
15433 static void HandleGameButtonsExt(int id, int button)
15434 {
15435   static boolean game_undo_executed = FALSE;
15436   int steps = BUTTON_STEPSIZE(button);
15437   boolean handle_game_buttons =
15438     (game_status == GAME_MODE_PLAYING ||
15439      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15440
15441   if (!handle_game_buttons)
15442     return;
15443
15444   switch (id)
15445   {
15446     case GAME_CTRL_ID_STOP:
15447       if (game_status == GAME_MODE_MAIN)
15448         break;
15449
15450       if (tape.playing)
15451         TapeStop();
15452       else
15453         RequestQuitGame(TRUE);
15454
15455       break;
15456
15457     case GAME_CTRL_ID_PAUSE:
15458     case GAME_CTRL_ID_PAUSE2:
15459       if (options.network && game_status == GAME_MODE_PLAYING)
15460       {
15461 #if defined(NETWORK_AVALIABLE)
15462         if (tape.pausing)
15463           SendToServer_ContinuePlaying();
15464         else
15465           SendToServer_PausePlaying();
15466 #endif
15467       }
15468       else
15469         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15470
15471       game_undo_executed = FALSE;
15472
15473       break;
15474
15475     case GAME_CTRL_ID_PLAY:
15476       if (game_status == GAME_MODE_MAIN)
15477       {
15478         StartGameActions(options.network, setup.autorecord, level.random_seed);
15479       }
15480       else if (tape.pausing)
15481       {
15482 #if defined(NETWORK_AVALIABLE)
15483         if (options.network)
15484           SendToServer_ContinuePlaying();
15485         else
15486 #endif
15487           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15488       }
15489       break;
15490
15491     case GAME_CTRL_ID_UNDO:
15492       // Important: When using "save snapshot when collecting an item" mode,
15493       // load last (current) snapshot for first "undo" after pressing "pause"
15494       // (else the last-but-one snapshot would be loaded, because the snapshot
15495       // pointer already points to the last snapshot when pressing "pause",
15496       // which is fine for "every step/move" mode, but not for "every collect")
15497       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15498           !game_undo_executed)
15499         steps--;
15500
15501       game_undo_executed = TRUE;
15502
15503       GameUndo(steps);
15504       break;
15505
15506     case GAME_CTRL_ID_REDO:
15507       GameRedo(steps);
15508       break;
15509
15510     case GAME_CTRL_ID_SAVE:
15511       TapeQuickSave();
15512       break;
15513
15514     case GAME_CTRL_ID_LOAD:
15515       TapeQuickLoad();
15516       break;
15517
15518     case SOUND_CTRL_ID_MUSIC:
15519       if (setup.sound_music)
15520       { 
15521         setup.sound_music = FALSE;
15522
15523         FadeMusic();
15524       }
15525       else if (audio.music_available)
15526       { 
15527         setup.sound = setup.sound_music = TRUE;
15528
15529         SetAudioMode(setup.sound);
15530
15531         PlayLevelMusic();
15532       }
15533       break;
15534
15535     case SOUND_CTRL_ID_LOOPS:
15536       if (setup.sound_loops)
15537         setup.sound_loops = FALSE;
15538       else if (audio.loops_available)
15539       {
15540         setup.sound = setup.sound_loops = TRUE;
15541
15542         SetAudioMode(setup.sound);
15543       }
15544       break;
15545
15546     case SOUND_CTRL_ID_SIMPLE:
15547       if (setup.sound_simple)
15548         setup.sound_simple = FALSE;
15549       else if (audio.sound_available)
15550       {
15551         setup.sound = setup.sound_simple = TRUE;
15552
15553         SetAudioMode(setup.sound);
15554       }
15555       break;
15556
15557     default:
15558       break;
15559   }
15560 }
15561
15562 static void HandleGameButtons(struct GadgetInfo *gi)
15563 {
15564   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15565 }
15566
15567 void HandleSoundButtonKeys(Key key)
15568 {
15569
15570   if (key == setup.shortcut.sound_simple)
15571     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15572   else if (key == setup.shortcut.sound_loops)
15573     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15574   else if (key == setup.shortcut.sound_music)
15575     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15576 }