added workaround for 'wrong animation frames' bug (with debug output)
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 // test element position in level set "test_gfxframe" / level "000"
29 #define DEBUG_GFXFRAME_X        11
30 #define DEBUG_GFXFRAME_Y        9
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_AMOEBA_CODE     FALSE
34
35 /* EXPERIMENTAL STUFF */
36 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
37 #define USE_QUICKSAND_IMPACT_BUGFIX     0
38 #define USE_DELAYED_GFX_REDRAW          0
39 #define USE_NEW_PLAYER_ASSIGNMENTS      1
40
41 #if USE_DELAYED_GFX_REDRAW
42 #define TEST_DrawLevelField(x, y)                               \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE
44 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
46 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
47         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
48 #define TEST_DrawTwinkleOnField(x, y)                           \
49         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
50 #else
51 #define TEST_DrawLevelField(x, y)                               \
52              DrawLevelField(x, y)
53 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
54              DrawLevelFieldCrumbled(x, y)
55 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
56              DrawLevelFieldCrumbledNeighbours(x, y)
57 #define TEST_DrawTwinkleOnField(x, y)                           \
58              DrawTwinkleOnField(x, y)
59 #endif
60
61
62 /* for DigField() */
63 #define DF_NO_PUSH              0
64 #define DF_DIG                  1
65 #define DF_SNAP                 2
66
67 /* for MovePlayer() */
68 #define MP_NO_ACTION            0
69 #define MP_MOVING               1
70 #define MP_ACTION               2
71 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
72
73 /* for ScrollPlayer() */
74 #define SCROLL_INIT             0
75 #define SCROLL_GO_ON            1
76
77 /* for Bang()/Explode() */
78 #define EX_PHASE_START          0
79 #define EX_TYPE_NONE            0
80 #define EX_TYPE_NORMAL          (1 << 0)
81 #define EX_TYPE_CENTER          (1 << 1)
82 #define EX_TYPE_BORDER          (1 << 2)
83 #define EX_TYPE_CROSS           (1 << 3)
84 #define EX_TYPE_DYNA            (1 << 4)
85 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
86
87 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
88 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
89 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
90 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
91
92 /* game panel display and control definitions */
93 #define GAME_PANEL_LEVEL_NUMBER                 0
94 #define GAME_PANEL_GEMS                         1
95 #define GAME_PANEL_INVENTORY_COUNT              2
96 #define GAME_PANEL_INVENTORY_FIRST_1            3
97 #define GAME_PANEL_INVENTORY_FIRST_2            4
98 #define GAME_PANEL_INVENTORY_FIRST_3            5
99 #define GAME_PANEL_INVENTORY_FIRST_4            6
100 #define GAME_PANEL_INVENTORY_FIRST_5            7
101 #define GAME_PANEL_INVENTORY_FIRST_6            8
102 #define GAME_PANEL_INVENTORY_FIRST_7            9
103 #define GAME_PANEL_INVENTORY_FIRST_8            10
104 #define GAME_PANEL_INVENTORY_LAST_1             11
105 #define GAME_PANEL_INVENTORY_LAST_2             12
106 #define GAME_PANEL_INVENTORY_LAST_3             13
107 #define GAME_PANEL_INVENTORY_LAST_4             14
108 #define GAME_PANEL_INVENTORY_LAST_5             15
109 #define GAME_PANEL_INVENTORY_LAST_6             16
110 #define GAME_PANEL_INVENTORY_LAST_7             17
111 #define GAME_PANEL_INVENTORY_LAST_8             18
112 #define GAME_PANEL_KEY_1                        19
113 #define GAME_PANEL_KEY_2                        20
114 #define GAME_PANEL_KEY_3                        21
115 #define GAME_PANEL_KEY_4                        22
116 #define GAME_PANEL_KEY_5                        23
117 #define GAME_PANEL_KEY_6                        24
118 #define GAME_PANEL_KEY_7                        25
119 #define GAME_PANEL_KEY_8                        26
120 #define GAME_PANEL_KEY_WHITE                    27
121 #define GAME_PANEL_KEY_WHITE_COUNT              28
122 #define GAME_PANEL_SCORE                        29
123 #define GAME_PANEL_HIGHSCORE                    30
124 #define GAME_PANEL_TIME                         31
125 #define GAME_PANEL_TIME_HH                      32
126 #define GAME_PANEL_TIME_MM                      33
127 #define GAME_PANEL_TIME_SS                      34
128 #define GAME_PANEL_FRAME                        35
129 #define GAME_PANEL_SHIELD_NORMAL                36
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
131 #define GAME_PANEL_SHIELD_DEADLY                38
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
133 #define GAME_PANEL_EXIT                         40
134 #define GAME_PANEL_EMC_MAGIC_BALL               41
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
136 #define GAME_PANEL_LIGHT_SWITCH                 43
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
138 #define GAME_PANEL_TIMEGATE_SWITCH              45
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
140 #define GAME_PANEL_SWITCHGATE_SWITCH            47
141 #define GAME_PANEL_EMC_LENSES                   48
142 #define GAME_PANEL_EMC_LENSES_TIME              49
143 #define GAME_PANEL_EMC_MAGNIFIER                50
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
145 #define GAME_PANEL_BALLOON_SWITCH               52
146 #define GAME_PANEL_DYNABOMB_NUMBER              53
147 #define GAME_PANEL_DYNABOMB_SIZE                54
148 #define GAME_PANEL_DYNABOMB_POWER               55
149 #define GAME_PANEL_PENGUINS                     56
150 #define GAME_PANEL_SOKOBAN_OBJECTS              57
151 #define GAME_PANEL_SOKOBAN_FIELDS               58
152 #define GAME_PANEL_ROBOT_WHEEL                  59
153 #define GAME_PANEL_CONVEYOR_BELT_1              60
154 #define GAME_PANEL_CONVEYOR_BELT_2              61
155 #define GAME_PANEL_CONVEYOR_BELT_3              62
156 #define GAME_PANEL_CONVEYOR_BELT_4              63
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
161 #define GAME_PANEL_MAGIC_WALL                   68
162 #define GAME_PANEL_MAGIC_WALL_TIME              69
163 #define GAME_PANEL_GRAVITY_STATE                70
164 #define GAME_PANEL_GRAPHIC_1                    71
165 #define GAME_PANEL_GRAPHIC_2                    72
166 #define GAME_PANEL_GRAPHIC_3                    73
167 #define GAME_PANEL_GRAPHIC_4                    74
168 #define GAME_PANEL_GRAPHIC_5                    75
169 #define GAME_PANEL_GRAPHIC_6                    76
170 #define GAME_PANEL_GRAPHIC_7                    77
171 #define GAME_PANEL_GRAPHIC_8                    78
172 #define GAME_PANEL_ELEMENT_1                    79
173 #define GAME_PANEL_ELEMENT_2                    80
174 #define GAME_PANEL_ELEMENT_3                    81
175 #define GAME_PANEL_ELEMENT_4                    82
176 #define GAME_PANEL_ELEMENT_5                    83
177 #define GAME_PANEL_ELEMENT_6                    84
178 #define GAME_PANEL_ELEMENT_7                    85
179 #define GAME_PANEL_ELEMENT_8                    86
180 #define GAME_PANEL_ELEMENT_COUNT_1              87
181 #define GAME_PANEL_ELEMENT_COUNT_2              88
182 #define GAME_PANEL_ELEMENT_COUNT_3              89
183 #define GAME_PANEL_ELEMENT_COUNT_4              90
184 #define GAME_PANEL_ELEMENT_COUNT_5              91
185 #define GAME_PANEL_ELEMENT_COUNT_6              92
186 #define GAME_PANEL_ELEMENT_COUNT_7              93
187 #define GAME_PANEL_ELEMENT_COUNT_8              94
188 #define GAME_PANEL_CE_SCORE_1                   95
189 #define GAME_PANEL_CE_SCORE_2                   96
190 #define GAME_PANEL_CE_SCORE_3                   97
191 #define GAME_PANEL_CE_SCORE_4                   98
192 #define GAME_PANEL_CE_SCORE_5                   99
193 #define GAME_PANEL_CE_SCORE_6                   100
194 #define GAME_PANEL_CE_SCORE_7                   101
195 #define GAME_PANEL_CE_SCORE_8                   102
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
204 #define GAME_PANEL_PLAYER_NAME                  111
205 #define GAME_PANEL_LEVEL_NAME                   112
206 #define GAME_PANEL_LEVEL_AUTHOR                 113
207
208 #define NUM_GAME_PANEL_CONTROLS                 114
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 value, last_value;
226   int frame, last_frame;
227   int gfx_frame;
228   int gfx_random;
229 };
230
231 static struct GamePanelControlInfo game_panel_controls[] =
232 {
233   {
234     GAME_PANEL_LEVEL_NUMBER,
235     &game.panel.level_number,
236     TYPE_INTEGER,
237   },
238   {
239     GAME_PANEL_GEMS,
240     &game.panel.gems,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_INVENTORY_COUNT,
245     &game.panel.inventory_count,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_INVENTORY_FIRST_1,
250     &game.panel.inventory_first[0],
251     TYPE_ELEMENT,
252   },
253   {
254     GAME_PANEL_INVENTORY_FIRST_2,
255     &game.panel.inventory_first[1],
256     TYPE_ELEMENT,
257   },
258   {
259     GAME_PANEL_INVENTORY_FIRST_3,
260     &game.panel.inventory_first[2],
261     TYPE_ELEMENT,
262   },
263   {
264     GAME_PANEL_INVENTORY_FIRST_4,
265     &game.panel.inventory_first[3],
266     TYPE_ELEMENT,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_5,
270     &game.panel.inventory_first[4],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_6,
275     &game.panel.inventory_first[5],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_7,
280     &game.panel.inventory_first[6],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_8,
285     &game.panel.inventory_first[7],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_LAST_1,
290     &game.panel.inventory_last[0],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_LAST_2,
295     &game.panel.inventory_last[1],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_LAST_3,
300     &game.panel.inventory_last[2],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_LAST_4,
305     &game.panel.inventory_last[3],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_5,
310     &game.panel.inventory_last[4],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_6,
315     &game.panel.inventory_last[5],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_7,
320     &game.panel.inventory_last[6],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_8,
325     &game.panel.inventory_last[7],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_KEY_1,
330     &game.panel.key[0],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_KEY_2,
335     &game.panel.key[1],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_KEY_3,
340     &game.panel.key[2],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_KEY_4,
345     &game.panel.key[3],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_5,
350     &game.panel.key[4],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_6,
355     &game.panel.key[5],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_7,
360     &game.panel.key[6],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_8,
365     &game.panel.key[7],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_WHITE,
370     &game.panel.key_white,
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_WHITE_COUNT,
375     &game.panel.key_white_count,
376     TYPE_INTEGER,
377   },
378   {
379     GAME_PANEL_SCORE,
380     &game.panel.score,
381     TYPE_INTEGER,
382   },
383   {
384     GAME_PANEL_HIGHSCORE,
385     &game.panel.highscore,
386     TYPE_INTEGER,
387   },
388   {
389     GAME_PANEL_TIME,
390     &game.panel.time,
391     TYPE_INTEGER,
392   },
393   {
394     GAME_PANEL_TIME_HH,
395     &game.panel.time_hh,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_TIME_MM,
400     &game.panel.time_mm,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_TIME_SS,
405     &game.panel.time_ss,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_FRAME,
410     &game.panel.frame,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_SHIELD_NORMAL,
415     &game.panel.shield_normal,
416     TYPE_ELEMENT,
417   },
418   {
419     GAME_PANEL_SHIELD_NORMAL_TIME,
420     &game.panel.shield_normal_time,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_SHIELD_DEADLY,
425     &game.panel.shield_deadly,
426     TYPE_ELEMENT,
427   },
428   {
429     GAME_PANEL_SHIELD_DEADLY_TIME,
430     &game.panel.shield_deadly_time,
431     TYPE_INTEGER,
432   },
433   {
434     GAME_PANEL_EXIT,
435     &game.panel.exit,
436     TYPE_ELEMENT,
437   },
438   {
439     GAME_PANEL_EMC_MAGIC_BALL,
440     &game.panel.emc_magic_ball,
441     TYPE_ELEMENT,
442   },
443   {
444     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
445     &game.panel.emc_magic_ball_switch,
446     TYPE_ELEMENT,
447   },
448   {
449     GAME_PANEL_LIGHT_SWITCH,
450     &game.panel.light_switch,
451     TYPE_ELEMENT,
452   },
453   {
454     GAME_PANEL_LIGHT_SWITCH_TIME,
455     &game.panel.light_switch_time,
456     TYPE_INTEGER,
457   },
458   {
459     GAME_PANEL_TIMEGATE_SWITCH,
460     &game.panel.timegate_switch,
461     TYPE_ELEMENT,
462   },
463   {
464     GAME_PANEL_TIMEGATE_SWITCH_TIME,
465     &game.panel.timegate_switch_time,
466     TYPE_INTEGER,
467   },
468   {
469     GAME_PANEL_SWITCHGATE_SWITCH,
470     &game.panel.switchgate_switch,
471     TYPE_ELEMENT,
472   },
473   {
474     GAME_PANEL_EMC_LENSES,
475     &game.panel.emc_lenses,
476     TYPE_ELEMENT,
477   },
478   {
479     GAME_PANEL_EMC_LENSES_TIME,
480     &game.panel.emc_lenses_time,
481     TYPE_INTEGER,
482   },
483   {
484     GAME_PANEL_EMC_MAGNIFIER,
485     &game.panel.emc_magnifier,
486     TYPE_ELEMENT,
487   },
488   {
489     GAME_PANEL_EMC_MAGNIFIER_TIME,
490     &game.panel.emc_magnifier_time,
491     TYPE_INTEGER,
492   },
493   {
494     GAME_PANEL_BALLOON_SWITCH,
495     &game.panel.balloon_switch,
496     TYPE_ELEMENT,
497   },
498   {
499     GAME_PANEL_DYNABOMB_NUMBER,
500     &game.panel.dynabomb_number,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_DYNABOMB_SIZE,
505     &game.panel.dynabomb_size,
506     TYPE_INTEGER,
507   },
508   {
509     GAME_PANEL_DYNABOMB_POWER,
510     &game.panel.dynabomb_power,
511     TYPE_ELEMENT,
512   },
513   {
514     GAME_PANEL_PENGUINS,
515     &game.panel.penguins,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_SOKOBAN_OBJECTS,
520     &game.panel.sokoban_objects,
521     TYPE_INTEGER,
522   },
523   {
524     GAME_PANEL_SOKOBAN_FIELDS,
525     &game.panel.sokoban_fields,
526     TYPE_INTEGER,
527   },
528   {
529     GAME_PANEL_ROBOT_WHEEL,
530     &game.panel.robot_wheel,
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_CONVEYOR_BELT_1,
535     &game.panel.conveyor_belt[0],
536     TYPE_ELEMENT,
537   },
538   {
539     GAME_PANEL_CONVEYOR_BELT_2,
540     &game.panel.conveyor_belt[1],
541     TYPE_ELEMENT,
542   },
543   {
544     GAME_PANEL_CONVEYOR_BELT_3,
545     &game.panel.conveyor_belt[2],
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_CONVEYOR_BELT_4,
550     &game.panel.conveyor_belt[3],
551     TYPE_ELEMENT,
552   },
553   {
554     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
555     &game.panel.conveyor_belt_switch[0],
556     TYPE_ELEMENT,
557   },
558   {
559     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
560     &game.panel.conveyor_belt_switch[1],
561     TYPE_ELEMENT,
562   },
563   {
564     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
565     &game.panel.conveyor_belt_switch[2],
566     TYPE_ELEMENT,
567   },
568   {
569     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
570     &game.panel.conveyor_belt_switch[3],
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_MAGIC_WALL,
575     &game.panel.magic_wall,
576     TYPE_ELEMENT,
577   },
578   {
579     GAME_PANEL_MAGIC_WALL_TIME,
580     &game.panel.magic_wall_time,
581     TYPE_INTEGER,
582   },
583   {
584     GAME_PANEL_GRAVITY_STATE,
585     &game.panel.gravity_state,
586     TYPE_STRING,
587   },
588   {
589     GAME_PANEL_GRAPHIC_1,
590     &game.panel.graphic[0],
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_GRAPHIC_2,
595     &game.panel.graphic[1],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_GRAPHIC_3,
600     &game.panel.graphic[2],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_GRAPHIC_4,
605     &game.panel.graphic[3],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_GRAPHIC_5,
610     &game.panel.graphic[4],
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_GRAPHIC_6,
615     &game.panel.graphic[5],
616     TYPE_ELEMENT,
617   },
618   {
619     GAME_PANEL_GRAPHIC_7,
620     &game.panel.graphic[6],
621     TYPE_ELEMENT,
622   },
623   {
624     GAME_PANEL_GRAPHIC_8,
625     &game.panel.graphic[7],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_ELEMENT_1,
630     &game.panel.element[0],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_ELEMENT_2,
635     &game.panel.element[1],
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_ELEMENT_3,
640     &game.panel.element[2],
641     TYPE_ELEMENT,
642   },
643   {
644     GAME_PANEL_ELEMENT_4,
645     &game.panel.element[3],
646     TYPE_ELEMENT,
647   },
648   {
649     GAME_PANEL_ELEMENT_5,
650     &game.panel.element[4],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_ELEMENT_6,
655     &game.panel.element[5],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_ELEMENT_7,
660     &game.panel.element[6],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_ELEMENT_8,
665     &game.panel.element[7],
666     TYPE_ELEMENT,
667   },
668   {
669     GAME_PANEL_ELEMENT_COUNT_1,
670     &game.panel.element_count[0],
671     TYPE_INTEGER,
672   },
673   {
674     GAME_PANEL_ELEMENT_COUNT_2,
675     &game.panel.element_count[1],
676     TYPE_INTEGER,
677   },
678   {
679     GAME_PANEL_ELEMENT_COUNT_3,
680     &game.panel.element_count[2],
681     TYPE_INTEGER,
682   },
683   {
684     GAME_PANEL_ELEMENT_COUNT_4,
685     &game.panel.element_count[3],
686     TYPE_INTEGER,
687   },
688   {
689     GAME_PANEL_ELEMENT_COUNT_5,
690     &game.panel.element_count[4],
691     TYPE_INTEGER,
692   },
693   {
694     GAME_PANEL_ELEMENT_COUNT_6,
695     &game.panel.element_count[5],
696     TYPE_INTEGER,
697   },
698   {
699     GAME_PANEL_ELEMENT_COUNT_7,
700     &game.panel.element_count[6],
701     TYPE_INTEGER,
702   },
703   {
704     GAME_PANEL_ELEMENT_COUNT_8,
705     &game.panel.element_count[7],
706     TYPE_INTEGER,
707   },
708   {
709     GAME_PANEL_CE_SCORE_1,
710     &game.panel.ce_score[0],
711     TYPE_INTEGER,
712   },
713   {
714     GAME_PANEL_CE_SCORE_2,
715     &game.panel.ce_score[1],
716     TYPE_INTEGER,
717   },
718   {
719     GAME_PANEL_CE_SCORE_3,
720     &game.panel.ce_score[2],
721     TYPE_INTEGER,
722   },
723   {
724     GAME_PANEL_CE_SCORE_4,
725     &game.panel.ce_score[3],
726     TYPE_INTEGER,
727   },
728   {
729     GAME_PANEL_CE_SCORE_5,
730     &game.panel.ce_score[4],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_CE_SCORE_6,
735     &game.panel.ce_score[5],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_CE_SCORE_7,
740     &game.panel.ce_score[6],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_CE_SCORE_8,
745     &game.panel.ce_score[7],
746     TYPE_INTEGER,
747   },
748   {
749     GAME_PANEL_CE_SCORE_1_ELEMENT,
750     &game.panel.ce_score_element[0],
751     TYPE_ELEMENT,
752   },
753   {
754     GAME_PANEL_CE_SCORE_2_ELEMENT,
755     &game.panel.ce_score_element[1],
756     TYPE_ELEMENT,
757   },
758   {
759     GAME_PANEL_CE_SCORE_3_ELEMENT,
760     &game.panel.ce_score_element[2],
761     TYPE_ELEMENT,
762   },
763   {
764     GAME_PANEL_CE_SCORE_4_ELEMENT,
765     &game.panel.ce_score_element[3],
766     TYPE_ELEMENT,
767   },
768   {
769     GAME_PANEL_CE_SCORE_5_ELEMENT,
770     &game.panel.ce_score_element[4],
771     TYPE_ELEMENT,
772   },
773   {
774     GAME_PANEL_CE_SCORE_6_ELEMENT,
775     &game.panel.ce_score_element[5],
776     TYPE_ELEMENT,
777   },
778   {
779     GAME_PANEL_CE_SCORE_7_ELEMENT,
780     &game.panel.ce_score_element[6],
781     TYPE_ELEMENT,
782   },
783   {
784     GAME_PANEL_CE_SCORE_8_ELEMENT,
785     &game.panel.ce_score_element[7],
786     TYPE_ELEMENT,
787   },
788   {
789     GAME_PANEL_PLAYER_NAME,
790     &game.panel.player_name,
791     TYPE_STRING,
792   },
793   {
794     GAME_PANEL_LEVEL_NAME,
795     &game.panel.level_name,
796     TYPE_STRING,
797   },
798   {
799     GAME_PANEL_LEVEL_AUTHOR,
800     &game.panel.level_author,
801     TYPE_STRING,
802   },
803
804   {
805     -1,
806     NULL,
807     -1,
808   }
809 };
810
811 /* values for delayed check of falling and moving elements and for collision */
812 #define CHECK_DELAY_MOVING      3
813 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
814 #define CHECK_DELAY_COLLISION   2
815 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
816
817 /* values for initial player move delay (initial delay counter value) */
818 #define INITIAL_MOVE_DELAY_OFF  -1
819 #define INITIAL_MOVE_DELAY_ON   0
820
821 /* values for player movement speed (which is in fact a delay value) */
822 #define MOVE_DELAY_MIN_SPEED    32
823 #define MOVE_DELAY_NORMAL_SPEED 8
824 #define MOVE_DELAY_HIGH_SPEED   4
825 #define MOVE_DELAY_MAX_SPEED    1
826
827 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
828 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
829
830 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
831 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
832
833 /* values for other actions */
834 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
835 #define MOVE_STEPSIZE_MIN       (1)
836 #define MOVE_STEPSIZE_MAX       (TILEX)
837
838 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
839 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
840
841 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
842
843 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
844                                  RND(element_info[e].push_delay_random))
845 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
846                                  RND(element_info[e].drop_delay_random))
847 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
848                                  RND(element_info[e].move_delay_random))
849 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
850                                     (element_info[e].move_delay_random))
851 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
852                                  RND(element_info[e].ce_value_random_initial))
853 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
854 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
855                                  RND((c)->delay_random * (c)->delay_frames))
856 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
857                                  RND((c)->delay_random))
858
859
860 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
861          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
862
863 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
864         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
865          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
866          (be) + (e) - EL_SELF)
867
868 #define GET_PLAYER_FROM_BITS(p)                                         \
869         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
870
871 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
872         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
873          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
874          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
875          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
876          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
877          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
878          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
879          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
880          (e))
881
882 #define CAN_GROW_INTO(e)                                                \
883         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (condition)))
888
889 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
890                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
891                                         (CAN_MOVE_INTO_ACID(e) &&       \
892                                          Feld[x][y] == EL_ACID) ||      \
893                                         (condition)))
894
895 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
896                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
897                                         (CAN_MOVE_INTO_ACID(e) &&       \
898                                          Feld[x][y] == EL_ACID) ||      \
899                                         (condition)))
900
901 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
902                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
903                                         (condition) ||                  \
904                                         (CAN_MOVE_INTO_ACID(e) &&       \
905                                          Feld[x][y] == EL_ACID) ||      \
906                                         (DONT_COLLIDE_WITH(e) &&        \
907                                          IS_PLAYER(x, y) &&             \
908                                          !PLAYER_ENEMY_PROTECTED(x, y))))
909
910 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
911         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
912
913 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
914         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
915
916 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
917         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
918
919 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
920         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
921                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
922
923 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
924         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
925
926 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
927         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
928
929 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
930         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
931
932 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
933         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
934
935 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
936         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
937
938 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
939         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
940                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
941                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
942                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
943                                                  IS_FOOD_PENGUIN(Feld[x][y])))
944 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
946
947 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
949
950 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
951         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
952
953 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
954         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
955                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
956
957 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
958
959 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
960                 (!IS_PLAYER(x, y) &&                                    \
961                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
962
963 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
965
966 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
967 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
968
969 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
970 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
971 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
972 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
973
974 /* game button identifiers */
975 #define GAME_CTRL_ID_STOP               0
976 #define GAME_CTRL_ID_PAUSE              1
977 #define GAME_CTRL_ID_PLAY               2
978 #define GAME_CTRL_ID_UNDO               3
979 #define GAME_CTRL_ID_REDO               4
980 #define GAME_CTRL_ID_SAVE               5
981 #define GAME_CTRL_ID_PAUSE2             6
982 #define GAME_CTRL_ID_LOAD               7
983 #define SOUND_CTRL_ID_MUSIC             8
984 #define SOUND_CTRL_ID_LOOPS             9
985 #define SOUND_CTRL_ID_SIMPLE            10
986
987 #define NUM_GAME_BUTTONS                11
988
989
990 /* forward declaration for internal use */
991
992 static void CreateField(int, int, int);
993
994 static void ResetGfxAnimation(int, int);
995
996 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
997 static void AdvanceFrameAndPlayerCounters(int);
998
999 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1000 static boolean MovePlayer(struct PlayerInfo *, int, int);
1001 static void ScrollPlayer(struct PlayerInfo *, int);
1002 static void ScrollScreen(struct PlayerInfo *, int);
1003
1004 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1005 static boolean DigFieldByCE(int, int, int);
1006 static boolean SnapField(struct PlayerInfo *, int, int);
1007 static boolean DropElement(struct PlayerInfo *);
1008
1009 static void InitBeltMovement(void);
1010 static void CloseAllOpenTimegates(void);
1011 static void CheckGravityMovement(struct PlayerInfo *);
1012 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1013 static void KillPlayerUnlessEnemyProtected(int, int);
1014 static void KillPlayerUnlessExplosionProtected(int, int);
1015
1016 static void TestIfPlayerTouchesCustomElement(int, int);
1017 static void TestIfElementTouchesCustomElement(int, int);
1018 static void TestIfElementHitsCustomElement(int, int, int);
1019
1020 static void HandleElementChange(int, int, int);
1021 static void ExecuteCustomElementAction(int, int, int, int);
1022 static boolean ChangeElement(int, int, int, int);
1023
1024 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1025 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1026         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1027 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1028         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1029 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1030         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1031 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1032         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1033
1034 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1035 #define CheckElementChange(x, y, e, te, ev)                             \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1037 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1038         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1039 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1040         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1041
1042 static void PlayLevelSound(int, int, int);
1043 static void PlayLevelSoundNearest(int, int, int);
1044 static void PlayLevelSoundAction(int, int, int);
1045 static void PlayLevelSoundElementAction(int, int, int, int);
1046 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1047 static void PlayLevelSoundActionIfLoop(int, int, int);
1048 static void StopLevelSoundActionIfLoop(int, int, int);
1049 static void PlayLevelMusic();
1050
1051 static void HandleGameButtons(struct GadgetInfo *);
1052
1053 int AmoebeNachbarNr(int, int);
1054 void AmoebeUmwandeln(int, int);
1055 void ContinueMoving(int, int);
1056 void Bang(int, int);
1057 void InitMovDir(int, int);
1058 void InitAmoebaNr(int, int);
1059 int NewHiScore(void);
1060
1061 void TestIfGoodThingHitsBadThing(int, int, int);
1062 void TestIfBadThingHitsGoodThing(int, int, int);
1063 void TestIfPlayerTouchesBadThing(int, int);
1064 void TestIfPlayerRunsIntoBadThing(int, int, int);
1065 void TestIfBadThingTouchesPlayer(int, int);
1066 void TestIfBadThingRunsIntoPlayer(int, int, int);
1067 void TestIfFriendTouchesBadThing(int, int);
1068 void TestIfBadThingTouchesFriend(int, int);
1069 void TestIfBadThingTouchesOtherBadThing(int, int);
1070 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1071
1072 void KillPlayer(struct PlayerInfo *);
1073 void BuryPlayer(struct PlayerInfo *);
1074 void RemovePlayer(struct PlayerInfo *);
1075
1076 static int getInvisibleActiveFromInvisibleElement(int);
1077 static int getInvisibleFromInvisibleActiveElement(int);
1078
1079 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1080
1081 /* for detection of endless loops, caused by custom element programming */
1082 /* (using maximal playfield width x 10 is just a rough approximation) */
1083 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1084
1085 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1086 {                                                                       \
1087   if (recursion_loop_detected)                                          \
1088     return (rc);                                                        \
1089                                                                         \
1090   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1091   {                                                                     \
1092     recursion_loop_detected = TRUE;                                     \
1093     recursion_loop_element = (e);                                       \
1094   }                                                                     \
1095                                                                         \
1096   recursion_loop_depth++;                                               \
1097 }
1098
1099 #define RECURSION_LOOP_DETECTION_END()                                  \
1100 {                                                                       \
1101   recursion_loop_depth--;                                               \
1102 }
1103
1104 static int recursion_loop_depth;
1105 static boolean recursion_loop_detected;
1106 static boolean recursion_loop_element;
1107
1108 static int map_player_action[MAX_PLAYERS];
1109
1110
1111 /* ------------------------------------------------------------------------- */
1112 /* definition of elements that automatically change to other elements after  */
1113 /* a specified time, eventually calling a function when changing             */
1114 /* ------------------------------------------------------------------------- */
1115
1116 /* forward declaration for changer functions */
1117 static void InitBuggyBase(int, int);
1118 static void WarnBuggyBase(int, int);
1119
1120 static void InitTrap(int, int);
1121 static void ActivateTrap(int, int);
1122 static void ChangeActiveTrap(int, int);
1123
1124 static void InitRobotWheel(int, int);
1125 static void RunRobotWheel(int, int);
1126 static void StopRobotWheel(int, int);
1127
1128 static void InitTimegateWheel(int, int);
1129 static void RunTimegateWheel(int, int);
1130
1131 static void InitMagicBallDelay(int, int);
1132 static void ActivateMagicBall(int, int);
1133
1134 struct ChangingElementInfo
1135 {
1136   int element;
1137   int target_element;
1138   int change_delay;
1139   void (*pre_change_function)(int x, int y);
1140   void (*change_function)(int x, int y);
1141   void (*post_change_function)(int x, int y);
1142 };
1143
1144 static struct ChangingElementInfo change_delay_list[] =
1145 {
1146   {
1147     EL_NUT_BREAKING,
1148     EL_EMERALD,
1149     6,
1150     NULL,
1151     NULL,
1152     NULL
1153   },
1154   {
1155     EL_PEARL_BREAKING,
1156     EL_EMPTY,
1157     8,
1158     NULL,
1159     NULL,
1160     NULL
1161   },
1162   {
1163     EL_EXIT_OPENING,
1164     EL_EXIT_OPEN,
1165     29,
1166     NULL,
1167     NULL,
1168     NULL
1169   },
1170   {
1171     EL_EXIT_CLOSING,
1172     EL_EXIT_CLOSED,
1173     29,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_STEEL_EXIT_OPENING,
1180     EL_STEEL_EXIT_OPEN,
1181     29,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_STEEL_EXIT_CLOSING,
1188     EL_STEEL_EXIT_CLOSED,
1189     29,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EM_EXIT_OPENING,
1196     EL_EM_EXIT_OPEN,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EM_EXIT_CLOSING,
1204     EL_EMPTY,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EM_STEEL_EXIT_OPENING,
1212     EL_EM_STEEL_EXIT_OPEN,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EM_STEEL_EXIT_CLOSING,
1220     EL_STEELWALL,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_SP_EXIT_OPENING,
1228     EL_SP_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_SP_EXIT_CLOSING,
1236     EL_SP_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_SWITCHGATE_OPENING,
1244     EL_SWITCHGATE_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_SWITCHGATE_CLOSING,
1252     EL_SWITCHGATE_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_TIMEGATE_OPENING,
1260     EL_TIMEGATE_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_TIMEGATE_CLOSING,
1268     EL_TIMEGATE_CLOSED,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274
1275   {
1276     EL_ACID_SPLASH_LEFT,
1277     EL_EMPTY,
1278     8,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_ACID_SPLASH_RIGHT,
1285     EL_EMPTY,
1286     8,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SP_BUGGY_BASE,
1293     EL_SP_BUGGY_BASE_ACTIVATING,
1294     0,
1295     InitBuggyBase,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_SP_BUGGY_BASE_ACTIVATING,
1301     EL_SP_BUGGY_BASE_ACTIVE,
1302     0,
1303     InitBuggyBase,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_SP_BUGGY_BASE_ACTIVE,
1309     EL_SP_BUGGY_BASE,
1310     0,
1311     InitBuggyBase,
1312     WarnBuggyBase,
1313     NULL
1314   },
1315   {
1316     EL_TRAP,
1317     EL_TRAP_ACTIVE,
1318     0,
1319     InitTrap,
1320     NULL,
1321     ActivateTrap
1322   },
1323   {
1324     EL_TRAP_ACTIVE,
1325     EL_TRAP,
1326     31,
1327     NULL,
1328     ChangeActiveTrap,
1329     NULL
1330   },
1331   {
1332     EL_ROBOT_WHEEL_ACTIVE,
1333     EL_ROBOT_WHEEL,
1334     0,
1335     InitRobotWheel,
1336     RunRobotWheel,
1337     StopRobotWheel
1338   },
1339   {
1340     EL_TIMEGATE_SWITCH_ACTIVE,
1341     EL_TIMEGATE_SWITCH,
1342     0,
1343     InitTimegateWheel,
1344     RunTimegateWheel,
1345     NULL
1346   },
1347   {
1348     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1349     EL_DC_TIMEGATE_SWITCH,
1350     0,
1351     InitTimegateWheel,
1352     RunTimegateWheel,
1353     NULL
1354   },
1355   {
1356     EL_EMC_MAGIC_BALL_ACTIVE,
1357     EL_EMC_MAGIC_BALL_ACTIVE,
1358     0,
1359     InitMagicBallDelay,
1360     NULL,
1361     ActivateMagicBall
1362   },
1363   {
1364     EL_EMC_SPRING_BUMPER_ACTIVE,
1365     EL_EMC_SPRING_BUMPER,
1366     8,
1367     NULL,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_DIAGONAL_SHRINKING,
1373     EL_UNDEFINED,
1374     0,
1375     NULL,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_DIAGONAL_GROWING,
1381     EL_UNDEFINED,
1382     0,
1383     NULL,
1384     NULL,
1385     NULL,
1386   },
1387
1388   {
1389     EL_UNDEFINED,
1390     EL_UNDEFINED,
1391     -1,
1392     NULL,
1393     NULL,
1394     NULL
1395   }
1396 };
1397
1398 struct
1399 {
1400   int element;
1401   int push_delay_fixed, push_delay_random;
1402 }
1403 push_delay_list[] =
1404 {
1405   { EL_SPRING,                  0, 0 },
1406   { EL_BALLOON,                 0, 0 },
1407
1408   { EL_SOKOBAN_OBJECT,          2, 0 },
1409   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1410   { EL_SATELLITE,               2, 0 },
1411   { EL_SP_DISK_YELLOW,          2, 0 },
1412
1413   { EL_UNDEFINED,               0, 0 },
1414 };
1415
1416 struct
1417 {
1418   int element;
1419   int move_stepsize;
1420 }
1421 move_stepsize_list[] =
1422 {
1423   { EL_AMOEBA_DROP,             2 },
1424   { EL_AMOEBA_DROPPING,         2 },
1425   { EL_QUICKSAND_FILLING,       1 },
1426   { EL_QUICKSAND_EMPTYING,      1 },
1427   { EL_QUICKSAND_FAST_FILLING,  2 },
1428   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1429   { EL_MAGIC_WALL_FILLING,      2 },
1430   { EL_MAGIC_WALL_EMPTYING,     2 },
1431   { EL_BD_MAGIC_WALL_FILLING,   2 },
1432   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1433   { EL_DC_MAGIC_WALL_FILLING,   2 },
1434   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1435
1436   { EL_UNDEFINED,               0 },
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int count;
1443 }
1444 collect_count_list[] =
1445 {
1446   { EL_EMERALD,                 1 },
1447   { EL_BD_DIAMOND,              1 },
1448   { EL_EMERALD_YELLOW,          1 },
1449   { EL_EMERALD_RED,             1 },
1450   { EL_EMERALD_PURPLE,          1 },
1451   { EL_DIAMOND,                 3 },
1452   { EL_SP_INFOTRON,             1 },
1453   { EL_PEARL,                   5 },
1454   { EL_CRYSTAL,                 8 },
1455
1456   { EL_UNDEFINED,               0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int direction;
1463 }
1464 access_direction_list[] =
1465 {
1466   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1468   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1469   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1470   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1471   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1472   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1473   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1474   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1475   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1476   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1477
1478   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1479   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1480   { EL_SP_PORT_UP,                                                   MV_DOWN },
1481   { EL_SP_PORT_DOWN,                                         MV_UP           },
1482   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1483   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1484   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1485   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1486   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1487   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1488   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1489   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1490   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1491   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1492   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1493   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1494   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1495   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1496   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1497
1498   { EL_UNDEFINED,                       MV_NONE                              }
1499 };
1500
1501 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1502
1503 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1504 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1505 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1506                                  IS_JUST_CHANGING(x, y))
1507
1508 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1509
1510 /* static variables for playfield scan mode (scanning forward or backward) */
1511 static int playfield_scan_start_x = 0;
1512 static int playfield_scan_start_y = 0;
1513 static int playfield_scan_delta_x = 1;
1514 static int playfield_scan_delta_y = 1;
1515
1516 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1517                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1518                                      (y) += playfield_scan_delta_y)     \
1519                                 for ((x) = playfield_scan_start_x;      \
1520                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1521                                      (x) += playfield_scan_delta_x)
1522
1523 #ifdef DEBUG
1524 void DEBUG_SetMaximumDynamite()
1525 {
1526   int i;
1527
1528   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1529     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1530       local_player->inventory_element[local_player->inventory_size++] =
1531         EL_DYNAMITE;
1532 }
1533 #endif
1534
1535 static void InitPlayfieldScanModeVars()
1536 {
1537   if (game.use_reverse_scan_direction)
1538   {
1539     playfield_scan_start_x = lev_fieldx - 1;
1540     playfield_scan_start_y = lev_fieldy - 1;
1541
1542     playfield_scan_delta_x = -1;
1543     playfield_scan_delta_y = -1;
1544   }
1545   else
1546   {
1547     playfield_scan_start_x = 0;
1548     playfield_scan_start_y = 0;
1549
1550     playfield_scan_delta_x = 1;
1551     playfield_scan_delta_y = 1;
1552   }
1553 }
1554
1555 static void InitPlayfieldScanMode(int mode)
1556 {
1557   game.use_reverse_scan_direction =
1558     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1559
1560   InitPlayfieldScanModeVars();
1561 }
1562
1563 static int get_move_delay_from_stepsize(int move_stepsize)
1564 {
1565   move_stepsize =
1566     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1567
1568   /* make sure that stepsize value is always a power of 2 */
1569   move_stepsize = (1 << log_2(move_stepsize));
1570
1571   return TILEX / move_stepsize;
1572 }
1573
1574 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1575                                boolean init_game)
1576 {
1577   int player_nr = player->index_nr;
1578   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1579   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1580
1581   /* do no immediately change move delay -- the player might just be moving */
1582   player->move_delay_value_next = move_delay;
1583
1584   /* information if player can move must be set separately */
1585   player->cannot_move = cannot_move;
1586
1587   if (init_game)
1588   {
1589     player->move_delay       = game.initial_move_delay[player_nr];
1590     player->move_delay_value = game.initial_move_delay_value[player_nr];
1591
1592     player->move_delay_value_next = -1;
1593
1594     player->move_delay_reset_counter = 0;
1595   }
1596 }
1597
1598 void GetPlayerConfig()
1599 {
1600   GameFrameDelay = setup.game_frame_delay;
1601
1602   if (!audio.sound_available)
1603     setup.sound_simple = FALSE;
1604
1605   if (!audio.loops_available)
1606     setup.sound_loops = FALSE;
1607
1608   if (!audio.music_available)
1609     setup.sound_music = FALSE;
1610
1611   if (!video.fullscreen_available)
1612     setup.fullscreen = FALSE;
1613
1614   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1615
1616   SetAudioMode(setup.sound);
1617   InitJoysticks();
1618 }
1619
1620 int GetElementFromGroupElement(int element)
1621 {
1622   if (IS_GROUP_ELEMENT(element))
1623   {
1624     struct ElementGroupInfo *group = element_info[element].group;
1625     int last_anim_random_frame = gfx.anim_random_frame;
1626     int element_pos;
1627
1628     if (group->choice_mode == ANIM_RANDOM)
1629       gfx.anim_random_frame = RND(group->num_elements_resolved);
1630
1631     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1632                                     group->choice_mode, 0,
1633                                     group->choice_pos);
1634
1635     if (group->choice_mode == ANIM_RANDOM)
1636       gfx.anim_random_frame = last_anim_random_frame;
1637
1638     group->choice_pos++;
1639
1640     element = group->element_resolved[element_pos];
1641   }
1642
1643   return element;
1644 }
1645
1646 static void InitPlayerField(int x, int y, int element, boolean init_game)
1647 {
1648   if (element == EL_SP_MURPHY)
1649   {
1650     if (init_game)
1651     {
1652       if (stored_player[0].present)
1653       {
1654         Feld[x][y] = EL_SP_MURPHY_CLONE;
1655
1656         return;
1657       }
1658       else
1659       {
1660         stored_player[0].initial_element = element;
1661         stored_player[0].use_murphy = TRUE;
1662
1663         if (!level.use_artwork_element[0])
1664           stored_player[0].artwork_element = EL_SP_MURPHY;
1665       }
1666
1667       Feld[x][y] = EL_PLAYER_1;
1668     }
1669   }
1670
1671   if (init_game)
1672   {
1673     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1674     int jx = player->jx, jy = player->jy;
1675
1676     player->present = TRUE;
1677
1678     player->block_last_field = (element == EL_SP_MURPHY ?
1679                                 level.sp_block_last_field :
1680                                 level.block_last_field);
1681
1682     /* ---------- initialize player's last field block delay --------------- */
1683
1684     /* always start with reliable default value (no adjustment needed) */
1685     player->block_delay_adjustment = 0;
1686
1687     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1688     if (player->block_last_field && element == EL_SP_MURPHY)
1689       player->block_delay_adjustment = 1;
1690
1691     /* special case 2: in game engines before 3.1.1, blocking was different */
1692     if (game.use_block_last_field_bug)
1693       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1694
1695     if (!options.network || player->connected)
1696     {
1697       player->active = TRUE;
1698
1699       /* remove potentially duplicate players */
1700       if (StorePlayer[jx][jy] == Feld[x][y])
1701         StorePlayer[jx][jy] = 0;
1702
1703       StorePlayer[x][y] = Feld[x][y];
1704
1705 #if DEBUG_INIT_PLAYER
1706       if (options.debug)
1707       {
1708         printf("- player element %d activated", player->element_nr);
1709         printf(" (local player is %d and currently %s)\n",
1710                local_player->element_nr,
1711                local_player->active ? "active" : "not active");
1712       }
1713     }
1714 #endif
1715
1716     Feld[x][y] = EL_EMPTY;
1717
1718     player->jx = player->last_jx = x;
1719     player->jy = player->last_jy = y;
1720   }
1721
1722   if (!init_game)
1723   {
1724     int player_nr = GET_PLAYER_NR(element);
1725     struct PlayerInfo *player = &stored_player[player_nr];
1726
1727     if (player->active && player->killed)
1728       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1729   }
1730 }
1731
1732 static void InitField(int x, int y, boolean init_game)
1733 {
1734   int element = Feld[x][y];
1735
1736   switch (element)
1737   {
1738     case EL_SP_MURPHY:
1739     case EL_PLAYER_1:
1740     case EL_PLAYER_2:
1741     case EL_PLAYER_3:
1742     case EL_PLAYER_4:
1743       InitPlayerField(x, y, element, init_game);
1744       break;
1745
1746     case EL_SOKOBAN_FIELD_PLAYER:
1747       element = Feld[x][y] = EL_PLAYER_1;
1748       InitField(x, y, init_game);
1749
1750       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1751       InitField(x, y, init_game);
1752       break;
1753
1754     case EL_SOKOBAN_FIELD_EMPTY:
1755       local_player->sokobanfields_still_needed++;
1756       break;
1757
1758     case EL_STONEBLOCK:
1759       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1760         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1761       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1765       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1766         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1767       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1768         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1769       break;
1770
1771     case EL_BUG:
1772     case EL_BUG_RIGHT:
1773     case EL_BUG_UP:
1774     case EL_BUG_LEFT:
1775     case EL_BUG_DOWN:
1776     case EL_SPACESHIP:
1777     case EL_SPACESHIP_RIGHT:
1778     case EL_SPACESHIP_UP:
1779     case EL_SPACESHIP_LEFT:
1780     case EL_SPACESHIP_DOWN:
1781     case EL_BD_BUTTERFLY:
1782     case EL_BD_BUTTERFLY_RIGHT:
1783     case EL_BD_BUTTERFLY_UP:
1784     case EL_BD_BUTTERFLY_LEFT:
1785     case EL_BD_BUTTERFLY_DOWN:
1786     case EL_BD_FIREFLY:
1787     case EL_BD_FIREFLY_RIGHT:
1788     case EL_BD_FIREFLY_UP:
1789     case EL_BD_FIREFLY_LEFT:
1790     case EL_BD_FIREFLY_DOWN:
1791     case EL_PACMAN_RIGHT:
1792     case EL_PACMAN_UP:
1793     case EL_PACMAN_LEFT:
1794     case EL_PACMAN_DOWN:
1795     case EL_YAMYAM:
1796     case EL_YAMYAM_LEFT:
1797     case EL_YAMYAM_RIGHT:
1798     case EL_YAMYAM_UP:
1799     case EL_YAMYAM_DOWN:
1800     case EL_DARK_YAMYAM:
1801     case EL_ROBOT:
1802     case EL_PACMAN:
1803     case EL_SP_SNIKSNAK:
1804     case EL_SP_ELECTRON:
1805     case EL_MOLE:
1806     case EL_MOLE_LEFT:
1807     case EL_MOLE_RIGHT:
1808     case EL_MOLE_UP:
1809     case EL_MOLE_DOWN:
1810       InitMovDir(x, y);
1811       break;
1812
1813     case EL_AMOEBA_FULL:
1814     case EL_BD_AMOEBA:
1815       InitAmoebaNr(x, y);
1816       break;
1817
1818     case EL_AMOEBA_DROP:
1819       if (y == lev_fieldy - 1)
1820       {
1821         Feld[x][y] = EL_AMOEBA_GROWING;
1822         Store[x][y] = EL_AMOEBA_WET;
1823       }
1824       break;
1825
1826     case EL_DYNAMITE_ACTIVE:
1827     case EL_SP_DISK_RED_ACTIVE:
1828     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1829     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1830     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1831     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1832       MovDelay[x][y] = 96;
1833       break;
1834
1835     case EL_EM_DYNAMITE_ACTIVE:
1836       MovDelay[x][y] = 32;
1837       break;
1838
1839     case EL_LAMP:
1840       local_player->lights_still_needed++;
1841       break;
1842
1843     case EL_PENGUIN:
1844       local_player->friends_still_needed++;
1845       break;
1846
1847     case EL_PIG:
1848     case EL_DRAGON:
1849       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1850       break;
1851
1852     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1853     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1854     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1855     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1856     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1857     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1858     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1859     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1860     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1861     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1862     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1863     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1864       if (init_game)
1865       {
1866         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1867         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1868         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1869
1870         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1871         {
1872           game.belt_dir[belt_nr] = belt_dir;
1873           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1874         }
1875         else    /* more than one switch -- set it like the first switch */
1876         {
1877           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1878         }
1879       }
1880       break;
1881
1882     case EL_LIGHT_SWITCH_ACTIVE:
1883       if (init_game)
1884         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1885       break;
1886
1887     case EL_INVISIBLE_STEELWALL:
1888     case EL_INVISIBLE_WALL:
1889     case EL_INVISIBLE_SAND:
1890       if (game.light_time_left > 0 ||
1891           game.lenses_time_left > 0)
1892         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1898       break;
1899
1900     case EL_EMC_MAGIC_BALL_SWITCH:
1901       if (game.ball_state)
1902         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1903       break;
1904
1905     case EL_TRIGGER_PLAYER:
1906     case EL_TRIGGER_ELEMENT:
1907     case EL_TRIGGER_CE_VALUE:
1908     case EL_TRIGGER_CE_SCORE:
1909     case EL_SELF:
1910     case EL_ANY_ELEMENT:
1911     case EL_CURRENT_CE_VALUE:
1912     case EL_CURRENT_CE_SCORE:
1913     case EL_PREV_CE_1:
1914     case EL_PREV_CE_2:
1915     case EL_PREV_CE_3:
1916     case EL_PREV_CE_4:
1917     case EL_PREV_CE_5:
1918     case EL_PREV_CE_6:
1919     case EL_PREV_CE_7:
1920     case EL_PREV_CE_8:
1921     case EL_NEXT_CE_1:
1922     case EL_NEXT_CE_2:
1923     case EL_NEXT_CE_3:
1924     case EL_NEXT_CE_4:
1925     case EL_NEXT_CE_5:
1926     case EL_NEXT_CE_6:
1927     case EL_NEXT_CE_7:
1928     case EL_NEXT_CE_8:
1929       /* reference elements should not be used on the playfield */
1930       Feld[x][y] = EL_EMPTY;
1931       break;
1932
1933     default:
1934       if (IS_CUSTOM_ELEMENT(element))
1935       {
1936         if (CAN_MOVE(element))
1937           InitMovDir(x, y);
1938
1939         if (!element_info[element].use_last_ce_value || init_game)
1940           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1941       }
1942       else if (IS_GROUP_ELEMENT(element))
1943       {
1944         Feld[x][y] = GetElementFromGroupElement(element);
1945
1946         InitField(x, y, init_game);
1947       }
1948
1949       break;
1950   }
1951
1952   if (!init_game)
1953     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1954 }
1955
1956 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1957 {
1958   InitField(x, y, init_game);
1959
1960   /* not needed to call InitMovDir() -- already done by InitField()! */
1961   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1962       CAN_MOVE(Feld[x][y]))
1963     InitMovDir(x, y);
1964 }
1965
1966 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1967 {
1968   int old_element = Feld[x][y];
1969
1970   InitField(x, y, init_game);
1971
1972   /* not needed to call InitMovDir() -- already done by InitField()! */
1973   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1974       CAN_MOVE(old_element) &&
1975       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1976     InitMovDir(x, y);
1977
1978   /* this case is in fact a combination of not less than three bugs:
1979      first, it calls InitMovDir() for elements that can move, although this is
1980      already done by InitField(); then, it checks the element that was at this
1981      field _before_ the call to InitField() (which can change it); lastly, it
1982      was not called for "mole with direction" elements, which were treated as
1983      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1984   */
1985 }
1986
1987 static int get_key_element_from_nr(int key_nr)
1988 {
1989   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1990                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1991                           EL_EM_KEY_1 : EL_KEY_1);
1992
1993   return key_base_element + key_nr;
1994 }
1995
1996 static int get_next_dropped_element(struct PlayerInfo *player)
1997 {
1998   return (player->inventory_size > 0 ?
1999           player->inventory_element[player->inventory_size - 1] :
2000           player->inventory_infinite_element != EL_UNDEFINED ?
2001           player->inventory_infinite_element :
2002           player->dynabombs_left > 0 ?
2003           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2004           EL_UNDEFINED);
2005 }
2006
2007 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2008 {
2009   /* pos >= 0: get element from bottom of the stack;
2010      pos <  0: get element from top of the stack */
2011
2012   if (pos < 0)
2013   {
2014     int min_inventory_size = -pos;
2015     int inventory_pos = player->inventory_size - min_inventory_size;
2016     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2017
2018     return (player->inventory_size >= min_inventory_size ?
2019             player->inventory_element[inventory_pos] :
2020             player->inventory_infinite_element != EL_UNDEFINED ?
2021             player->inventory_infinite_element :
2022             player->dynabombs_left >= min_dynabombs_left ?
2023             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2024             EL_UNDEFINED);
2025   }
2026   else
2027   {
2028     int min_dynabombs_left = pos + 1;
2029     int min_inventory_size = pos + 1 - player->dynabombs_left;
2030     int inventory_pos = pos - player->dynabombs_left;
2031
2032     return (player->inventory_infinite_element != EL_UNDEFINED ?
2033             player->inventory_infinite_element :
2034             player->dynabombs_left >= min_dynabombs_left ?
2035             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2036             player->inventory_size >= min_inventory_size ?
2037             player->inventory_element[inventory_pos] :
2038             EL_UNDEFINED);
2039   }
2040 }
2041
2042 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2043 {
2044   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2045   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2046   int compare_result;
2047
2048   if (gpo1->sort_priority != gpo2->sort_priority)
2049     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2050   else
2051     compare_result = gpo1->nr - gpo2->nr;
2052
2053   return compare_result;
2054 }
2055
2056 void InitGameControlValues()
2057 {
2058   int i;
2059
2060   for (i = 0; game_panel_controls[i].nr != -1; i++)
2061   {
2062     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2063     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2064     struct TextPosInfo *pos = gpc->pos;
2065     int nr = gpc->nr;
2066     int type = gpc->type;
2067
2068     if (nr != i)
2069     {
2070       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2071       Error(ERR_EXIT, "this should not happen -- please debug");
2072     }
2073
2074     /* force update of game controls after initialization */
2075     gpc->value = gpc->last_value = -1;
2076     gpc->frame = gpc->last_frame = -1;
2077     gpc->gfx_frame = -1;
2078
2079     /* determine panel value width for later calculation of alignment */
2080     if (type == TYPE_INTEGER || type == TYPE_STRING)
2081     {
2082       pos->width = pos->size * getFontWidth(pos->font);
2083       pos->height = getFontHeight(pos->font);
2084     }
2085     else if (type == TYPE_ELEMENT)
2086     {
2087       pos->width = pos->size;
2088       pos->height = pos->size;
2089     }
2090
2091     /* fill structure for game panel draw order */
2092     gpo->nr = gpc->nr;
2093     gpo->sort_priority = pos->sort_priority;
2094   }
2095
2096   /* sort game panel controls according to sort_priority and control number */
2097   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2098         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2099 }
2100
2101 void UpdatePlayfieldElementCount()
2102 {
2103   boolean use_element_count = FALSE;
2104   int i, j, x, y;
2105
2106   /* first check if it is needed at all to calculate playfield element count */
2107   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2108     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2109       use_element_count = TRUE;
2110
2111   if (!use_element_count)
2112     return;
2113
2114   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2115     element_info[i].element_count = 0;
2116
2117   SCAN_PLAYFIELD(x, y)
2118   {
2119     element_info[Feld[x][y]].element_count++;
2120   }
2121
2122   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2123     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2124       if (IS_IN_GROUP(j, i))
2125         element_info[EL_GROUP_START + i].element_count +=
2126           element_info[j].element_count;
2127 }
2128
2129 void UpdateGameControlValues()
2130 {
2131   int i, k;
2132   int time = (local_player->LevelSolved ?
2133               local_player->LevelSolved_CountingTime :
2134               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2135               level.native_em_level->lev->time :
2136               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2137               level.native_sp_level->game_sp->time_played :
2138               game.no_time_limit ? TimePlayed : TimeLeft);
2139   int score = (local_player->LevelSolved ?
2140                local_player->LevelSolved_CountingScore :
2141                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142                level.native_em_level->lev->score :
2143                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144                level.native_sp_level->game_sp->score :
2145                local_player->score);
2146   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147               level.native_em_level->lev->required :
2148               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149               level.native_sp_level->game_sp->infotrons_still_needed :
2150               local_player->gems_still_needed);
2151   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2152                      level.native_em_level->lev->required > 0 :
2153                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2154                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2155                      local_player->gems_still_needed > 0 ||
2156                      local_player->sokobanfields_still_needed > 0 ||
2157                      local_player->lights_still_needed > 0);
2158
2159   UpdatePlayfieldElementCount();
2160
2161   /* update game panel control values */
2162
2163   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2164   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2165
2166   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2167   for (i = 0; i < MAX_NUM_KEYS; i++)
2168     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2169   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2170   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2171
2172   if (game.centered_player_nr == -1)
2173   {
2174     for (i = 0; i < MAX_PLAYERS; i++)
2175     {
2176       /* only one player in Supaplex game engine */
2177       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2178         break;
2179
2180       for (k = 0; k < MAX_NUM_KEYS; k++)
2181       {
2182         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2183         {
2184           if (level.native_em_level->ply[i]->keys & (1 << k))
2185             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186               get_key_element_from_nr(k);
2187         }
2188         else if (stored_player[i].key[k])
2189           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2190             get_key_element_from_nr(k);
2191       }
2192
2193       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2194         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2195           level.native_em_level->ply[i]->dynamite;
2196       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2197         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2198           level.native_sp_level->game_sp->red_disk_count;
2199       else
2200         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2201           stored_player[i].inventory_size;
2202
2203       if (stored_player[i].num_white_keys > 0)
2204         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2205           EL_DC_KEY_WHITE;
2206
2207       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2208         stored_player[i].num_white_keys;
2209     }
2210   }
2211   else
2212   {
2213     int player_nr = game.centered_player_nr;
2214
2215     for (k = 0; k < MAX_NUM_KEYS; k++)
2216     {
2217       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2218       {
2219         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2220           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221             get_key_element_from_nr(k);
2222       }
2223       else if (stored_player[player_nr].key[k])
2224         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2225           get_key_element_from_nr(k);
2226     }
2227
2228     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2229       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2230         level.native_em_level->ply[player_nr]->dynamite;
2231     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2232       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2233         level.native_sp_level->game_sp->red_disk_count;
2234     else
2235       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236         stored_player[player_nr].inventory_size;
2237
2238     if (stored_player[player_nr].num_white_keys > 0)
2239       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2240
2241     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242       stored_player[player_nr].num_white_keys;
2243   }
2244
2245   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2246   {
2247     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248       get_inventory_element_from_pos(local_player, i);
2249     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250       get_inventory_element_from_pos(local_player, -i - 1);
2251   }
2252
2253   game_panel_controls[GAME_PANEL_SCORE].value = score;
2254   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2255
2256   game_panel_controls[GAME_PANEL_TIME].value = time;
2257
2258   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2261
2262   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2263
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268     local_player->shield_normal_time_left;
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2271      EL_EMPTY);
2272   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273     local_player->shield_deadly_time_left;
2274
2275   game_panel_controls[GAME_PANEL_EXIT].value =
2276     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2277
2278   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282      EL_EMC_MAGIC_BALL_SWITCH);
2283
2284   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287     game.light_time_left;
2288
2289   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292     game.timegate_time_left;
2293
2294   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2296
2297   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300     game.lenses_time_left;
2301
2302   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305     game.magnify_time_left;
2306
2307   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2309      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2311      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2312      EL_BALLOON_SWITCH_NONE);
2313
2314   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315     local_player->dynabomb_count;
2316   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317     local_player->dynabomb_size;
2318   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2320
2321   game_panel_controls[GAME_PANEL_PENGUINS].value =
2322     local_player->friends_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325     local_player->sokobanfields_still_needed;
2326   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327     local_player->sokobanfields_still_needed;
2328
2329   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2331
2332   for (i = 0; i < NUM_BELTS; i++)
2333   {
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2339   }
2340
2341   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344     game.magic_wall_time_left;
2345
2346   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347     local_player->gravity;
2348
2349   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355        game.panel.element[i].id : EL_UNDEFINED);
2356
2357   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360        element_info[game.panel.element_count[i].id].element_count : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365        element_info[game.panel.ce_score[i].id].collect_score : 0);
2366
2367   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370        element_info[game.panel.ce_score_element[i].id].collect_score :
2371        EL_UNDEFINED);
2372
2373   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2376
2377   /* update game panel control frames */
2378
2379   for (i = 0; game_panel_controls[i].nr != -1; i++)
2380   {
2381     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2382
2383     if (gpc->type == TYPE_ELEMENT)
2384     {
2385       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2386       {
2387         int last_anim_random_frame = gfx.anim_random_frame;
2388         int element = gpc->value;
2389         int graphic = el2panelimg(element);
2390
2391         if (gpc->value != gpc->last_value)
2392         {
2393           gpc->gfx_frame = 0;
2394           gpc->gfx_random = INIT_GFX_RANDOM();
2395         }
2396         else
2397         {
2398           gpc->gfx_frame++;
2399
2400           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402             gpc->gfx_random = INIT_GFX_RANDOM();
2403         }
2404
2405         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406           gfx.anim_random_frame = gpc->gfx_random;
2407
2408         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409           gpc->gfx_frame = element_info[element].collect_score;
2410
2411         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2412                                               gpc->gfx_frame);
2413
2414         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415           gfx.anim_random_frame = last_anim_random_frame;
2416       }
2417     }
2418   }
2419 }
2420
2421 void DisplayGameControlValues()
2422 {
2423   boolean redraw_panel = FALSE;
2424   int i;
2425
2426   for (i = 0; game_panel_controls[i].nr != -1; i++)
2427   {
2428     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2429
2430     if (PANEL_DEACTIVATED(gpc->pos))
2431       continue;
2432
2433     if (gpc->value == gpc->last_value &&
2434         gpc->frame == gpc->last_frame)
2435       continue;
2436
2437     redraw_panel = TRUE;
2438   }
2439
2440   if (!redraw_panel)
2441     return;
2442
2443   /* copy default game door content to main double buffer */
2444
2445   /* !!! CHECK AGAIN !!! */
2446   SetPanelBackground();
2447   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2449
2450   /* redraw game control buttons */
2451   RedrawGameButtons();
2452
2453   game_status = GAME_MODE_PSEUDO_PANEL;
2454
2455   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2456   {
2457     int nr = game_panel_order[i].nr;
2458     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459     struct TextPosInfo *pos = gpc->pos;
2460     int type = gpc->type;
2461     int value = gpc->value;
2462     int frame = gpc->frame;
2463     int size = pos->size;
2464     int font = pos->font;
2465     boolean draw_masked = pos->draw_masked;
2466     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2467
2468     if (PANEL_DEACTIVATED(pos))
2469       continue;
2470
2471     gpc->last_value = value;
2472     gpc->last_frame = frame;
2473
2474     if (type == TYPE_INTEGER)
2475     {
2476       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477           nr == GAME_PANEL_TIME)
2478       {
2479         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2480
2481         if (use_dynamic_size)           /* use dynamic number of digits */
2482         {
2483           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485           int size2 = size1 + 1;
2486           int font1 = pos->font;
2487           int font2 = pos->font_alt;
2488
2489           size = (value < value_change ? size1 : size2);
2490           font = (value < value_change ? font1 : font2);
2491         }
2492       }
2493
2494       /* correct text size if "digits" is zero or less */
2495       if (size <= 0)
2496         size = strlen(int2str(value, size));
2497
2498       /* dynamically correct text alignment */
2499       pos->width = size * getFontWidth(font);
2500
2501       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502                   int2str(value, size), font, mask_mode);
2503     }
2504     else if (type == TYPE_ELEMENT)
2505     {
2506       int element, graphic;
2507       Bitmap *src_bitmap;
2508       int src_x, src_y;
2509       int width, height;
2510       int dst_x = PANEL_XPOS(pos);
2511       int dst_y = PANEL_YPOS(pos);
2512
2513       if (value != EL_UNDEFINED && value != EL_EMPTY)
2514       {
2515         element = value;
2516         graphic = el2panelimg(value);
2517
2518         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2519
2520         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2521           size = TILESIZE;
2522
2523         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2524                               &src_x, &src_y);
2525
2526         width  = graphic_info[graphic].width  * size / TILESIZE;
2527         height = graphic_info[graphic].height * size / TILESIZE;
2528
2529         if (draw_masked)
2530           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2531                            dst_x, dst_y);
2532         else
2533           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2534                      dst_x, dst_y);
2535       }
2536     }
2537     else if (type == TYPE_STRING)
2538     {
2539       boolean active = (value != 0);
2540       char *state_normal = "off";
2541       char *state_active = "on";
2542       char *state = (active ? state_active : state_normal);
2543       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2545                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2546                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2547
2548       if (nr == GAME_PANEL_GRAVITY_STATE)
2549       {
2550         int font1 = pos->font;          /* (used for normal state) */
2551         int font2 = pos->font_alt;      /* (used for active state) */
2552
2553         font = (active ? font2 : font1);
2554       }
2555
2556       if (s != NULL)
2557       {
2558         char *s_cut;
2559
2560         if (size <= 0)
2561         {
2562           /* don't truncate output if "chars" is zero or less */
2563           size = strlen(s);
2564
2565           /* dynamically correct text alignment */
2566           pos->width = size * getFontWidth(font);
2567         }
2568
2569         s_cut = getStringCopyN(s, size);
2570
2571         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572                     s_cut, font, mask_mode);
2573
2574         free(s_cut);
2575       }
2576     }
2577
2578     redraw_mask |= REDRAW_DOOR_1;
2579   }
2580
2581   game_status = GAME_MODE_PLAYING;
2582 }
2583
2584 void UpdateAndDisplayGameControlValues()
2585 {
2586   if (tape.deactivate_display)
2587     return;
2588
2589   UpdateGameControlValues();
2590   DisplayGameControlValues();
2591 }
2592
2593 void UpdateGameDoorValues()
2594 {
2595   UpdateGameControlValues();
2596 }
2597
2598 void DrawGameDoorValues()
2599 {
2600   DisplayGameControlValues();
2601 }
2602
2603
2604 /*
2605   =============================================================================
2606   InitGameEngine()
2607   -----------------------------------------------------------------------------
2608   initialize game engine due to level / tape version number
2609   =============================================================================
2610 */
2611
2612 static void InitGameEngine()
2613 {
2614   int i, j, k, l, x, y;
2615
2616   /* set game engine from tape file when re-playing, else from level file */
2617   game.engine_version = (tape.playing ? tape.engine_version :
2618                          level.game_version);
2619
2620   /* set single or multi-player game mode (needed for re-playing tapes) */
2621   game.team_mode = setup.team_mode;
2622
2623   if (tape.playing)
2624   {
2625     int num_players = 0;
2626
2627     for (i = 0; i < MAX_PLAYERS; i++)
2628       if (tape.player_participates[i])
2629         num_players++;
2630
2631     /* multi-player tapes contain input data for more than one player */
2632     game.team_mode = (num_players > 1);
2633   }
2634
2635   /* ---------------------------------------------------------------------- */
2636   /* set flags for bugs and changes according to active game engine version */
2637   /* ---------------------------------------------------------------------- */
2638
2639   /*
2640     Summary of bugfix/change:
2641     Fixed handling for custom elements that change when pushed by the player.
2642
2643     Fixed/changed in version:
2644     3.1.0
2645
2646     Description:
2647     Before 3.1.0, custom elements that "change when pushing" changed directly
2648     after the player started pushing them (until then handled in "DigField()").
2649     Since 3.1.0, these custom elements are not changed until the "pushing"
2650     move of the element is finished (now handled in "ContinueMoving()").
2651
2652     Affected levels/tapes:
2653     The first condition is generally needed for all levels/tapes before version
2654     3.1.0, which might use the old behaviour before it was changed; known tapes
2655     that are affected are some tapes from the level set "Walpurgis Gardens" by
2656     Jamie Cullen.
2657     The second condition is an exception from the above case and is needed for
2658     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659     above (including some development versions of 3.1.0), but before it was
2660     known that this change would break tapes like the above and was fixed in
2661     3.1.1, so that the changed behaviour was active although the engine version
2662     while recording maybe was before 3.1.0. There is at least one tape that is
2663     affected by this exception, which is the tape for the one-level set "Bug
2664     Machine" by Juergen Bonhagen.
2665   */
2666
2667   game.use_change_when_pushing_bug =
2668     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2669      !(tape.playing &&
2670        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2672
2673   /*
2674     Summary of bugfix/change:
2675     Fixed handling for blocking the field the player leaves when moving.
2676
2677     Fixed/changed in version:
2678     3.1.1
2679
2680     Description:
2681     Before 3.1.1, when "block last field when moving" was enabled, the field
2682     the player is leaving when moving was blocked for the time of the move,
2683     and was directly unblocked afterwards. This resulted in the last field
2684     being blocked for exactly one less than the number of frames of one player
2685     move. Additionally, even when blocking was disabled, the last field was
2686     blocked for exactly one frame.
2687     Since 3.1.1, due to changes in player movement handling, the last field
2688     is not blocked at all when blocking is disabled. When blocking is enabled,
2689     the last field is blocked for exactly the number of frames of one player
2690     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691     last field is blocked for exactly one more than the number of frames of
2692     one player move.
2693
2694     Affected levels/tapes:
2695     (!!! yet to be determined -- probably many !!!)
2696   */
2697
2698   game.use_block_last_field_bug =
2699     (game.engine_version < VERSION_IDENT(3,1,1,0));
2700
2701   /* ---------------------------------------------------------------------- */
2702
2703   /* set maximal allowed number of custom element changes per game frame */
2704   game.max_num_changes_per_frame = 1;
2705
2706   /* default scan direction: scan playfield from top/left to bottom/right */
2707   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2708
2709   /* dynamically adjust element properties according to game engine version */
2710   InitElementPropertiesEngine(game.engine_version);
2711
2712 #if 0
2713   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2714   printf("          tape version == %06d [%s] [file: %06d]\n",
2715          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2716          tape.file_version);
2717   printf("       => game.engine_version == %06d\n", game.engine_version);
2718 #endif
2719
2720   /* ---------- initialize player's initial move delay --------------------- */
2721
2722   /* dynamically adjust player properties according to level information */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay_value[i] =
2725       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2726
2727   /* dynamically adjust player properties according to game engine version */
2728   for (i = 0; i < MAX_PLAYERS; i++)
2729     game.initial_move_delay[i] =
2730       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2731        game.initial_move_delay_value[i] : 0);
2732
2733   /* ---------- initialize player's initial push delay --------------------- */
2734
2735   /* dynamically adjust player properties according to game engine version */
2736   game.initial_push_delay_value =
2737     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2738
2739   /* ---------- initialize changing elements ------------------------------- */
2740
2741   /* initialize changing elements information */
2742   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2743   {
2744     struct ElementInfo *ei = &element_info[i];
2745
2746     /* this pointer might have been changed in the level editor */
2747     ei->change = &ei->change_page[0];
2748
2749     if (!IS_CUSTOM_ELEMENT(i))
2750     {
2751       ei->change->target_element = EL_EMPTY_SPACE;
2752       ei->change->delay_fixed = 0;
2753       ei->change->delay_random = 0;
2754       ei->change->delay_frames = 1;
2755     }
2756
2757     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2758     {
2759       ei->has_change_event[j] = FALSE;
2760
2761       ei->event_page_nr[j] = 0;
2762       ei->event_page[j] = &ei->change_page[0];
2763     }
2764   }
2765
2766   /* add changing elements from pre-defined list */
2767   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2768   {
2769     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2770     struct ElementInfo *ei = &element_info[ch_delay->element];
2771
2772     ei->change->target_element       = ch_delay->target_element;
2773     ei->change->delay_fixed          = ch_delay->change_delay;
2774
2775     ei->change->pre_change_function  = ch_delay->pre_change_function;
2776     ei->change->change_function      = ch_delay->change_function;
2777     ei->change->post_change_function = ch_delay->post_change_function;
2778
2779     ei->change->can_change = TRUE;
2780     ei->change->can_change_or_has_action = TRUE;
2781
2782     ei->has_change_event[CE_DELAY] = TRUE;
2783
2784     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2785     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2786   }
2787
2788   /* ---------- initialize internal run-time variables --------------------- */
2789
2790   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2791   {
2792     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2793
2794     for (j = 0; j < ei->num_change_pages; j++)
2795     {
2796       ei->change_page[j].can_change_or_has_action =
2797         (ei->change_page[j].can_change |
2798          ei->change_page[j].has_action);
2799     }
2800   }
2801
2802   /* add change events from custom element configuration */
2803   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2804   {
2805     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2806
2807     for (j = 0; j < ei->num_change_pages; j++)
2808     {
2809       if (!ei->change_page[j].can_change_or_has_action)
2810         continue;
2811
2812       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2813       {
2814         /* only add event page for the first page found with this event */
2815         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2816         {
2817           ei->has_change_event[k] = TRUE;
2818
2819           ei->event_page_nr[k] = j;
2820           ei->event_page[k] = &ei->change_page[j];
2821         }
2822       }
2823     }
2824   }
2825
2826   /* ---------- initialize reference elements in change conditions --------- */
2827
2828   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2829   {
2830     int element = EL_CUSTOM_START + i;
2831     struct ElementInfo *ei = &element_info[element];
2832
2833     for (j = 0; j < ei->num_change_pages; j++)
2834     {
2835       int trigger_element = ei->change_page[j].initial_trigger_element;
2836
2837       if (trigger_element >= EL_PREV_CE_8 &&
2838           trigger_element <= EL_NEXT_CE_8)
2839         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2840
2841       ei->change_page[j].trigger_element = trigger_element;
2842     }
2843   }
2844
2845   /* ---------- initialize run-time trigger player and element ------------- */
2846
2847   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2848   {
2849     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2850
2851     for (j = 0; j < ei->num_change_pages; j++)
2852     {
2853       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2854       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2855       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2856       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2857       ei->change_page[j].actual_trigger_ce_value = 0;
2858       ei->change_page[j].actual_trigger_ce_score = 0;
2859     }
2860   }
2861
2862   /* ---------- initialize trigger events ---------------------------------- */
2863
2864   /* initialize trigger events information */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2867       trigger_events[i][j] = FALSE;
2868
2869   /* add trigger events from element change event properties */
2870   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2871   {
2872     struct ElementInfo *ei = &element_info[i];
2873
2874     for (j = 0; j < ei->num_change_pages; j++)
2875     {
2876       if (!ei->change_page[j].can_change_or_has_action)
2877         continue;
2878
2879       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2880       {
2881         int trigger_element = ei->change_page[j].trigger_element;
2882
2883         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2884         {
2885           if (ei->change_page[j].has_event[k])
2886           {
2887             if (IS_GROUP_ELEMENT(trigger_element))
2888             {
2889               struct ElementGroupInfo *group =
2890                 element_info[trigger_element].group;
2891
2892               for (l = 0; l < group->num_elements_resolved; l++)
2893                 trigger_events[group->element_resolved[l]][k] = TRUE;
2894             }
2895             else if (trigger_element == EL_ANY_ELEMENT)
2896               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2897                 trigger_events[l][k] = TRUE;
2898             else
2899               trigger_events[trigger_element][k] = TRUE;
2900           }
2901         }
2902       }
2903     }
2904   }
2905
2906   /* ---------- initialize push delay -------------------------------------- */
2907
2908   /* initialize push delay values to default */
2909   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2910   {
2911     if (!IS_CUSTOM_ELEMENT(i))
2912     {
2913       /* set default push delay values (corrected since version 3.0.7-1) */
2914       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2915       {
2916         element_info[i].push_delay_fixed = 2;
2917         element_info[i].push_delay_random = 8;
2918       }
2919       else
2920       {
2921         element_info[i].push_delay_fixed = 8;
2922         element_info[i].push_delay_random = 8;
2923       }
2924     }
2925   }
2926
2927   /* set push delay value for certain elements from pre-defined list */
2928   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2929   {
2930     int e = push_delay_list[i].element;
2931
2932     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2933     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2934   }
2935
2936   /* set push delay value for Supaplex elements for newer engine versions */
2937   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2938   {
2939     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2940     {
2941       if (IS_SP_ELEMENT(i))
2942       {
2943         /* set SP push delay to just enough to push under a falling zonk */
2944         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2945
2946         element_info[i].push_delay_fixed  = delay;
2947         element_info[i].push_delay_random = 0;
2948       }
2949     }
2950   }
2951
2952   /* ---------- initialize move stepsize ----------------------------------- */
2953
2954   /* initialize move stepsize values to default */
2955   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2956     if (!IS_CUSTOM_ELEMENT(i))
2957       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2958
2959   /* set move stepsize value for certain elements from pre-defined list */
2960   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2961   {
2962     int e = move_stepsize_list[i].element;
2963
2964     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2965   }
2966
2967   /* ---------- initialize collect score ----------------------------------- */
2968
2969   /* initialize collect score values for custom elements from initial value */
2970   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2971     if (IS_CUSTOM_ELEMENT(i))
2972       element_info[i].collect_score = element_info[i].collect_score_initial;
2973
2974   /* ---------- initialize collect count ----------------------------------- */
2975
2976   /* initialize collect count values for non-custom elements */
2977   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2978     if (!IS_CUSTOM_ELEMENT(i))
2979       element_info[i].collect_count_initial = 0;
2980
2981   /* add collect count values for all elements from pre-defined list */
2982   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2983     element_info[collect_count_list[i].element].collect_count_initial =
2984       collect_count_list[i].count;
2985
2986   /* ---------- initialize access direction -------------------------------- */
2987
2988   /* initialize access direction values to default (access from every side) */
2989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2990     if (!IS_CUSTOM_ELEMENT(i))
2991       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2992
2993   /* set access direction value for certain elements from pre-defined list */
2994   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2995     element_info[access_direction_list[i].element].access_direction =
2996       access_direction_list[i].direction;
2997
2998   /* ---------- initialize explosion content ------------------------------- */
2999   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3000   {
3001     if (IS_CUSTOM_ELEMENT(i))
3002       continue;
3003
3004     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3005     {
3006       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3007
3008       element_info[i].content.e[x][y] =
3009         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3010          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3011          i == EL_PLAYER_3 ? EL_EMERALD :
3012          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3013          i == EL_MOLE ? EL_EMERALD_RED :
3014          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3015          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3016          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3017          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3018          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3019          i == EL_WALL_EMERALD ? EL_EMERALD :
3020          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3021          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3022          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3023          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3024          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3025          i == EL_WALL_PEARL ? EL_PEARL :
3026          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3027          EL_EMPTY);
3028     }
3029   }
3030
3031   /* ---------- initialize recursion detection ------------------------------ */
3032   recursion_loop_depth = 0;
3033   recursion_loop_detected = FALSE;
3034   recursion_loop_element = EL_UNDEFINED;
3035
3036   /* ---------- initialize graphics engine ---------------------------------- */
3037   game.scroll_delay_value =
3038     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3039      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3040   game.scroll_delay_value =
3041     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3042
3043   /* ---------- initialize game engine snapshots ---------------------------- */
3044   for (i = 0; i < MAX_PLAYERS; i++)
3045     game.snapshot.last_action[i] = 0;
3046   game.snapshot.changed_action = FALSE;
3047   game.snapshot.mode =
3048     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3049      SNAPSHOT_MODE_EVERY_STEP :
3050      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3051      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3052 }
3053
3054 int get_num_special_action(int element, int action_first, int action_last)
3055 {
3056   int num_special_action = 0;
3057   int i, j;
3058
3059   for (i = action_first; i <= action_last; i++)
3060   {
3061     boolean found = FALSE;
3062
3063     for (j = 0; j < NUM_DIRECTIONS; j++)
3064       if (el_act_dir2img(element, i, j) !=
3065           el_act_dir2img(element, ACTION_DEFAULT, j))
3066         found = TRUE;
3067
3068     if (found)
3069       num_special_action++;
3070     else
3071       break;
3072   }
3073
3074   return num_special_action;
3075 }
3076
3077
3078 /*
3079   =============================================================================
3080   InitGame()
3081   -----------------------------------------------------------------------------
3082   initialize and start new game
3083   =============================================================================
3084 */
3085
3086 void InitGame()
3087 {
3088   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3089   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3090   int fade_mask = REDRAW_FIELD;
3091
3092   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3093   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3094   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3095   int initial_move_dir = MV_DOWN;
3096   int i, j, x, y;
3097
3098 #if 1
3099   printf("::: game.graphics_engine_version == %d\n",
3100          game.graphics_engine_version);
3101 #endif
3102
3103   // required here to update video display before fading (FIX THIS)
3104   DrawMaskedBorder(REDRAW_DOOR_2);
3105
3106   game_status = GAME_MODE_PLAYING;
3107
3108   if (!game.restart_level)
3109     CloseDoor(DOOR_CLOSE_1);
3110
3111   /* needed if different viewport properties defined for playing */
3112   ChangeViewportPropertiesIfNeeded();
3113
3114   if (level_editor_test_game)
3115     FadeSkipNextFadeIn();
3116   else
3117     FadeSetEnterScreen();
3118
3119   if (CheckIfGlobalBorderHasChanged())
3120     fade_mask = REDRAW_ALL;
3121
3122   FadeOut(fade_mask);
3123
3124   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3125
3126   ClearField();
3127
3128   DrawCompleteVideoDisplay();
3129
3130   InitGameEngine();
3131   InitGameControlValues();
3132
3133   /* don't play tapes over network */
3134   network_playing = (options.network && !tape.playing);
3135
3136   for (i = 0; i < MAX_PLAYERS; i++)
3137   {
3138     struct PlayerInfo *player = &stored_player[i];
3139
3140     player->index_nr = i;
3141     player->index_bit = (1 << i);
3142     player->element_nr = EL_PLAYER_1 + i;
3143
3144     player->present = FALSE;
3145     player->active = FALSE;
3146     player->mapped = FALSE;
3147
3148     player->killed = FALSE;
3149     player->reanimated = FALSE;
3150
3151     player->action = 0;
3152     player->effective_action = 0;
3153     player->programmed_action = 0;
3154
3155     player->score = 0;
3156     player->score_final = 0;
3157
3158     player->gems_still_needed = level.gems_needed;
3159     player->sokobanfields_still_needed = 0;
3160     player->lights_still_needed = 0;
3161     player->friends_still_needed = 0;
3162
3163     for (j = 0; j < MAX_NUM_KEYS; j++)
3164       player->key[j] = FALSE;
3165
3166     player->num_white_keys = 0;
3167
3168     player->dynabomb_count = 0;
3169     player->dynabomb_size = 1;
3170     player->dynabombs_left = 0;
3171     player->dynabomb_xl = FALSE;
3172
3173     player->MovDir = initial_move_dir;
3174     player->MovPos = 0;
3175     player->GfxPos = 0;
3176     player->GfxDir = initial_move_dir;
3177     player->GfxAction = ACTION_DEFAULT;
3178     player->Frame = 0;
3179     player->StepFrame = 0;
3180
3181     player->initial_element = player->element_nr;
3182     player->artwork_element =
3183       (level.use_artwork_element[i] ? level.artwork_element[i] :
3184        player->element_nr);
3185     player->use_murphy = FALSE;
3186
3187     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3188     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3189
3190     player->gravity = level.initial_player_gravity[i];
3191
3192     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3193
3194     player->actual_frame_counter = 0;
3195
3196     player->step_counter = 0;
3197
3198     player->last_move_dir = initial_move_dir;
3199
3200     player->is_active = FALSE;
3201
3202     player->is_waiting = FALSE;
3203     player->is_moving = FALSE;
3204     player->is_auto_moving = FALSE;
3205     player->is_digging = FALSE;
3206     player->is_snapping = FALSE;
3207     player->is_collecting = FALSE;
3208     player->is_pushing = FALSE;
3209     player->is_switching = FALSE;
3210     player->is_dropping = FALSE;
3211     player->is_dropping_pressed = FALSE;
3212
3213     player->is_bored = FALSE;
3214     player->is_sleeping = FALSE;
3215
3216     player->frame_counter_bored = -1;
3217     player->frame_counter_sleeping = -1;
3218
3219     player->anim_delay_counter = 0;
3220     player->post_delay_counter = 0;
3221
3222     player->dir_waiting = initial_move_dir;
3223     player->action_waiting = ACTION_DEFAULT;
3224     player->last_action_waiting = ACTION_DEFAULT;
3225     player->special_action_bored = ACTION_DEFAULT;
3226     player->special_action_sleeping = ACTION_DEFAULT;
3227
3228     player->switch_x = -1;
3229     player->switch_y = -1;
3230
3231     player->drop_x = -1;
3232     player->drop_y = -1;
3233
3234     player->show_envelope = 0;
3235
3236     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3237
3238     player->push_delay       = -1;      /* initialized when pushing starts */
3239     player->push_delay_value = game.initial_push_delay_value;
3240
3241     player->drop_delay = 0;
3242     player->drop_pressed_delay = 0;
3243
3244     player->last_jx = -1;
3245     player->last_jy = -1;
3246     player->jx = -1;
3247     player->jy = -1;
3248
3249     player->shield_normal_time_left = 0;
3250     player->shield_deadly_time_left = 0;
3251
3252     player->inventory_infinite_element = EL_UNDEFINED;
3253     player->inventory_size = 0;
3254
3255     if (level.use_initial_inventory[i])
3256     {
3257       for (j = 0; j < level.initial_inventory_size[i]; j++)
3258       {
3259         int element = level.initial_inventory_content[i][j];
3260         int collect_count = element_info[element].collect_count_initial;
3261         int k;
3262
3263         if (!IS_CUSTOM_ELEMENT(element))
3264           collect_count = 1;
3265
3266         if (collect_count == 0)
3267           player->inventory_infinite_element = element;
3268         else
3269           for (k = 0; k < collect_count; k++)
3270             if (player->inventory_size < MAX_INVENTORY_SIZE)
3271               player->inventory_element[player->inventory_size++] = element;
3272       }
3273     }
3274
3275     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3276     SnapField(player, 0, 0);
3277
3278     player->LevelSolved = FALSE;
3279     player->GameOver = FALSE;
3280
3281     player->LevelSolved_GameWon = FALSE;
3282     player->LevelSolved_GameEnd = FALSE;
3283     player->LevelSolved_PanelOff = FALSE;
3284     player->LevelSolved_SaveTape = FALSE;
3285     player->LevelSolved_SaveScore = FALSE;
3286     player->LevelSolved_CountingTime = 0;
3287     player->LevelSolved_CountingScore = 0;
3288
3289     map_player_action[i] = i;
3290   }
3291
3292   network_player_action_received = FALSE;
3293
3294 #if defined(NETWORK_AVALIABLE)
3295   /* initial null action */
3296   if (network_playing)
3297     SendToServer_MovePlayer(MV_NONE);
3298 #endif
3299
3300   ZX = ZY = -1;
3301   ExitX = ExitY = -1;
3302
3303   FrameCounter = 0;
3304   TimeFrames = 0;
3305   TimePlayed = 0;
3306   TimeLeft = level.time;
3307   TapeTime = 0;
3308
3309   ScreenMovDir = MV_NONE;
3310   ScreenMovPos = 0;
3311   ScreenGfxPos = 0;
3312
3313   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3314
3315   AllPlayersGone = FALSE;
3316
3317   game.no_time_limit = (level.time == 0);
3318
3319   game.yamyam_content_nr = 0;
3320   game.robot_wheel_active = FALSE;
3321   game.magic_wall_active = FALSE;
3322   game.magic_wall_time_left = 0;
3323   game.light_time_left = 0;
3324   game.timegate_time_left = 0;
3325   game.switchgate_pos = 0;
3326   game.wind_direction = level.wind_direction_initial;
3327
3328   game.lenses_time_left = 0;
3329   game.magnify_time_left = 0;
3330
3331   game.ball_state = level.ball_state_initial;
3332   game.ball_content_nr = 0;
3333
3334   game.envelope_active = FALSE;
3335
3336   /* set focus to local player for network games, else to all players */
3337   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3338   game.centered_player_nr_next = game.centered_player_nr;
3339   game.set_centered_player = FALSE;
3340
3341   if (network_playing && tape.recording)
3342   {
3343     /* store client dependent player focus when recording network games */
3344     tape.centered_player_nr_next = game.centered_player_nr_next;
3345     tape.set_centered_player = TRUE;
3346   }
3347
3348   for (i = 0; i < NUM_BELTS; i++)
3349   {
3350     game.belt_dir[i] = MV_NONE;
3351     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3352   }
3353
3354   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3355     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3356
3357 #if DEBUG_INIT_PLAYER
3358   if (options.debug)
3359   {
3360     printf("Player status at level initialization:\n");
3361   }
3362 #endif
3363
3364   SCAN_PLAYFIELD(x, y)
3365   {
3366     Feld[x][y] = level.field[x][y];
3367     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3368     ChangeDelay[x][y] = 0;
3369     ChangePage[x][y] = -1;
3370     CustomValue[x][y] = 0;              /* initialized in InitField() */
3371     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3372     AmoebaNr[x][y] = 0;
3373     WasJustMoving[x][y] = 0;
3374     WasJustFalling[x][y] = 0;
3375     CheckCollision[x][y] = 0;
3376     CheckImpact[x][y] = 0;
3377     Stop[x][y] = FALSE;
3378     Pushed[x][y] = FALSE;
3379
3380     ChangeCount[x][y] = 0;
3381     ChangeEvent[x][y] = -1;
3382
3383     ExplodePhase[x][y] = 0;
3384     ExplodeDelay[x][y] = 0;
3385     ExplodeField[x][y] = EX_TYPE_NONE;
3386
3387     RunnerVisit[x][y] = 0;
3388     PlayerVisit[x][y] = 0;
3389
3390     GfxFrame[x][y] = 0;
3391     GfxRandom[x][y] = INIT_GFX_RANDOM();
3392     GfxElement[x][y] = EL_UNDEFINED;
3393     GfxAction[x][y] = ACTION_DEFAULT;
3394     GfxDir[x][y] = MV_NONE;
3395     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3396   }
3397
3398 #if 1
3399   printf("::: INIT GAME");
3400 #endif
3401
3402   SCAN_PLAYFIELD(x, y)
3403   {
3404     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3405       emulate_bd = FALSE;
3406     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3407       emulate_sb = FALSE;
3408     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3409       emulate_sp = FALSE;
3410
3411     InitField(x, y, TRUE);
3412
3413     ResetGfxAnimation(x, y);
3414   }
3415
3416 #if 1
3417   printf(" -> %d\n", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
3418 #endif
3419
3420   InitBeltMovement();
3421
3422   for (i = 0; i < MAX_PLAYERS; i++)
3423   {
3424     struct PlayerInfo *player = &stored_player[i];
3425
3426     /* set number of special actions for bored and sleeping animation */
3427     player->num_special_action_bored =
3428       get_num_special_action(player->artwork_element,
3429                              ACTION_BORING_1, ACTION_BORING_LAST);
3430     player->num_special_action_sleeping =
3431       get_num_special_action(player->artwork_element,
3432                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3433   }
3434
3435   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3436                     emulate_sb ? EMU_SOKOBAN :
3437                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3438
3439   /* initialize type of slippery elements */
3440   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3441   {
3442     if (!IS_CUSTOM_ELEMENT(i))
3443     {
3444       /* default: elements slip down either to the left or right randomly */
3445       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3446
3447       /* SP style elements prefer to slip down on the left side */
3448       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3449         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3450
3451       /* BD style elements prefer to slip down on the left side */
3452       if (game.emulation == EMU_BOULDERDASH)
3453         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3454     }
3455   }
3456
3457   /* initialize explosion and ignition delay */
3458   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3459   {
3460     if (!IS_CUSTOM_ELEMENT(i))
3461     {
3462       int num_phase = 8;
3463       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3464                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3465                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3466       int last_phase = (num_phase + 1) * delay;
3467       int half_phase = (num_phase / 2) * delay;
3468
3469       element_info[i].explosion_delay = last_phase - 1;
3470       element_info[i].ignition_delay = half_phase;
3471
3472       if (i == EL_BLACK_ORB)
3473         element_info[i].ignition_delay = 1;
3474     }
3475   }
3476
3477   /* correct non-moving belts to start moving left */
3478   for (i = 0; i < NUM_BELTS; i++)
3479     if (game.belt_dir[i] == MV_NONE)
3480       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3481
3482 #if USE_NEW_PLAYER_ASSIGNMENTS
3483   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3484   /* choose default local player */
3485   local_player = &stored_player[0];
3486
3487   for (i = 0; i < MAX_PLAYERS; i++)
3488     stored_player[i].connected = FALSE;
3489
3490   local_player->connected = TRUE;
3491   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3492
3493   if (tape.playing)
3494   {
3495     for (i = 0; i < MAX_PLAYERS; i++)
3496       stored_player[i].connected = tape.player_participates[i];
3497   }
3498   else if (game.team_mode && !options.network)
3499   {
3500     /* try to guess locally connected team mode players (needed for correct
3501        assignment of player figures from level to locally playing players) */
3502
3503     for (i = 0; i < MAX_PLAYERS; i++)
3504       if (setup.input[i].use_joystick ||
3505           setup.input[i].key.left != KSYM_UNDEFINED)
3506         stored_player[i].connected = TRUE;
3507   }
3508
3509 #if DEBUG_INIT_PLAYER
3510   if (options.debug)
3511   {
3512     printf("Player status after level initialization:\n");
3513
3514     for (i = 0; i < MAX_PLAYERS; i++)
3515     {
3516       struct PlayerInfo *player = &stored_player[i];
3517
3518       printf("- player %d: present == %d, connected == %d, active == %d",
3519              i + 1,
3520              player->present,
3521              player->connected,
3522              player->active);
3523
3524       if (local_player == player)
3525         printf(" (local player)");
3526
3527       printf("\n");
3528     }
3529   }
3530 #endif
3531
3532 #if DEBUG_INIT_PLAYER
3533   if (options.debug)
3534     printf("Reassigning players ...\n");
3535 #endif
3536
3537   /* check if any connected player was not found in playfield */
3538   for (i = 0; i < MAX_PLAYERS; i++)
3539   {
3540     struct PlayerInfo *player = &stored_player[i];
3541
3542     if (player->connected && !player->present)
3543     {
3544       struct PlayerInfo *field_player = NULL;
3545
3546 #if DEBUG_INIT_PLAYER
3547       if (options.debug)
3548         printf("- looking for field player for player %d ...\n", i + 1);
3549 #endif
3550
3551       /* assign first free player found that is present in the playfield */
3552
3553       /* first try: look for unmapped playfield player that is not connected */
3554       for (j = 0; j < MAX_PLAYERS; j++)
3555         if (field_player == NULL &&
3556             stored_player[j].present &&
3557             !stored_player[j].mapped &&
3558             !stored_player[j].connected)
3559           field_player = &stored_player[j];
3560
3561       /* second try: look for *any* unmapped playfield player */
3562       for (j = 0; j < MAX_PLAYERS; j++)
3563         if (field_player == NULL &&
3564             stored_player[j].present &&
3565             !stored_player[j].mapped)
3566           field_player = &stored_player[j];
3567
3568       if (field_player != NULL)
3569       {
3570         int jx = field_player->jx, jy = field_player->jy;
3571
3572 #if DEBUG_INIT_PLAYER
3573         if (options.debug)
3574           printf("- found player %d\n", field_player->index_nr + 1);
3575 #endif
3576
3577         player->present = FALSE;
3578         player->active = FALSE;
3579
3580         field_player->present = TRUE;
3581         field_player->active = TRUE;
3582
3583         /*
3584         player->initial_element = field_player->initial_element;
3585         player->artwork_element = field_player->artwork_element;
3586
3587         player->block_last_field       = field_player->block_last_field;
3588         player->block_delay_adjustment = field_player->block_delay_adjustment;
3589         */
3590
3591         StorePlayer[jx][jy] = field_player->element_nr;
3592
3593         field_player->jx = field_player->last_jx = jx;
3594         field_player->jy = field_player->last_jy = jy;
3595
3596         if (local_player == player)
3597           local_player = field_player;
3598
3599         map_player_action[field_player->index_nr] = i;
3600
3601         field_player->mapped = TRUE;
3602
3603 #if DEBUG_INIT_PLAYER
3604         if (options.debug)
3605           printf("- map_player_action[%d] == %d\n",
3606                  field_player->index_nr + 1, i + 1);
3607 #endif
3608       }
3609     }
3610
3611     if (player->connected && player->present)
3612       player->mapped = TRUE;
3613   }
3614
3615 #if DEBUG_INIT_PLAYER
3616   if (options.debug)
3617   {
3618     printf("Player status after player assignment (first stage):\n");
3619
3620     for (i = 0; i < MAX_PLAYERS; i++)
3621     {
3622       struct PlayerInfo *player = &stored_player[i];
3623
3624       printf("- player %d: present == %d, connected == %d, active == %d",
3625              i + 1,
3626              player->present,
3627              player->connected,
3628              player->active);
3629
3630       if (local_player == player)
3631         printf(" (local player)");
3632
3633       printf("\n");
3634     }
3635   }
3636 #endif
3637
3638 #else
3639
3640   /* check if any connected player was not found in playfield */
3641   for (i = 0; i < MAX_PLAYERS; i++)
3642   {
3643     struct PlayerInfo *player = &stored_player[i];
3644
3645     if (player->connected && !player->present)
3646     {
3647       for (j = 0; j < MAX_PLAYERS; j++)
3648       {
3649         struct PlayerInfo *field_player = &stored_player[j];
3650         int jx = field_player->jx, jy = field_player->jy;
3651
3652         /* assign first free player found that is present in the playfield */
3653         if (field_player->present && !field_player->connected)
3654         {
3655           player->present = TRUE;
3656           player->active = TRUE;
3657
3658           field_player->present = FALSE;
3659           field_player->active = FALSE;
3660
3661           player->initial_element = field_player->initial_element;
3662           player->artwork_element = field_player->artwork_element;
3663
3664           player->block_last_field       = field_player->block_last_field;
3665           player->block_delay_adjustment = field_player->block_delay_adjustment;
3666
3667           StorePlayer[jx][jy] = player->element_nr;
3668
3669           player->jx = player->last_jx = jx;
3670           player->jy = player->last_jy = jy;
3671
3672           break;
3673         }
3674       }
3675     }
3676   }
3677 #endif
3678
3679 #if 0
3680   printf("::: local_player->present == %d\n", local_player->present);
3681 #endif
3682
3683   if (tape.playing)
3684   {
3685     /* when playing a tape, eliminate all players who do not participate */
3686
3687 #if USE_NEW_PLAYER_ASSIGNMENTS
3688
3689     if (!game.team_mode)
3690     {
3691       for (i = 0; i < MAX_PLAYERS; i++)
3692       {
3693         if (stored_player[i].active &&
3694             !tape.player_participates[map_player_action[i]])
3695         {
3696           struct PlayerInfo *player = &stored_player[i];
3697           int jx = player->jx, jy = player->jy;
3698
3699 #if DEBUG_INIT_PLAYER
3700           if (options.debug)
3701             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3702 #endif
3703
3704           player->active = FALSE;
3705           StorePlayer[jx][jy] = 0;
3706           Feld[jx][jy] = EL_EMPTY;
3707         }
3708       }
3709     }
3710
3711 #else
3712
3713     for (i = 0; i < MAX_PLAYERS; i++)
3714     {
3715       if (stored_player[i].active &&
3716           !tape.player_participates[i])
3717       {
3718         struct PlayerInfo *player = &stored_player[i];
3719         int jx = player->jx, jy = player->jy;
3720
3721         player->active = FALSE;
3722         StorePlayer[jx][jy] = 0;
3723         Feld[jx][jy] = EL_EMPTY;
3724       }
3725     }
3726 #endif
3727   }
3728   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3729   {
3730     /* when in single player mode, eliminate all but the first active player */
3731
3732     for (i = 0; i < MAX_PLAYERS; i++)
3733     {
3734       if (stored_player[i].active)
3735       {
3736         for (j = i + 1; j < MAX_PLAYERS; j++)
3737         {
3738           if (stored_player[j].active)
3739           {
3740             struct PlayerInfo *player = &stored_player[j];
3741             int jx = player->jx, jy = player->jy;
3742
3743             player->active = FALSE;
3744             player->present = FALSE;
3745
3746             StorePlayer[jx][jy] = 0;
3747             Feld[jx][jy] = EL_EMPTY;
3748           }
3749         }
3750       }
3751     }
3752   }
3753
3754   /* when recording the game, store which players take part in the game */
3755   if (tape.recording)
3756   {
3757 #if USE_NEW_PLAYER_ASSIGNMENTS
3758     for (i = 0; i < MAX_PLAYERS; i++)
3759       if (stored_player[i].connected)
3760         tape.player_participates[i] = TRUE;
3761 #else
3762     for (i = 0; i < MAX_PLAYERS; i++)
3763       if (stored_player[i].active)
3764         tape.player_participates[i] = TRUE;
3765 #endif
3766   }
3767
3768 #if DEBUG_INIT_PLAYER
3769   if (options.debug)
3770   {
3771     printf("Player status after player assignment (final stage):\n");
3772
3773     for (i = 0; i < MAX_PLAYERS; i++)
3774     {
3775       struct PlayerInfo *player = &stored_player[i];
3776
3777       printf("- player %d: present == %d, connected == %d, active == %d",
3778              i + 1,
3779              player->present,
3780              player->connected,
3781              player->active);
3782
3783       if (local_player == player)
3784         printf(" (local player)");
3785
3786       printf("\n");
3787     }
3788   }
3789 #endif
3790
3791   if (BorderElement == EL_EMPTY)
3792   {
3793     SBX_Left = 0;
3794     SBX_Right = lev_fieldx - SCR_FIELDX;
3795     SBY_Upper = 0;
3796     SBY_Lower = lev_fieldy - SCR_FIELDY;
3797   }
3798   else
3799   {
3800     SBX_Left = -1;
3801     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3802     SBY_Upper = -1;
3803     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3804   }
3805
3806   if (full_lev_fieldx <= SCR_FIELDX)
3807     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3808   if (full_lev_fieldy <= SCR_FIELDY)
3809     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3810
3811   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3812     SBX_Left--;
3813   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3814     SBY_Upper--;
3815
3816   /* if local player not found, look for custom element that might create
3817      the player (make some assumptions about the right custom element) */
3818   if (!local_player->present)
3819   {
3820     int start_x = 0, start_y = 0;
3821     int found_rating = 0;
3822     int found_element = EL_UNDEFINED;
3823     int player_nr = local_player->index_nr;
3824
3825     SCAN_PLAYFIELD(x, y)
3826     {
3827       int element = Feld[x][y];
3828       int content;
3829       int xx, yy;
3830       boolean is_player;
3831
3832       if (level.use_start_element[player_nr] &&
3833           level.start_element[player_nr] == element &&
3834           found_rating < 4)
3835       {
3836         start_x = x;
3837         start_y = y;
3838
3839         found_rating = 4;
3840         found_element = element;
3841       }
3842
3843       if (!IS_CUSTOM_ELEMENT(element))
3844         continue;
3845
3846       if (CAN_CHANGE(element))
3847       {
3848         for (i = 0; i < element_info[element].num_change_pages; i++)
3849         {
3850           /* check for player created from custom element as single target */
3851           content = element_info[element].change_page[i].target_element;
3852           is_player = ELEM_IS_PLAYER(content);
3853
3854           if (is_player && (found_rating < 3 ||
3855                             (found_rating == 3 && element < found_element)))
3856           {
3857             start_x = x;
3858             start_y = y;
3859
3860             found_rating = 3;
3861             found_element = element;
3862           }
3863         }
3864       }
3865
3866       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3867       {
3868         /* check for player created from custom element as explosion content */
3869         content = element_info[element].content.e[xx][yy];
3870         is_player = ELEM_IS_PLAYER(content);
3871
3872         if (is_player && (found_rating < 2 ||
3873                           (found_rating == 2 && element < found_element)))
3874         {
3875           start_x = x + xx - 1;
3876           start_y = y + yy - 1;
3877
3878           found_rating = 2;
3879           found_element = element;
3880         }
3881
3882         if (!CAN_CHANGE(element))
3883           continue;
3884
3885         for (i = 0; i < element_info[element].num_change_pages; i++)
3886         {
3887           /* check for player created from custom element as extended target */
3888           content =
3889             element_info[element].change_page[i].target_content.e[xx][yy];
3890
3891           is_player = ELEM_IS_PLAYER(content);
3892
3893           if (is_player && (found_rating < 1 ||
3894                             (found_rating == 1 && element < found_element)))
3895           {
3896             start_x = x + xx - 1;
3897             start_y = y + yy - 1;
3898
3899             found_rating = 1;
3900             found_element = element;
3901           }
3902         }
3903       }
3904     }
3905
3906     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3907                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3908                 start_x - MIDPOSX);
3909
3910     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3911                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3912                 start_y - MIDPOSY);
3913   }
3914   else
3915   {
3916     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3917                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3918                 local_player->jx - MIDPOSX);
3919
3920     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3921                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3922                 local_player->jy - MIDPOSY);
3923   }
3924
3925   /* !!! FIX THIS (START) !!! */
3926   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3927   {
3928     InitGameEngine_EM();
3929   }
3930   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3931   {
3932     InitGameEngine_SP();
3933   }
3934   else
3935   {
3936     DrawLevel(REDRAW_FIELD);
3937     DrawAllPlayers();
3938
3939     /* after drawing the level, correct some elements */
3940     if (game.timegate_time_left == 0)
3941       CloseAllOpenTimegates();
3942   }
3943
3944   /* blit playfield from scroll buffer to normal back buffer for fading in */
3945   BlitScreenToBitmap(backbuffer);
3946   /* !!! FIX THIS (END) !!! */
3947
3948   DrawMaskedBorder(fade_mask);
3949
3950   FadeIn(fade_mask);
3951
3952 #if 1
3953   // full screen redraw is required at this point in the following cases:
3954   // - special editor door undrawn when game was started from level editor
3955   // - drawing area (playfield) was changed and has to be removed completely
3956   redraw_mask = REDRAW_ALL;
3957   BackToFront();
3958 #endif
3959
3960   if (!game.restart_level)
3961   {
3962     /* copy default game door content to main double buffer */
3963
3964     /* !!! CHECK AGAIN !!! */
3965     SetPanelBackground();
3966     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3967     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3968   }
3969
3970   SetPanelBackground();
3971   SetDrawBackgroundMask(REDRAW_DOOR_1);
3972
3973   UpdateAndDisplayGameControlValues();
3974
3975   if (!game.restart_level)
3976   {
3977     UnmapGameButtons();
3978     UnmapTapeButtons();
3979
3980     FreeGameButtons();
3981     CreateGameButtons();
3982
3983     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3984     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3985     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3986
3987     MapGameButtons();
3988     MapTapeButtons();
3989
3990     /* copy actual game door content to door double buffer for OpenDoor() */
3991     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3992
3993     OpenDoor(DOOR_OPEN_ALL);
3994
3995     PlaySound(SND_GAME_STARTING);
3996
3997     if (setup.sound_music)
3998       PlayLevelMusic();
3999
4000     KeyboardAutoRepeatOffUnlessAutoplay();
4001
4002 #if DEBUG_INIT_PLAYER
4003     if (options.debug)
4004     {
4005       printf("Player status (final):\n");
4006
4007       for (i = 0; i < MAX_PLAYERS; i++)
4008       {
4009         struct PlayerInfo *player = &stored_player[i];
4010
4011         printf("- player %d: present == %d, connected == %d, active == %d",
4012                i + 1,
4013                player->present,
4014                player->connected,
4015                player->active);
4016
4017         if (local_player == player)
4018           printf(" (local player)");
4019
4020         printf("\n");
4021       }
4022     }
4023 #endif
4024   }
4025
4026   UnmapAllGadgets();
4027
4028   MapGameButtons();
4029   MapTapeButtons();
4030
4031   if (!game.restart_level && !tape.playing)
4032   {
4033     LevelStats_incPlayed(level_nr);
4034
4035     SaveLevelSetup_SeriesInfo();
4036   }
4037
4038   game.restart_level = FALSE;
4039
4040   SaveEngineSnapshotToListInitial();
4041 }
4042
4043 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4044 {
4045   /* this is used for non-R'n'D game engines to update certain engine values */
4046
4047   /* needed to determine if sounds are played within the visible screen area */
4048   scroll_x = actual_scroll_x;
4049   scroll_y = actual_scroll_y;
4050 }
4051
4052 void InitMovDir(int x, int y)
4053 {
4054   int i, element = Feld[x][y];
4055   static int xy[4][2] =
4056   {
4057     {  0, +1 },
4058     { +1,  0 },
4059     {  0, -1 },
4060     { -1,  0 }
4061   };
4062   static int direction[3][4] =
4063   {
4064     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4065     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4066     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4067   };
4068
4069   switch (element)
4070   {
4071     case EL_BUG_RIGHT:
4072     case EL_BUG_UP:
4073     case EL_BUG_LEFT:
4074     case EL_BUG_DOWN:
4075       Feld[x][y] = EL_BUG;
4076       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4077       break;
4078
4079     case EL_SPACESHIP_RIGHT:
4080     case EL_SPACESHIP_UP:
4081     case EL_SPACESHIP_LEFT:
4082     case EL_SPACESHIP_DOWN:
4083       Feld[x][y] = EL_SPACESHIP;
4084       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4085       break;
4086
4087     case EL_BD_BUTTERFLY_RIGHT:
4088     case EL_BD_BUTTERFLY_UP:
4089     case EL_BD_BUTTERFLY_LEFT:
4090     case EL_BD_BUTTERFLY_DOWN:
4091       Feld[x][y] = EL_BD_BUTTERFLY;
4092       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4093       break;
4094
4095     case EL_BD_FIREFLY_RIGHT:
4096     case EL_BD_FIREFLY_UP:
4097     case EL_BD_FIREFLY_LEFT:
4098     case EL_BD_FIREFLY_DOWN:
4099       Feld[x][y] = EL_BD_FIREFLY;
4100       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4101       break;
4102
4103     case EL_PACMAN_RIGHT:
4104     case EL_PACMAN_UP:
4105     case EL_PACMAN_LEFT:
4106     case EL_PACMAN_DOWN:
4107       Feld[x][y] = EL_PACMAN;
4108       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4109       break;
4110
4111     case EL_YAMYAM_LEFT:
4112     case EL_YAMYAM_RIGHT:
4113     case EL_YAMYAM_UP:
4114     case EL_YAMYAM_DOWN:
4115       Feld[x][y] = EL_YAMYAM;
4116       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4117       break;
4118
4119     case EL_SP_SNIKSNAK:
4120       MovDir[x][y] = MV_UP;
4121       break;
4122
4123     case EL_SP_ELECTRON:
4124       MovDir[x][y] = MV_LEFT;
4125       break;
4126
4127     case EL_MOLE_LEFT:
4128     case EL_MOLE_RIGHT:
4129     case EL_MOLE_UP:
4130     case EL_MOLE_DOWN:
4131       Feld[x][y] = EL_MOLE;
4132       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4133       break;
4134
4135     default:
4136       if (IS_CUSTOM_ELEMENT(element))
4137       {
4138         struct ElementInfo *ei = &element_info[element];
4139         int move_direction_initial = ei->move_direction_initial;
4140         int move_pattern = ei->move_pattern;
4141
4142         if (move_direction_initial == MV_START_PREVIOUS)
4143         {
4144           if (MovDir[x][y] != MV_NONE)
4145             return;
4146
4147           move_direction_initial = MV_START_AUTOMATIC;
4148         }
4149
4150         if (move_direction_initial == MV_START_RANDOM)
4151           MovDir[x][y] = 1 << RND(4);
4152         else if (move_direction_initial & MV_ANY_DIRECTION)
4153           MovDir[x][y] = move_direction_initial;
4154         else if (move_pattern == MV_ALL_DIRECTIONS ||
4155                  move_pattern == MV_TURNING_LEFT ||
4156                  move_pattern == MV_TURNING_RIGHT ||
4157                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4158                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4159                  move_pattern == MV_TURNING_RANDOM)
4160           MovDir[x][y] = 1 << RND(4);
4161         else if (move_pattern == MV_HORIZONTAL)
4162           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4163         else if (move_pattern == MV_VERTICAL)
4164           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4165         else if (move_pattern & MV_ANY_DIRECTION)
4166           MovDir[x][y] = element_info[element].move_pattern;
4167         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4168                  move_pattern == MV_ALONG_RIGHT_SIDE)
4169         {
4170           /* use random direction as default start direction */
4171           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4172             MovDir[x][y] = 1 << RND(4);
4173
4174           for (i = 0; i < NUM_DIRECTIONS; i++)
4175           {
4176             int x1 = x + xy[i][0];
4177             int y1 = y + xy[i][1];
4178
4179             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4180             {
4181               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4182                 MovDir[x][y] = direction[0][i];
4183               else
4184                 MovDir[x][y] = direction[1][i];
4185
4186               break;
4187             }
4188           }
4189         }                
4190       }
4191       else
4192       {
4193         MovDir[x][y] = 1 << RND(4);
4194
4195         if (element != EL_BUG &&
4196             element != EL_SPACESHIP &&
4197             element != EL_BD_BUTTERFLY &&
4198             element != EL_BD_FIREFLY)
4199           break;
4200
4201         for (i = 0; i < NUM_DIRECTIONS; i++)
4202         {
4203           int x1 = x + xy[i][0];
4204           int y1 = y + xy[i][1];
4205
4206           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4207           {
4208             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4209             {
4210               MovDir[x][y] = direction[0][i];
4211               break;
4212             }
4213             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4214                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4215             {
4216               MovDir[x][y] = direction[1][i];
4217               break;
4218             }
4219           }
4220         }
4221       }
4222       break;
4223   }
4224
4225   GfxDir[x][y] = MovDir[x][y];
4226 }
4227
4228 void InitAmoebaNr(int x, int y)
4229 {
4230   int i;
4231   int group_nr = AmoebeNachbarNr(x, y);
4232
4233   if (group_nr == 0)
4234   {
4235     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4236     {
4237       if (AmoebaCnt[i] == 0)
4238       {
4239         group_nr = i;
4240         break;
4241       }
4242     }
4243   }
4244
4245   AmoebaNr[x][y] = group_nr;
4246   AmoebaCnt[group_nr]++;
4247   AmoebaCnt2[group_nr]++;
4248 }
4249
4250 static void PlayerWins(struct PlayerInfo *player)
4251 {
4252   player->LevelSolved = TRUE;
4253   player->GameOver = TRUE;
4254
4255   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4256                          level.native_em_level->lev->score : player->score);
4257
4258   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4259                                       TimeLeft);
4260   player->LevelSolved_CountingScore = player->score_final;
4261 }
4262
4263 void GameWon()
4264 {
4265   static int time, time_final;
4266   static int score, score_final;
4267   static int game_over_delay_1 = 0;
4268   static int game_over_delay_2 = 0;
4269   int game_over_delay_value_1 = 50;
4270   int game_over_delay_value_2 = 50;
4271
4272   if (!local_player->LevelSolved_GameWon)
4273   {
4274     int i;
4275
4276     /* do not start end game actions before the player stops moving (to exit) */
4277     if (local_player->MovPos)
4278       return;
4279
4280     local_player->LevelSolved_GameWon = TRUE;
4281     local_player->LevelSolved_SaveTape = tape.recording;
4282     local_player->LevelSolved_SaveScore = !tape.playing;
4283
4284     if (!tape.playing)
4285     {
4286       LevelStats_incSolved(level_nr);
4287
4288       SaveLevelSetup_SeriesInfo();
4289     }
4290
4291     if (tape.auto_play)         /* tape might already be stopped here */
4292       tape.auto_play_level_solved = TRUE;
4293
4294     TapeStop();
4295
4296     game_over_delay_1 = game_over_delay_value_1;
4297     game_over_delay_2 = game_over_delay_value_2;
4298
4299     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4300     score = score_final = local_player->score_final;
4301
4302     if (TimeLeft > 0)
4303     {
4304       time_final = 0;
4305       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4306     }
4307     else if (game.no_time_limit && TimePlayed < 999)
4308     {
4309       time_final = 999;
4310       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4311     }
4312
4313     local_player->score_final = score_final;
4314
4315     if (level_editor_test_game)
4316     {
4317       time = time_final;
4318       score = score_final;
4319
4320       local_player->LevelSolved_CountingTime = time;
4321       local_player->LevelSolved_CountingScore = score;
4322
4323       game_panel_controls[GAME_PANEL_TIME].value = time;
4324       game_panel_controls[GAME_PANEL_SCORE].value = score;
4325
4326       DisplayGameControlValues();
4327     }
4328
4329     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4330     {
4331       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4332       {
4333         /* close exit door after last player */
4334         if ((AllPlayersGone &&
4335              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4336               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4337               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4338             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4339             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4340         {
4341           int element = Feld[ExitX][ExitY];
4342
4343           Feld[ExitX][ExitY] =
4344             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4345              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4346              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4347              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4348              EL_EM_STEEL_EXIT_CLOSING);
4349
4350           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4351         }
4352
4353         /* player disappears */
4354         DrawLevelField(ExitX, ExitY);
4355       }
4356
4357       for (i = 0; i < MAX_PLAYERS; i++)
4358       {
4359         struct PlayerInfo *player = &stored_player[i];
4360
4361         if (player->present)
4362         {
4363           RemovePlayer(player);
4364
4365           /* player disappears */
4366           DrawLevelField(player->jx, player->jy);
4367         }
4368       }
4369     }
4370
4371     PlaySound(SND_GAME_WINNING);
4372   }
4373
4374   if (game_over_delay_1 > 0)
4375   {
4376     game_over_delay_1--;
4377
4378     return;
4379   }
4380
4381   if (time != time_final)
4382   {
4383     int time_to_go = ABS(time_final - time);
4384     int time_count_dir = (time < time_final ? +1 : -1);
4385     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4386
4387     time  += time_count_steps * time_count_dir;
4388     score += time_count_steps * level.score[SC_TIME_BONUS];
4389
4390     local_player->LevelSolved_CountingTime = time;
4391     local_player->LevelSolved_CountingScore = score;
4392
4393     game_panel_controls[GAME_PANEL_TIME].value = time;
4394     game_panel_controls[GAME_PANEL_SCORE].value = score;
4395
4396     DisplayGameControlValues();
4397
4398     if (time == time_final)
4399       StopSound(SND_GAME_LEVELTIME_BONUS);
4400     else if (setup.sound_loops)
4401       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4402     else
4403       PlaySound(SND_GAME_LEVELTIME_BONUS);
4404
4405     return;
4406   }
4407
4408   local_player->LevelSolved_PanelOff = TRUE;
4409
4410   if (game_over_delay_2 > 0)
4411   {
4412     game_over_delay_2--;
4413
4414     return;
4415   }
4416
4417   GameEnd();
4418 }
4419
4420 void GameEnd()
4421 {
4422   int hi_pos;
4423   boolean raise_level = FALSE;
4424
4425   local_player->LevelSolved_GameEnd = TRUE;
4426
4427   if (!global.use_envelope_request)
4428     CloseDoor(DOOR_CLOSE_1);
4429
4430   if (local_player->LevelSolved_SaveTape)
4431   {
4432     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4433   }
4434
4435   CloseDoor(DOOR_CLOSE_ALL);
4436
4437   if (level_editor_test_game)
4438   {
4439     game_status = GAME_MODE_MAIN;
4440
4441     DrawMainMenu();
4442
4443     return;
4444   }
4445
4446   if (!local_player->LevelSolved_SaveScore)
4447   {
4448     FadeOut(REDRAW_FIELD);
4449
4450     game_status = GAME_MODE_MAIN;
4451
4452     DrawMainMenu();
4453
4454     return;
4455   }
4456
4457   if (level_nr == leveldir_current->handicap_level)
4458   {
4459     leveldir_current->handicap_level++;
4460
4461     SaveLevelSetup_SeriesInfo();
4462   }
4463
4464   if (level_nr < leveldir_current->last_level)
4465     raise_level = TRUE;                 /* advance to next level */
4466
4467   if ((hi_pos = NewHiScore()) >= 0) 
4468   {
4469     game_status = GAME_MODE_SCORES;
4470
4471     DrawHallOfFame(hi_pos);
4472
4473     if (raise_level)
4474     {
4475       level_nr++;
4476       TapeErase();
4477     }
4478   }
4479   else
4480   {
4481     FadeOut(REDRAW_FIELD);
4482
4483     game_status = GAME_MODE_MAIN;
4484
4485     if (raise_level)
4486     {
4487       level_nr++;
4488       TapeErase();
4489     }
4490
4491     DrawMainMenu();
4492   }
4493 }
4494
4495 int NewHiScore()
4496 {
4497   int k, l;
4498   int position = -1;
4499
4500   LoadScore(level_nr);
4501
4502   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4503       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4504     return -1;
4505
4506   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4507   {
4508     if (local_player->score_final > highscore[k].Score)
4509     {
4510       /* player has made it to the hall of fame */
4511
4512       if (k < MAX_SCORE_ENTRIES - 1)
4513       {
4514         int m = MAX_SCORE_ENTRIES - 1;
4515
4516 #ifdef ONE_PER_NAME
4517         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4518           if (strEqual(setup.player_name, highscore[l].Name))
4519             m = l;
4520         if (m == k)     /* player's new highscore overwrites his old one */
4521           goto put_into_list;
4522 #endif
4523
4524         for (l = m; l > k; l--)
4525         {
4526           strcpy(highscore[l].Name, highscore[l - 1].Name);
4527           highscore[l].Score = highscore[l - 1].Score;
4528         }
4529       }
4530
4531 #ifdef ONE_PER_NAME
4532       put_into_list:
4533 #endif
4534       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4535       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4536       highscore[k].Score = local_player->score_final; 
4537       position = k;
4538       break;
4539     }
4540
4541 #ifdef ONE_PER_NAME
4542     else if (!strncmp(setup.player_name, highscore[k].Name,
4543                       MAX_PLAYER_NAME_LEN))
4544       break;    /* player already there with a higher score */
4545 #endif
4546
4547   }
4548
4549   if (position >= 0) 
4550     SaveScore(level_nr);
4551
4552   return position;
4553 }
4554
4555 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4556 {
4557   int element = Feld[x][y];
4558   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4559   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4560   int horiz_move = (dx != 0);
4561   int sign = (horiz_move ? dx : dy);
4562   int step = sign * element_info[element].move_stepsize;
4563
4564   /* special values for move stepsize for spring and things on conveyor belt */
4565   if (horiz_move)
4566   {
4567     if (CAN_FALL(element) &&
4568         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4569       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4570     else if (element == EL_SPRING)
4571       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4572   }
4573
4574   return step;
4575 }
4576
4577 inline static int getElementMoveStepsize(int x, int y)
4578 {
4579   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4580 }
4581
4582 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4583 {
4584   if (player->GfxAction != action || player->GfxDir != dir)
4585   {
4586     player->GfxAction = action;
4587     player->GfxDir = dir;
4588     player->Frame = 0;
4589     player->StepFrame = 0;
4590   }
4591 }
4592
4593 static void ResetGfxFrame(int x, int y, boolean redraw)
4594 {
4595   int element = Feld[x][y];
4596   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4597   int last_gfx_frame = GfxFrame[x][y];
4598
4599   if (graphic_info[graphic].anim_global_sync)
4600     GfxFrame[x][y] = FrameCounter;
4601   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4602     GfxFrame[x][y] = CustomValue[x][y];
4603   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4604     GfxFrame[x][y] = element_info[element].collect_score;
4605   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4606     GfxFrame[x][y] = ChangeDelay[x][y];
4607
4608   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4609     DrawLevelGraphicAnimation(x, y, graphic);
4610 }
4611
4612 static void ResetGfxAnimation(int x, int y)
4613 {
4614   GfxAction[x][y] = ACTION_DEFAULT;
4615   GfxDir[x][y] = MovDir[x][y];
4616   GfxFrame[x][y] = 0;
4617
4618   ResetGfxFrame(x, y, FALSE);
4619
4620 #if 1
4621   if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
4622     printf(" (RESET_GFX_ANIM)");
4623 #endif
4624 }
4625
4626 static void ResetRandomAnimationValue(int x, int y)
4627 {
4628   GfxRandom[x][y] = INIT_GFX_RANDOM();
4629 }
4630
4631 void InitMovingField(int x, int y, int direction)
4632 {
4633   int element = Feld[x][y];
4634   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4635   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4636   int newx = x + dx;
4637   int newy = y + dy;
4638   boolean is_moving_before, is_moving_after;
4639
4640   /* check if element was/is moving or being moved before/after mode change */
4641   is_moving_before = (WasJustMoving[x][y] != 0);
4642   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4643
4644   /* reset animation only for moving elements which change direction of moving
4645      or which just started or stopped moving
4646      (else CEs with property "can move" / "not moving" are reset each frame) */
4647   if (is_moving_before != is_moving_after ||
4648       direction != MovDir[x][y])
4649     ResetGfxAnimation(x, y);
4650
4651   MovDir[x][y] = direction;
4652   GfxDir[x][y] = direction;
4653
4654   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4655                      direction == MV_DOWN && CAN_FALL(element) ?
4656                      ACTION_FALLING : ACTION_MOVING);
4657
4658   /* this is needed for CEs with property "can move" / "not moving" */
4659
4660   if (is_moving_after)
4661   {
4662     if (Feld[newx][newy] == EL_EMPTY)
4663       Feld[newx][newy] = EL_BLOCKED;
4664
4665     MovDir[newx][newy] = MovDir[x][y];
4666
4667     CustomValue[newx][newy] = CustomValue[x][y];
4668
4669     GfxFrame[newx][newy] = GfxFrame[x][y];
4670     GfxRandom[newx][newy] = GfxRandom[x][y];
4671     GfxAction[newx][newy] = GfxAction[x][y];
4672     GfxDir[newx][newy] = GfxDir[x][y];
4673   }
4674 }
4675
4676 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4677 {
4678   int direction = MovDir[x][y];
4679   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4680   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4681
4682   *goes_to_x = newx;
4683   *goes_to_y = newy;
4684 }
4685
4686 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4687 {
4688   int oldx = x, oldy = y;
4689   int direction = MovDir[x][y];
4690
4691   if (direction == MV_LEFT)
4692     oldx++;
4693   else if (direction == MV_RIGHT)
4694     oldx--;
4695   else if (direction == MV_UP)
4696     oldy++;
4697   else if (direction == MV_DOWN)
4698     oldy--;
4699
4700   *comes_from_x = oldx;
4701   *comes_from_y = oldy;
4702 }
4703
4704 int MovingOrBlocked2Element(int x, int y)
4705 {
4706   int element = Feld[x][y];
4707
4708   if (element == EL_BLOCKED)
4709   {
4710     int oldx, oldy;
4711
4712     Blocked2Moving(x, y, &oldx, &oldy);
4713     return Feld[oldx][oldy];
4714   }
4715   else
4716     return element;
4717 }
4718
4719 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4720 {
4721   /* like MovingOrBlocked2Element(), but if element is moving
4722      and (x,y) is the field the moving element is just leaving,
4723      return EL_BLOCKED instead of the element value */
4724   int element = Feld[x][y];
4725
4726   if (IS_MOVING(x, y))
4727   {
4728     if (element == EL_BLOCKED)
4729     {
4730       int oldx, oldy;
4731
4732       Blocked2Moving(x, y, &oldx, &oldy);
4733       return Feld[oldx][oldy];
4734     }
4735     else
4736       return EL_BLOCKED;
4737   }
4738   else
4739     return element;
4740 }
4741
4742 static void RemoveField(int x, int y)
4743 {
4744   Feld[x][y] = EL_EMPTY;
4745
4746   MovPos[x][y] = 0;
4747   MovDir[x][y] = 0;
4748   MovDelay[x][y] = 0;
4749
4750   CustomValue[x][y] = 0;
4751
4752   AmoebaNr[x][y] = 0;
4753   ChangeDelay[x][y] = 0;
4754   ChangePage[x][y] = -1;
4755   Pushed[x][y] = FALSE;
4756
4757   GfxElement[x][y] = EL_UNDEFINED;
4758   GfxAction[x][y] = ACTION_DEFAULT;
4759   GfxDir[x][y] = MV_NONE;
4760 }
4761
4762 void RemoveMovingField(int x, int y)
4763 {
4764   int oldx = x, oldy = y, newx = x, newy = y;
4765   int element = Feld[x][y];
4766   int next_element = EL_UNDEFINED;
4767
4768   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4769     return;
4770
4771   if (IS_MOVING(x, y))
4772   {
4773     Moving2Blocked(x, y, &newx, &newy);
4774
4775     if (Feld[newx][newy] != EL_BLOCKED)
4776     {
4777       /* element is moving, but target field is not free (blocked), but
4778          already occupied by something different (example: acid pool);
4779          in this case, only remove the moving field, but not the target */
4780
4781       RemoveField(oldx, oldy);
4782
4783       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4784
4785       TEST_DrawLevelField(oldx, oldy);
4786
4787       return;
4788     }
4789   }
4790   else if (element == EL_BLOCKED)
4791   {
4792     Blocked2Moving(x, y, &oldx, &oldy);
4793     if (!IS_MOVING(oldx, oldy))
4794       return;
4795   }
4796
4797   if (element == EL_BLOCKED &&
4798       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4799        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4800        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4801        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4802        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4803        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4804     next_element = get_next_element(Feld[oldx][oldy]);
4805
4806   RemoveField(oldx, oldy);
4807   RemoveField(newx, newy);
4808
4809   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4810
4811   if (next_element != EL_UNDEFINED)
4812     Feld[oldx][oldy] = next_element;
4813
4814   TEST_DrawLevelField(oldx, oldy);
4815   TEST_DrawLevelField(newx, newy);
4816 }
4817
4818 void DrawDynamite(int x, int y)
4819 {
4820   int sx = SCREENX(x), sy = SCREENY(y);
4821   int graphic = el2img(Feld[x][y]);
4822   int frame;
4823
4824   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4825     return;
4826
4827   if (IS_WALKABLE_INSIDE(Back[x][y]))
4828     return;
4829
4830   if (Back[x][y])
4831     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4832   else if (Store[x][y])
4833     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4834
4835   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4836
4837   if (Back[x][y] || Store[x][y])
4838     DrawGraphicThruMask(sx, sy, graphic, frame);
4839   else
4840     DrawGraphic(sx, sy, graphic, frame);
4841 }
4842
4843 void CheckDynamite(int x, int y)
4844 {
4845   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4846   {
4847     MovDelay[x][y]--;
4848
4849     if (MovDelay[x][y] != 0)
4850     {
4851       DrawDynamite(x, y);
4852       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4853
4854       return;
4855     }
4856   }
4857
4858   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4859
4860   Bang(x, y);
4861 }
4862
4863 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4864 {
4865   boolean num_checked_players = 0;
4866   int i;
4867
4868   for (i = 0; i < MAX_PLAYERS; i++)
4869   {
4870     if (stored_player[i].active)
4871     {
4872       int sx = stored_player[i].jx;
4873       int sy = stored_player[i].jy;
4874
4875       if (num_checked_players == 0)
4876       {
4877         *sx1 = *sx2 = sx;
4878         *sy1 = *sy2 = sy;
4879       }
4880       else
4881       {
4882         *sx1 = MIN(*sx1, sx);
4883         *sy1 = MIN(*sy1, sy);
4884         *sx2 = MAX(*sx2, sx);
4885         *sy2 = MAX(*sy2, sy);
4886       }
4887
4888       num_checked_players++;
4889     }
4890   }
4891 }
4892
4893 static boolean checkIfAllPlayersFitToScreen_RND()
4894 {
4895   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4896
4897   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4898
4899   return (sx2 - sx1 < SCR_FIELDX &&
4900           sy2 - sy1 < SCR_FIELDY);
4901 }
4902
4903 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4904 {
4905   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4906
4907   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4908
4909   *sx = (sx1 + sx2) / 2;
4910   *sy = (sy1 + sy2) / 2;
4911 }
4912
4913 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4914                         boolean center_screen, boolean quick_relocation)
4915 {
4916   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4917   boolean no_delay = (tape.warp_forward);
4918   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4919   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4920
4921   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4922   {
4923     RedrawPlayfield();
4924   }
4925   else if (quick_relocation)
4926   {
4927     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4928     {
4929       if (!level.shifted_relocation || center_screen)
4930       {
4931         /* quick relocation (without scrolling), with centering of screen */
4932
4933         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4934                     x > SBX_Right + MIDPOSX ? SBX_Right :
4935                     x - MIDPOSX);
4936
4937         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4938                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4939                     y - MIDPOSY);
4940       }
4941       else
4942       {
4943         /* quick relocation (without scrolling), but do not center screen */
4944
4945         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4946                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4947                                old_x - MIDPOSX);
4948
4949         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4950                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4951                                old_y - MIDPOSY);
4952
4953         int offset_x = x + (scroll_x - center_scroll_x);
4954         int offset_y = y + (scroll_y - center_scroll_y);
4955
4956         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4957                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4958                     offset_x - MIDPOSX);
4959
4960         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4961                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4962                     offset_y - MIDPOSY);
4963       }
4964     }
4965     else
4966     {
4967       if (!level.shifted_relocation || center_screen)
4968       {
4969         /* quick relocation (without scrolling), with centering of screen */
4970
4971         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4972                     x > SBX_Right + MIDPOSX ? SBX_Right :
4973                     x - MIDPOSX);
4974
4975         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4976                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4977                     y - MIDPOSY);
4978       }
4979       else
4980       {
4981         /* quick relocation (without scrolling), but do not center screen */
4982
4983         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4984                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4985                                old_x - MIDPOSX);
4986
4987         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4988                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4989                                old_y - MIDPOSY);
4990
4991         int offset_x = x + (scroll_x - center_scroll_x);
4992         int offset_y = y + (scroll_y - center_scroll_y);
4993
4994         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4995                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4996                     offset_x - MIDPOSX);
4997
4998         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4999                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5000                     offset_y - MIDPOSY);
5001       }
5002     }
5003
5004     RedrawPlayfield();
5005   }
5006   else
5007   {
5008     int scroll_xx, scroll_yy;
5009
5010     if (!level.shifted_relocation || center_screen)
5011     {
5012       /* visible relocation (with scrolling), with centering of screen */
5013
5014       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5015                    x > SBX_Right + MIDPOSX ? SBX_Right :
5016                    x - MIDPOSX);
5017
5018       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5019                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5020                    y - MIDPOSY);
5021     }
5022     else
5023     {
5024       /* visible relocation (with scrolling), but do not center screen */
5025
5026       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5027                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5028                              old_x - MIDPOSX);
5029
5030       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5031                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5032                              old_y - MIDPOSY);
5033
5034       int offset_x = x + (scroll_x - center_scroll_x);
5035       int offset_y = y + (scroll_y - center_scroll_y);
5036
5037       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5038                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5039                    offset_x - MIDPOSX);
5040
5041       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5042                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5043                    offset_y - MIDPOSY);
5044     }
5045
5046     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5047
5048     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5049     {
5050       int dx = 0, dy = 0;
5051       int fx = FX, fy = FY;
5052
5053       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5054       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5055
5056       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5057         break;
5058
5059       scroll_x -= dx;
5060       scroll_y -= dy;
5061
5062       fx += dx * TILEX / 2;
5063       fy += dy * TILEY / 2;
5064
5065       ScrollLevel(dx, dy);
5066       DrawAllPlayers();
5067
5068       /* scroll in two steps of half tile size to make things smoother */
5069       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5070       Delay(wait_delay_value);
5071
5072       /* scroll second step to align at full tile size */
5073       BackToFront();
5074       Delay(wait_delay_value);
5075     }
5076
5077     DrawAllPlayers();
5078     BackToFront();
5079     Delay(wait_delay_value);
5080   }
5081 }
5082
5083 void RelocatePlayer(int jx, int jy, int el_player_raw)
5084 {
5085   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5086   int player_nr = GET_PLAYER_NR(el_player);
5087   struct PlayerInfo *player = &stored_player[player_nr];
5088   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5089   boolean no_delay = (tape.warp_forward);
5090   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5091   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5092   int old_jx = player->jx;
5093   int old_jy = player->jy;
5094   int old_element = Feld[old_jx][old_jy];
5095   int element = Feld[jx][jy];
5096   boolean player_relocated = (old_jx != jx || old_jy != jy);
5097
5098   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5099   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5100   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5101   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5102   int leave_side_horiz = move_dir_horiz;
5103   int leave_side_vert  = move_dir_vert;
5104   int enter_side = enter_side_horiz | enter_side_vert;
5105   int leave_side = leave_side_horiz | leave_side_vert;
5106
5107   if (player->GameOver)         /* do not reanimate dead player */
5108     return;
5109
5110   if (!player_relocated)        /* no need to relocate the player */
5111     return;
5112
5113   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5114   {
5115     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5116     DrawLevelField(jx, jy);
5117   }
5118
5119   if (player->present)
5120   {
5121     while (player->MovPos)
5122     {
5123       ScrollPlayer(player, SCROLL_GO_ON);
5124       ScrollScreen(NULL, SCROLL_GO_ON);
5125
5126       AdvanceFrameAndPlayerCounters(player->index_nr);
5127
5128       DrawPlayer(player);
5129
5130       BackToFront();
5131       Delay(wait_delay_value);
5132     }
5133
5134     DrawPlayer(player);         /* needed here only to cleanup last field */
5135     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5136
5137     player->is_moving = FALSE;
5138   }
5139
5140   if (IS_CUSTOM_ELEMENT(old_element))
5141     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5142                                CE_LEFT_BY_PLAYER,
5143                                player->index_bit, leave_side);
5144
5145   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5146                                       CE_PLAYER_LEAVES_X,
5147                                       player->index_bit, leave_side);
5148
5149   Feld[jx][jy] = el_player;
5150   InitPlayerField(jx, jy, el_player, TRUE);
5151
5152   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5153      possible that the relocation target field did not contain a player element,
5154      but a walkable element, to which the new player was relocated -- in this
5155      case, restore that (already initialized!) element on the player field */
5156   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5157   {
5158     Feld[jx][jy] = element;     /* restore previously existing element */
5159   }
5160
5161   /* only visually relocate centered player */
5162   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5163                      FALSE, level.instant_relocation);
5164
5165   TestIfPlayerTouchesBadThing(jx, jy);
5166   TestIfPlayerTouchesCustomElement(jx, jy);
5167
5168   if (IS_CUSTOM_ELEMENT(element))
5169     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5170                                player->index_bit, enter_side);
5171
5172   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5173                                       player->index_bit, enter_side);
5174
5175   if (player->is_switching)
5176   {
5177     /* ensure that relocation while still switching an element does not cause
5178        a new element to be treated as also switched directly after relocation
5179        (this is important for teleporter switches that teleport the player to
5180        a place where another teleporter switch is in the same direction, which
5181        would then incorrectly be treated as immediately switched before the
5182        direction key that caused the switch was released) */
5183
5184     player->switch_x += jx - old_jx;
5185     player->switch_y += jy - old_jy;
5186   }
5187 }
5188
5189 void Explode(int ex, int ey, int phase, int mode)
5190 {
5191   int x, y;
5192   int last_phase;
5193   int border_element;
5194
5195   /* !!! eliminate this variable !!! */
5196   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5197
5198   if (game.explosions_delayed)
5199   {
5200     ExplodeField[ex][ey] = mode;
5201     return;
5202   }
5203
5204   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5205   {
5206     int center_element = Feld[ex][ey];
5207     int artwork_element, explosion_element;     /* set these values later */
5208
5209     /* remove things displayed in background while burning dynamite */
5210     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5211       Back[ex][ey] = 0;
5212
5213     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5214     {
5215       /* put moving element to center field (and let it explode there) */
5216       center_element = MovingOrBlocked2Element(ex, ey);
5217       RemoveMovingField(ex, ey);
5218       Feld[ex][ey] = center_element;
5219     }
5220
5221     /* now "center_element" is finally determined -- set related values now */
5222     artwork_element = center_element;           /* for custom player artwork */
5223     explosion_element = center_element;         /* for custom player artwork */
5224
5225     if (IS_PLAYER(ex, ey))
5226     {
5227       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5228
5229       artwork_element = stored_player[player_nr].artwork_element;
5230
5231       if (level.use_explosion_element[player_nr])
5232       {
5233         explosion_element = level.explosion_element[player_nr];
5234         artwork_element = explosion_element;
5235       }
5236     }
5237
5238     if (mode == EX_TYPE_NORMAL ||
5239         mode == EX_TYPE_CENTER ||
5240         mode == EX_TYPE_CROSS)
5241       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5242
5243     last_phase = element_info[explosion_element].explosion_delay + 1;
5244
5245     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5246     {
5247       int xx = x - ex + 1;
5248       int yy = y - ey + 1;
5249       int element;
5250
5251       if (!IN_LEV_FIELD(x, y) ||
5252           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5253           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5254         continue;
5255
5256       element = Feld[x][y];
5257
5258       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5259       {
5260         element = MovingOrBlocked2Element(x, y);
5261
5262         if (!IS_EXPLOSION_PROOF(element))
5263           RemoveMovingField(x, y);
5264       }
5265
5266       /* indestructible elements can only explode in center (but not flames) */
5267       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5268                                            mode == EX_TYPE_BORDER)) ||
5269           element == EL_FLAMES)
5270         continue;
5271
5272       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5273          behaviour, for example when touching a yamyam that explodes to rocks
5274          with active deadly shield, a rock is created under the player !!! */
5275       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5276 #if 0
5277       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5278           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5279            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5280 #else
5281       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5282 #endif
5283       {
5284         if (IS_ACTIVE_BOMB(element))
5285         {
5286           /* re-activate things under the bomb like gate or penguin */
5287           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5288           Back[x][y] = 0;
5289         }
5290
5291         continue;
5292       }
5293
5294       /* save walkable background elements while explosion on same tile */
5295       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5296           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5297         Back[x][y] = element;
5298
5299       /* ignite explodable elements reached by other explosion */
5300       if (element == EL_EXPLOSION)
5301         element = Store2[x][y];
5302
5303       if (AmoebaNr[x][y] &&
5304           (element == EL_AMOEBA_FULL ||
5305            element == EL_BD_AMOEBA ||
5306            element == EL_AMOEBA_GROWING))
5307       {
5308         AmoebaCnt[AmoebaNr[x][y]]--;
5309         AmoebaCnt2[AmoebaNr[x][y]]--;
5310       }
5311
5312       RemoveField(x, y);
5313
5314       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5315       {
5316         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5317
5318         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5319
5320         if (PLAYERINFO(ex, ey)->use_murphy)
5321           Store[x][y] = EL_EMPTY;
5322       }
5323
5324       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5325          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5326       else if (ELEM_IS_PLAYER(center_element))
5327         Store[x][y] = EL_EMPTY;
5328       else if (center_element == EL_YAMYAM)
5329         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5330       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5331         Store[x][y] = element_info[center_element].content.e[xx][yy];
5332 #if 1
5333       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5334          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5335          otherwise) -- FIX THIS !!! */
5336       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5337         Store[x][y] = element_info[element].content.e[1][1];
5338 #else
5339       else if (!CAN_EXPLODE(element))
5340         Store[x][y] = element_info[element].content.e[1][1];
5341 #endif
5342       else
5343         Store[x][y] = EL_EMPTY;
5344
5345       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5346           center_element == EL_AMOEBA_TO_DIAMOND)
5347         Store2[x][y] = element;
5348
5349       Feld[x][y] = EL_EXPLOSION;
5350       GfxElement[x][y] = artwork_element;
5351
5352       ExplodePhase[x][y] = 1;
5353       ExplodeDelay[x][y] = last_phase;
5354
5355       Stop[x][y] = TRUE;
5356     }
5357
5358     if (center_element == EL_YAMYAM)
5359       game.yamyam_content_nr =
5360         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5361
5362     return;
5363   }
5364
5365   if (Stop[ex][ey])
5366     return;
5367
5368   x = ex;
5369   y = ey;
5370
5371   if (phase == 1)
5372     GfxFrame[x][y] = 0;         /* restart explosion animation */
5373
5374   last_phase = ExplodeDelay[x][y];
5375
5376   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5377
5378   /* this can happen if the player leaves an explosion just in time */
5379   if (GfxElement[x][y] == EL_UNDEFINED)
5380     GfxElement[x][y] = EL_EMPTY;
5381
5382   border_element = Store2[x][y];
5383   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5384     border_element = StorePlayer[x][y];
5385
5386   if (phase == element_info[border_element].ignition_delay ||
5387       phase == last_phase)
5388   {
5389     boolean border_explosion = FALSE;
5390
5391     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5392         !PLAYER_EXPLOSION_PROTECTED(x, y))
5393     {
5394       KillPlayerUnlessExplosionProtected(x, y);
5395       border_explosion = TRUE;
5396     }
5397     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5398     {
5399       Feld[x][y] = Store2[x][y];
5400       Store2[x][y] = 0;
5401       Bang(x, y);
5402       border_explosion = TRUE;
5403     }
5404     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5405     {
5406       AmoebeUmwandeln(x, y);
5407       Store2[x][y] = 0;
5408       border_explosion = TRUE;
5409     }
5410
5411     /* if an element just explodes due to another explosion (chain-reaction),
5412        do not immediately end the new explosion when it was the last frame of
5413        the explosion (as it would be done in the following "if"-statement!) */
5414     if (border_explosion && phase == last_phase)
5415       return;
5416   }
5417
5418   if (phase == last_phase)
5419   {
5420     int element;
5421
5422     element = Feld[x][y] = Store[x][y];
5423     Store[x][y] = Store2[x][y] = 0;
5424     GfxElement[x][y] = EL_UNDEFINED;
5425
5426     /* player can escape from explosions and might therefore be still alive */
5427     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5428         element <= EL_PLAYER_IS_EXPLODING_4)
5429     {
5430       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5431       int explosion_element = EL_PLAYER_1 + player_nr;
5432       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5433       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5434
5435       if (level.use_explosion_element[player_nr])
5436         explosion_element = level.explosion_element[player_nr];
5437
5438       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5439                     element_info[explosion_element].content.e[xx][yy]);
5440     }
5441
5442     /* restore probably existing indestructible background element */
5443     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5444       element = Feld[x][y] = Back[x][y];
5445     Back[x][y] = 0;
5446
5447     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5448     GfxDir[x][y] = MV_NONE;
5449     ChangeDelay[x][y] = 0;
5450     ChangePage[x][y] = -1;
5451
5452     CustomValue[x][y] = 0;
5453
5454     InitField_WithBug2(x, y, FALSE);
5455
5456     TEST_DrawLevelField(x, y);
5457
5458     TestIfElementTouchesCustomElement(x, y);
5459
5460     if (GFX_CRUMBLED(element))
5461       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5462
5463     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5464       StorePlayer[x][y] = 0;
5465
5466     if (ELEM_IS_PLAYER(element))
5467       RelocatePlayer(x, y, element);
5468   }
5469   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5470   {
5471     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5472     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5473
5474     if (phase == delay)
5475       TEST_DrawLevelFieldCrumbled(x, y);
5476
5477     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5478     {
5479       DrawLevelElement(x, y, Back[x][y]);
5480       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5481     }
5482     else if (IS_WALKABLE_UNDER(Back[x][y]))
5483     {
5484       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5485       DrawLevelElementThruMask(x, y, Back[x][y]);
5486     }
5487     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5488       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5489   }
5490 }
5491
5492 void DynaExplode(int ex, int ey)
5493 {
5494   int i, j;
5495   int dynabomb_element = Feld[ex][ey];
5496   int dynabomb_size = 1;
5497   boolean dynabomb_xl = FALSE;
5498   struct PlayerInfo *player;
5499   static int xy[4][2] =
5500   {
5501     { 0, -1 },
5502     { -1, 0 },
5503     { +1, 0 },
5504     { 0, +1 }
5505   };
5506
5507   if (IS_ACTIVE_BOMB(dynabomb_element))
5508   {
5509     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5510     dynabomb_size = player->dynabomb_size;
5511     dynabomb_xl = player->dynabomb_xl;
5512     player->dynabombs_left++;
5513   }
5514
5515   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5516
5517   for (i = 0; i < NUM_DIRECTIONS; i++)
5518   {
5519     for (j = 1; j <= dynabomb_size; j++)
5520     {
5521       int x = ex + j * xy[i][0];
5522       int y = ey + j * xy[i][1];
5523       int element;
5524
5525       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5526         break;
5527
5528       element = Feld[x][y];
5529
5530       /* do not restart explosions of fields with active bombs */
5531       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5532         continue;
5533
5534       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5535
5536       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5537           !IS_DIGGABLE(element) && !dynabomb_xl)
5538         break;
5539     }
5540   }
5541 }
5542
5543 void Bang(int x, int y)
5544 {
5545   int element = MovingOrBlocked2Element(x, y);
5546   int explosion_type = EX_TYPE_NORMAL;
5547
5548   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5549   {
5550     struct PlayerInfo *player = PLAYERINFO(x, y);
5551
5552     element = Feld[x][y] = player->initial_element;
5553
5554     if (level.use_explosion_element[player->index_nr])
5555     {
5556       int explosion_element = level.explosion_element[player->index_nr];
5557
5558       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5559         explosion_type = EX_TYPE_CROSS;
5560       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5561         explosion_type = EX_TYPE_CENTER;
5562     }
5563   }
5564
5565   switch (element)
5566   {
5567     case EL_BUG:
5568     case EL_SPACESHIP:
5569     case EL_BD_BUTTERFLY:
5570     case EL_BD_FIREFLY:
5571     case EL_YAMYAM:
5572     case EL_DARK_YAMYAM:
5573     case EL_ROBOT:
5574     case EL_PACMAN:
5575     case EL_MOLE:
5576       RaiseScoreElement(element);
5577       break;
5578
5579     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5580     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5581     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5582     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5583     case EL_DYNABOMB_INCREASE_NUMBER:
5584     case EL_DYNABOMB_INCREASE_SIZE:
5585     case EL_DYNABOMB_INCREASE_POWER:
5586       explosion_type = EX_TYPE_DYNA;
5587       break;
5588
5589     case EL_DC_LANDMINE:
5590       explosion_type = EX_TYPE_CENTER;
5591       break;
5592
5593     case EL_PENGUIN:
5594     case EL_LAMP:
5595     case EL_LAMP_ACTIVE:
5596     case EL_AMOEBA_TO_DIAMOND:
5597       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5598         explosion_type = EX_TYPE_CENTER;
5599       break;
5600
5601     default:
5602       if (element_info[element].explosion_type == EXPLODES_CROSS)
5603         explosion_type = EX_TYPE_CROSS;
5604       else if (element_info[element].explosion_type == EXPLODES_1X1)
5605         explosion_type = EX_TYPE_CENTER;
5606       break;
5607   }
5608
5609   if (explosion_type == EX_TYPE_DYNA)
5610     DynaExplode(x, y);
5611   else
5612     Explode(x, y, EX_PHASE_START, explosion_type);
5613
5614   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5615 }
5616
5617 void SplashAcid(int x, int y)
5618 {
5619   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5620       (!IN_LEV_FIELD(x - 1, y - 2) ||
5621        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5622     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5623
5624   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5625       (!IN_LEV_FIELD(x + 1, y - 2) ||
5626        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5627     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5628
5629   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5630 }
5631
5632 static void InitBeltMovement()
5633 {
5634   static int belt_base_element[4] =
5635   {
5636     EL_CONVEYOR_BELT_1_LEFT,
5637     EL_CONVEYOR_BELT_2_LEFT,
5638     EL_CONVEYOR_BELT_3_LEFT,
5639     EL_CONVEYOR_BELT_4_LEFT
5640   };
5641   static int belt_base_active_element[4] =
5642   {
5643     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5644     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5645     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5646     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5647   };
5648
5649   int x, y, i, j;
5650
5651   /* set frame order for belt animation graphic according to belt direction */
5652   for (i = 0; i < NUM_BELTS; i++)
5653   {
5654     int belt_nr = i;
5655
5656     for (j = 0; j < NUM_BELT_PARTS; j++)
5657     {
5658       int element = belt_base_active_element[belt_nr] + j;
5659       int graphic_1 = el2img(element);
5660       int graphic_2 = el2panelimg(element);
5661
5662       if (game.belt_dir[i] == MV_LEFT)
5663       {
5664         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5665         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5666       }
5667       else
5668       {
5669         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5670         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5671       }
5672     }
5673   }
5674
5675   SCAN_PLAYFIELD(x, y)
5676   {
5677     int element = Feld[x][y];
5678
5679     for (i = 0; i < NUM_BELTS; i++)
5680     {
5681       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5682       {
5683         int e_belt_nr = getBeltNrFromBeltElement(element);
5684         int belt_nr = i;
5685
5686         if (e_belt_nr == belt_nr)
5687         {
5688           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5689
5690           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5691         }
5692       }
5693     }
5694   }
5695 }
5696
5697 static void ToggleBeltSwitch(int x, int y)
5698 {
5699   static int belt_base_element[4] =
5700   {
5701     EL_CONVEYOR_BELT_1_LEFT,
5702     EL_CONVEYOR_BELT_2_LEFT,
5703     EL_CONVEYOR_BELT_3_LEFT,
5704     EL_CONVEYOR_BELT_4_LEFT
5705   };
5706   static int belt_base_active_element[4] =
5707   {
5708     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5709     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5710     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5711     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5712   };
5713   static int belt_base_switch_element[4] =
5714   {
5715     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5716     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5717     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5718     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5719   };
5720   static int belt_move_dir[4] =
5721   {
5722     MV_LEFT,
5723     MV_NONE,
5724     MV_RIGHT,
5725     MV_NONE,
5726   };
5727
5728   int element = Feld[x][y];
5729   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5730   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5731   int belt_dir = belt_move_dir[belt_dir_nr];
5732   int xx, yy, i;
5733
5734   if (!IS_BELT_SWITCH(element))
5735     return;
5736
5737   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5738   game.belt_dir[belt_nr] = belt_dir;
5739
5740   if (belt_dir_nr == 3)
5741     belt_dir_nr = 1;
5742
5743   /* set frame order for belt animation graphic according to belt direction */
5744   for (i = 0; i < NUM_BELT_PARTS; i++)
5745   {
5746     int element = belt_base_active_element[belt_nr] + i;
5747     int graphic_1 = el2img(element);
5748     int graphic_2 = el2panelimg(element);
5749
5750     if (belt_dir == MV_LEFT)
5751     {
5752       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5753       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5754     }
5755     else
5756     {
5757       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5758       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5759     }
5760   }
5761
5762   SCAN_PLAYFIELD(xx, yy)
5763   {
5764     int element = Feld[xx][yy];
5765
5766     if (IS_BELT_SWITCH(element))
5767     {
5768       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5769
5770       if (e_belt_nr == belt_nr)
5771       {
5772         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5773         TEST_DrawLevelField(xx, yy);
5774       }
5775     }
5776     else if (IS_BELT(element) && belt_dir != MV_NONE)
5777     {
5778       int e_belt_nr = getBeltNrFromBeltElement(element);
5779
5780       if (e_belt_nr == belt_nr)
5781       {
5782         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5783
5784         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5785         TEST_DrawLevelField(xx, yy);
5786       }
5787     }
5788     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5789     {
5790       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5791
5792       if (e_belt_nr == belt_nr)
5793       {
5794         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5795
5796         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5797         TEST_DrawLevelField(xx, yy);
5798       }
5799     }
5800   }
5801 }
5802
5803 static void ToggleSwitchgateSwitch(int x, int y)
5804 {
5805   int xx, yy;
5806
5807   game.switchgate_pos = !game.switchgate_pos;
5808
5809   SCAN_PLAYFIELD(xx, yy)
5810   {
5811     int element = Feld[xx][yy];
5812
5813     if (element == EL_SWITCHGATE_SWITCH_UP)
5814     {
5815       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5816       TEST_DrawLevelField(xx, yy);
5817     }
5818     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5819     {
5820       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5821       TEST_DrawLevelField(xx, yy);
5822     }
5823     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5824     {
5825       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5826       TEST_DrawLevelField(xx, yy);
5827     }
5828     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5829     {
5830       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5831       TEST_DrawLevelField(xx, yy);
5832     }
5833     else if (element == EL_SWITCHGATE_OPEN ||
5834              element == EL_SWITCHGATE_OPENING)
5835     {
5836       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5837
5838       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5839     }
5840     else if (element == EL_SWITCHGATE_CLOSED ||
5841              element == EL_SWITCHGATE_CLOSING)
5842     {
5843       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5844
5845       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5846     }
5847   }
5848 }
5849
5850 static int getInvisibleActiveFromInvisibleElement(int element)
5851 {
5852   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5853           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5854           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5855           element);
5856 }
5857
5858 static int getInvisibleFromInvisibleActiveElement(int element)
5859 {
5860   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5861           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5862           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5863           element);
5864 }
5865
5866 static void RedrawAllLightSwitchesAndInvisibleElements()
5867 {
5868   int x, y;
5869
5870   SCAN_PLAYFIELD(x, y)
5871   {
5872     int element = Feld[x][y];
5873
5874     if (element == EL_LIGHT_SWITCH &&
5875         game.light_time_left > 0)
5876     {
5877       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5878       TEST_DrawLevelField(x, y);
5879     }
5880     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5881              game.light_time_left == 0)
5882     {
5883       Feld[x][y] = EL_LIGHT_SWITCH;
5884       TEST_DrawLevelField(x, y);
5885     }
5886     else if (element == EL_EMC_DRIPPER &&
5887              game.light_time_left > 0)
5888     {
5889       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5890       TEST_DrawLevelField(x, y);
5891     }
5892     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5893              game.light_time_left == 0)
5894     {
5895       Feld[x][y] = EL_EMC_DRIPPER;
5896       TEST_DrawLevelField(x, y);
5897     }
5898     else if (element == EL_INVISIBLE_STEELWALL ||
5899              element == EL_INVISIBLE_WALL ||
5900              element == EL_INVISIBLE_SAND)
5901     {
5902       if (game.light_time_left > 0)
5903         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5904
5905       TEST_DrawLevelField(x, y);
5906
5907       /* uncrumble neighbour fields, if needed */
5908       if (element == EL_INVISIBLE_SAND)
5909         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5910     }
5911     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5912              element == EL_INVISIBLE_WALL_ACTIVE ||
5913              element == EL_INVISIBLE_SAND_ACTIVE)
5914     {
5915       if (game.light_time_left == 0)
5916         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5917
5918       TEST_DrawLevelField(x, y);
5919
5920       /* re-crumble neighbour fields, if needed */
5921       if (element == EL_INVISIBLE_SAND)
5922         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5923     }
5924   }
5925 }
5926
5927 static void RedrawAllInvisibleElementsForLenses()
5928 {
5929   int x, y;
5930
5931   SCAN_PLAYFIELD(x, y)
5932   {
5933     int element = Feld[x][y];
5934
5935     if (element == EL_EMC_DRIPPER &&
5936         game.lenses_time_left > 0)
5937     {
5938       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5939       TEST_DrawLevelField(x, y);
5940     }
5941     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5942              game.lenses_time_left == 0)
5943     {
5944       Feld[x][y] = EL_EMC_DRIPPER;
5945       TEST_DrawLevelField(x, y);
5946     }
5947     else if (element == EL_INVISIBLE_STEELWALL ||
5948              element == EL_INVISIBLE_WALL ||
5949              element == EL_INVISIBLE_SAND)
5950     {
5951       if (game.lenses_time_left > 0)
5952         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5953
5954       TEST_DrawLevelField(x, y);
5955
5956       /* uncrumble neighbour fields, if needed */
5957       if (element == EL_INVISIBLE_SAND)
5958         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5959     }
5960     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5961              element == EL_INVISIBLE_WALL_ACTIVE ||
5962              element == EL_INVISIBLE_SAND_ACTIVE)
5963     {
5964       if (game.lenses_time_left == 0)
5965         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5966
5967       TEST_DrawLevelField(x, y);
5968
5969       /* re-crumble neighbour fields, if needed */
5970       if (element == EL_INVISIBLE_SAND)
5971         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5972     }
5973   }
5974 }
5975
5976 static void RedrawAllInvisibleElementsForMagnifier()
5977 {
5978   int x, y;
5979
5980   SCAN_PLAYFIELD(x, y)
5981   {
5982     int element = Feld[x][y];
5983
5984     if (element == EL_EMC_FAKE_GRASS &&
5985         game.magnify_time_left > 0)
5986     {
5987       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5988       TEST_DrawLevelField(x, y);
5989     }
5990     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5991              game.magnify_time_left == 0)
5992     {
5993       Feld[x][y] = EL_EMC_FAKE_GRASS;
5994       TEST_DrawLevelField(x, y);
5995     }
5996     else if (IS_GATE_GRAY(element) &&
5997              game.magnify_time_left > 0)
5998     {
5999       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6000                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6001                     IS_EM_GATE_GRAY(element) ?
6002                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6003                     IS_EMC_GATE_GRAY(element) ?
6004                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6005                     IS_DC_GATE_GRAY(element) ?
6006                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6007                     element);
6008       TEST_DrawLevelField(x, y);
6009     }
6010     else if (IS_GATE_GRAY_ACTIVE(element) &&
6011              game.magnify_time_left == 0)
6012     {
6013       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6014                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6015                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6016                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6017                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6018                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6019                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6020                     EL_DC_GATE_WHITE_GRAY :
6021                     element);
6022       TEST_DrawLevelField(x, y);
6023     }
6024   }
6025 }
6026
6027 static void ToggleLightSwitch(int x, int y)
6028 {
6029   int element = Feld[x][y];
6030
6031   game.light_time_left =
6032     (element == EL_LIGHT_SWITCH ?
6033      level.time_light * FRAMES_PER_SECOND : 0);
6034
6035   RedrawAllLightSwitchesAndInvisibleElements();
6036 }
6037
6038 static void ActivateTimegateSwitch(int x, int y)
6039 {
6040   int xx, yy;
6041
6042   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6043
6044   SCAN_PLAYFIELD(xx, yy)
6045   {
6046     int element = Feld[xx][yy];
6047
6048     if (element == EL_TIMEGATE_CLOSED ||
6049         element == EL_TIMEGATE_CLOSING)
6050     {
6051       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6052       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6053     }
6054
6055     /*
6056     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6057     {
6058       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6059       TEST_DrawLevelField(xx, yy);
6060     }
6061     */
6062
6063   }
6064
6065   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6066                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6067 }
6068
6069 void Impact(int x, int y)
6070 {
6071   boolean last_line = (y == lev_fieldy - 1);
6072   boolean object_hit = FALSE;
6073   boolean impact = (last_line || object_hit);
6074   int element = Feld[x][y];
6075   int smashed = EL_STEELWALL;
6076
6077   if (!last_line)       /* check if element below was hit */
6078   {
6079     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6080       return;
6081
6082     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6083                                          MovDir[x][y + 1] != MV_DOWN ||
6084                                          MovPos[x][y + 1] <= TILEY / 2));
6085
6086     /* do not smash moving elements that left the smashed field in time */
6087     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6088         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6089       object_hit = FALSE;
6090
6091 #if USE_QUICKSAND_IMPACT_BUGFIX
6092     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6093     {
6094       RemoveMovingField(x, y + 1);
6095       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6096       Feld[x][y + 2] = EL_ROCK;
6097       TEST_DrawLevelField(x, y + 2);
6098
6099       object_hit = TRUE;
6100     }
6101
6102     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6103     {
6104       RemoveMovingField(x, y + 1);
6105       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6106       Feld[x][y + 2] = EL_ROCK;
6107       TEST_DrawLevelField(x, y + 2);
6108
6109       object_hit = TRUE;
6110     }
6111 #endif
6112
6113     if (object_hit)
6114       smashed = MovingOrBlocked2Element(x, y + 1);
6115
6116     impact = (last_line || object_hit);
6117   }
6118
6119   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6120   {
6121     SplashAcid(x, y + 1);
6122     return;
6123   }
6124
6125   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6126   /* only reset graphic animation if graphic really changes after impact */
6127   if (impact &&
6128       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6129   {
6130     ResetGfxAnimation(x, y);
6131     TEST_DrawLevelField(x, y);
6132   }
6133
6134   if (impact && CAN_EXPLODE_IMPACT(element))
6135   {
6136     Bang(x, y);
6137     return;
6138   }
6139   else if (impact && element == EL_PEARL &&
6140            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6141   {
6142     ResetGfxAnimation(x, y);
6143
6144     Feld[x][y] = EL_PEARL_BREAKING;
6145     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6146     return;
6147   }
6148   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6149   {
6150     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6151
6152     return;
6153   }
6154
6155   if (impact && element == EL_AMOEBA_DROP)
6156   {
6157     if (object_hit && IS_PLAYER(x, y + 1))
6158       KillPlayerUnlessEnemyProtected(x, y + 1);
6159     else if (object_hit && smashed == EL_PENGUIN)
6160       Bang(x, y + 1);
6161     else
6162     {
6163       Feld[x][y] = EL_AMOEBA_GROWING;
6164       Store[x][y] = EL_AMOEBA_WET;
6165
6166       ResetRandomAnimationValue(x, y);
6167     }
6168     return;
6169   }
6170
6171   if (object_hit)               /* check which object was hit */
6172   {
6173     if ((CAN_PASS_MAGIC_WALL(element) && 
6174          (smashed == EL_MAGIC_WALL ||
6175           smashed == EL_BD_MAGIC_WALL)) ||
6176         (CAN_PASS_DC_MAGIC_WALL(element) &&
6177          smashed == EL_DC_MAGIC_WALL))
6178     {
6179       int xx, yy;
6180       int activated_magic_wall =
6181         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6182          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6183          EL_DC_MAGIC_WALL_ACTIVE);
6184
6185       /* activate magic wall / mill */
6186       SCAN_PLAYFIELD(xx, yy)
6187       {
6188         if (Feld[xx][yy] == smashed)
6189           Feld[xx][yy] = activated_magic_wall;
6190       }
6191
6192       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6193       game.magic_wall_active = TRUE;
6194
6195       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6196                             SND_MAGIC_WALL_ACTIVATING :
6197                             smashed == EL_BD_MAGIC_WALL ?
6198                             SND_BD_MAGIC_WALL_ACTIVATING :
6199                             SND_DC_MAGIC_WALL_ACTIVATING));
6200     }
6201
6202     if (IS_PLAYER(x, y + 1))
6203     {
6204       if (CAN_SMASH_PLAYER(element))
6205       {
6206         KillPlayerUnlessEnemyProtected(x, y + 1);
6207         return;
6208       }
6209     }
6210     else if (smashed == EL_PENGUIN)
6211     {
6212       if (CAN_SMASH_PLAYER(element))
6213       {
6214         Bang(x, y + 1);
6215         return;
6216       }
6217     }
6218     else if (element == EL_BD_DIAMOND)
6219     {
6220       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6221       {
6222         Bang(x, y + 1);
6223         return;
6224       }
6225     }
6226     else if (((element == EL_SP_INFOTRON ||
6227                element == EL_SP_ZONK) &&
6228               (smashed == EL_SP_SNIKSNAK ||
6229                smashed == EL_SP_ELECTRON ||
6230                smashed == EL_SP_DISK_ORANGE)) ||
6231              (element == EL_SP_INFOTRON &&
6232               smashed == EL_SP_DISK_YELLOW))
6233     {
6234       Bang(x, y + 1);
6235       return;
6236     }
6237     else if (CAN_SMASH_EVERYTHING(element))
6238     {
6239       if (IS_CLASSIC_ENEMY(smashed) ||
6240           CAN_EXPLODE_SMASHED(smashed))
6241       {
6242         Bang(x, y + 1);
6243         return;
6244       }
6245       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6246       {
6247         if (smashed == EL_LAMP ||
6248             smashed == EL_LAMP_ACTIVE)
6249         {
6250           Bang(x, y + 1);
6251           return;
6252         }
6253         else if (smashed == EL_NUT)
6254         {
6255           Feld[x][y + 1] = EL_NUT_BREAKING;
6256           PlayLevelSound(x, y, SND_NUT_BREAKING);
6257           RaiseScoreElement(EL_NUT);
6258           return;
6259         }
6260         else if (smashed == EL_PEARL)
6261         {
6262           ResetGfxAnimation(x, y);
6263
6264           Feld[x][y + 1] = EL_PEARL_BREAKING;
6265           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6266           return;
6267         }
6268         else if (smashed == EL_DIAMOND)
6269         {
6270           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6271           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6272           return;
6273         }
6274         else if (IS_BELT_SWITCH(smashed))
6275         {
6276           ToggleBeltSwitch(x, y + 1);
6277         }
6278         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6279                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6280                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6281                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6282         {
6283           ToggleSwitchgateSwitch(x, y + 1);
6284         }
6285         else if (smashed == EL_LIGHT_SWITCH ||
6286                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6287         {
6288           ToggleLightSwitch(x, y + 1);
6289         }
6290         else
6291         {
6292           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6293
6294           CheckElementChangeBySide(x, y + 1, smashed, element,
6295                                    CE_SWITCHED, CH_SIDE_TOP);
6296           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6297                                             CH_SIDE_TOP);
6298         }
6299       }
6300       else
6301       {
6302         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6303       }
6304     }
6305   }
6306
6307   /* play sound of magic wall / mill */
6308   if (!last_line &&
6309       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6310        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6311        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6312   {
6313     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6314       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6315     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6316       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6317     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6318       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6319
6320     return;
6321   }
6322
6323   /* play sound of object that hits the ground */
6324   if (last_line || object_hit)
6325     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6326 }
6327
6328 inline static void TurnRoundExt(int x, int y)
6329 {
6330   static struct
6331   {
6332     int dx, dy;
6333   } move_xy[] =
6334   {
6335     {  0,  0 },
6336     { -1,  0 },
6337     { +1,  0 },
6338     {  0,  0 },
6339     {  0, -1 },
6340     {  0,  0 }, { 0, 0 }, { 0, 0 },
6341     {  0, +1 }
6342   };
6343   static struct
6344   {
6345     int left, right, back;
6346   } turn[] =
6347   {
6348     { 0,        0,              0        },
6349     { MV_DOWN,  MV_UP,          MV_RIGHT },
6350     { MV_UP,    MV_DOWN,        MV_LEFT  },
6351     { 0,        0,              0        },
6352     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6353     { 0,        0,              0        },
6354     { 0,        0,              0        },
6355     { 0,        0,              0        },
6356     { MV_RIGHT, MV_LEFT,        MV_UP    }
6357   };
6358
6359   int element = Feld[x][y];
6360   int move_pattern = element_info[element].move_pattern;
6361
6362   int old_move_dir = MovDir[x][y];
6363   int left_dir  = turn[old_move_dir].left;
6364   int right_dir = turn[old_move_dir].right;
6365   int back_dir  = turn[old_move_dir].back;
6366
6367   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6368   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6369   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6370   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6371
6372   int left_x  = x + left_dx,  left_y  = y + left_dy;
6373   int right_x = x + right_dx, right_y = y + right_dy;
6374   int move_x  = x + move_dx,  move_y  = y + move_dy;
6375
6376   int xx, yy;
6377
6378   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6379   {
6380     TestIfBadThingTouchesOtherBadThing(x, y);
6381
6382     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6383       MovDir[x][y] = right_dir;
6384     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6385       MovDir[x][y] = left_dir;
6386
6387     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6388       MovDelay[x][y] = 9;
6389     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6390       MovDelay[x][y] = 1;
6391   }
6392   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6393   {
6394     TestIfBadThingTouchesOtherBadThing(x, y);
6395
6396     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6397       MovDir[x][y] = left_dir;
6398     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6399       MovDir[x][y] = right_dir;
6400
6401     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6402       MovDelay[x][y] = 9;
6403     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6404       MovDelay[x][y] = 1;
6405   }
6406   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6407   {
6408     TestIfBadThingTouchesOtherBadThing(x, y);
6409
6410     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6411       MovDir[x][y] = left_dir;
6412     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6413       MovDir[x][y] = right_dir;
6414
6415     if (MovDir[x][y] != old_move_dir)
6416       MovDelay[x][y] = 9;
6417   }
6418   else if (element == EL_YAMYAM)
6419   {
6420     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6421     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6422
6423     if (can_turn_left && can_turn_right)
6424       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6425     else if (can_turn_left)
6426       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6427     else if (can_turn_right)
6428       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6429     else
6430       MovDir[x][y] = back_dir;
6431
6432     MovDelay[x][y] = 16 + 16 * RND(3);
6433   }
6434   else if (element == EL_DARK_YAMYAM)
6435   {
6436     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6437                                                          left_x, left_y);
6438     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6439                                                          right_x, right_y);
6440
6441     if (can_turn_left && can_turn_right)
6442       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6443     else if (can_turn_left)
6444       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6445     else if (can_turn_right)
6446       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6447     else
6448       MovDir[x][y] = back_dir;
6449
6450     MovDelay[x][y] = 16 + 16 * RND(3);
6451   }
6452   else if (element == EL_PACMAN)
6453   {
6454     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6455     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6456
6457     if (can_turn_left && can_turn_right)
6458       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6459     else if (can_turn_left)
6460       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6461     else if (can_turn_right)
6462       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6463     else
6464       MovDir[x][y] = back_dir;
6465
6466     MovDelay[x][y] = 6 + RND(40);
6467   }
6468   else if (element == EL_PIG)
6469   {
6470     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6471     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6472     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6473     boolean should_turn_left, should_turn_right, should_move_on;
6474     int rnd_value = 24;
6475     int rnd = RND(rnd_value);
6476
6477     should_turn_left = (can_turn_left &&
6478                         (!can_move_on ||
6479                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6480                                                    y + back_dy + left_dy)));
6481     should_turn_right = (can_turn_right &&
6482                          (!can_move_on ||
6483                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6484                                                     y + back_dy + right_dy)));
6485     should_move_on = (can_move_on &&
6486                       (!can_turn_left ||
6487                        !can_turn_right ||
6488                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6489                                                  y + move_dy + left_dy) ||
6490                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6491                                                  y + move_dy + right_dy)));
6492
6493     if (should_turn_left || should_turn_right || should_move_on)
6494     {
6495       if (should_turn_left && should_turn_right && should_move_on)
6496         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6497                         rnd < 2 * rnd_value / 3 ? right_dir :
6498                         old_move_dir);
6499       else if (should_turn_left && should_turn_right)
6500         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6501       else if (should_turn_left && should_move_on)
6502         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6503       else if (should_turn_right && should_move_on)
6504         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6505       else if (should_turn_left)
6506         MovDir[x][y] = left_dir;
6507       else if (should_turn_right)
6508         MovDir[x][y] = right_dir;
6509       else if (should_move_on)
6510         MovDir[x][y] = old_move_dir;
6511     }
6512     else if (can_move_on && rnd > rnd_value / 8)
6513       MovDir[x][y] = old_move_dir;
6514     else if (can_turn_left && can_turn_right)
6515       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6516     else if (can_turn_left && rnd > rnd_value / 8)
6517       MovDir[x][y] = left_dir;
6518     else if (can_turn_right && rnd > rnd_value/8)
6519       MovDir[x][y] = right_dir;
6520     else
6521       MovDir[x][y] = back_dir;
6522
6523     xx = x + move_xy[MovDir[x][y]].dx;
6524     yy = y + move_xy[MovDir[x][y]].dy;
6525
6526     if (!IN_LEV_FIELD(xx, yy) ||
6527         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6528       MovDir[x][y] = old_move_dir;
6529
6530     MovDelay[x][y] = 0;
6531   }
6532   else if (element == EL_DRAGON)
6533   {
6534     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6535     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6536     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6537     int rnd_value = 24;
6538     int rnd = RND(rnd_value);
6539
6540     if (can_move_on && rnd > rnd_value / 8)
6541       MovDir[x][y] = old_move_dir;
6542     else if (can_turn_left && can_turn_right)
6543       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6544     else if (can_turn_left && rnd > rnd_value / 8)
6545       MovDir[x][y] = left_dir;
6546     else if (can_turn_right && rnd > rnd_value / 8)
6547       MovDir[x][y] = right_dir;
6548     else
6549       MovDir[x][y] = back_dir;
6550
6551     xx = x + move_xy[MovDir[x][y]].dx;
6552     yy = y + move_xy[MovDir[x][y]].dy;
6553
6554     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6555       MovDir[x][y] = old_move_dir;
6556
6557     MovDelay[x][y] = 0;
6558   }
6559   else if (element == EL_MOLE)
6560   {
6561     boolean can_move_on =
6562       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6563                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6564                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6565     if (!can_move_on)
6566     {
6567       boolean can_turn_left =
6568         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6569                               IS_AMOEBOID(Feld[left_x][left_y])));
6570
6571       boolean can_turn_right =
6572         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6573                               IS_AMOEBOID(Feld[right_x][right_y])));
6574
6575       if (can_turn_left && can_turn_right)
6576         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6577       else if (can_turn_left)
6578         MovDir[x][y] = left_dir;
6579       else
6580         MovDir[x][y] = right_dir;
6581     }
6582
6583     if (MovDir[x][y] != old_move_dir)
6584       MovDelay[x][y] = 9;
6585   }
6586   else if (element == EL_BALLOON)
6587   {
6588     MovDir[x][y] = game.wind_direction;
6589     MovDelay[x][y] = 0;
6590   }
6591   else if (element == EL_SPRING)
6592   {
6593     if (MovDir[x][y] & MV_HORIZONTAL)
6594     {
6595       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6596           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6597       {
6598         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6599         ResetGfxAnimation(move_x, move_y);
6600         TEST_DrawLevelField(move_x, move_y);
6601
6602         MovDir[x][y] = back_dir;
6603       }
6604       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6605                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6606         MovDir[x][y] = MV_NONE;
6607     }
6608
6609     MovDelay[x][y] = 0;
6610   }
6611   else if (element == EL_ROBOT ||
6612            element == EL_SATELLITE ||
6613            element == EL_PENGUIN ||
6614            element == EL_EMC_ANDROID)
6615   {
6616     int attr_x = -1, attr_y = -1;
6617
6618     if (AllPlayersGone)
6619     {
6620       attr_x = ExitX;
6621       attr_y = ExitY;
6622     }
6623     else
6624     {
6625       int i;
6626
6627       for (i = 0; i < MAX_PLAYERS; i++)
6628       {
6629         struct PlayerInfo *player = &stored_player[i];
6630         int jx = player->jx, jy = player->jy;
6631
6632         if (!player->active)
6633           continue;
6634
6635         if (attr_x == -1 ||
6636             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6637         {
6638           attr_x = jx;
6639           attr_y = jy;
6640         }
6641       }
6642     }
6643
6644     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6645         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6646          game.engine_version < VERSION_IDENT(3,1,0,0)))
6647     {
6648       attr_x = ZX;
6649       attr_y = ZY;
6650     }
6651
6652     if (element == EL_PENGUIN)
6653     {
6654       int i;
6655       static int xy[4][2] =
6656       {
6657         { 0, -1 },
6658         { -1, 0 },
6659         { +1, 0 },
6660         { 0, +1 }
6661       };
6662
6663       for (i = 0; i < NUM_DIRECTIONS; i++)
6664       {
6665         int ex = x + xy[i][0];
6666         int ey = y + xy[i][1];
6667
6668         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6669                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6670                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6671                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6672         {
6673           attr_x = ex;
6674           attr_y = ey;
6675           break;
6676         }
6677       }
6678     }
6679
6680     MovDir[x][y] = MV_NONE;
6681     if (attr_x < x)
6682       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6683     else if (attr_x > x)
6684       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6685     if (attr_y < y)
6686       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6687     else if (attr_y > y)
6688       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6689
6690     if (element == EL_ROBOT)
6691     {
6692       int newx, newy;
6693
6694       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6695         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6696       Moving2Blocked(x, y, &newx, &newy);
6697
6698       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6699         MovDelay[x][y] = 8 + 8 * !RND(3);
6700       else
6701         MovDelay[x][y] = 16;
6702     }
6703     else if (element == EL_PENGUIN)
6704     {
6705       int newx, newy;
6706
6707       MovDelay[x][y] = 1;
6708
6709       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6710       {
6711         boolean first_horiz = RND(2);
6712         int new_move_dir = MovDir[x][y];
6713
6714         MovDir[x][y] =
6715           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6716         Moving2Blocked(x, y, &newx, &newy);
6717
6718         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6719           return;
6720
6721         MovDir[x][y] =
6722           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6723         Moving2Blocked(x, y, &newx, &newy);
6724
6725         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6726           return;
6727
6728         MovDir[x][y] = old_move_dir;
6729         return;
6730       }
6731     }
6732     else if (element == EL_SATELLITE)
6733     {
6734       int newx, newy;
6735
6736       MovDelay[x][y] = 1;
6737
6738       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6739       {
6740         boolean first_horiz = RND(2);
6741         int new_move_dir = MovDir[x][y];
6742
6743         MovDir[x][y] =
6744           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6745         Moving2Blocked(x, y, &newx, &newy);
6746
6747         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6748           return;
6749
6750         MovDir[x][y] =
6751           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6752         Moving2Blocked(x, y, &newx, &newy);
6753
6754         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6755           return;
6756
6757         MovDir[x][y] = old_move_dir;
6758         return;
6759       }
6760     }
6761     else if (element == EL_EMC_ANDROID)
6762     {
6763       static int check_pos[16] =
6764       {
6765         -1,             /*  0 => (invalid)          */
6766         7,              /*  1 => MV_LEFT            */
6767         3,              /*  2 => MV_RIGHT           */
6768         -1,             /*  3 => (invalid)          */
6769         1,              /*  4 =>            MV_UP   */
6770         0,              /*  5 => MV_LEFT  | MV_UP   */
6771         2,              /*  6 => MV_RIGHT | MV_UP   */
6772         -1,             /*  7 => (invalid)          */
6773         5,              /*  8 =>            MV_DOWN */
6774         6,              /*  9 => MV_LEFT  | MV_DOWN */
6775         4,              /* 10 => MV_RIGHT | MV_DOWN */
6776         -1,             /* 11 => (invalid)          */
6777         -1,             /* 12 => (invalid)          */
6778         -1,             /* 13 => (invalid)          */
6779         -1,             /* 14 => (invalid)          */
6780         -1,             /* 15 => (invalid)          */
6781       };
6782       static struct
6783       {
6784         int dx, dy;
6785         int dir;
6786       } check_xy[8] =
6787       {
6788         { -1, -1,       MV_LEFT  | MV_UP   },
6789         {  0, -1,                  MV_UP   },
6790         { +1, -1,       MV_RIGHT | MV_UP   },
6791         { +1,  0,       MV_RIGHT           },
6792         { +1, +1,       MV_RIGHT | MV_DOWN },
6793         {  0, +1,                  MV_DOWN },
6794         { -1, +1,       MV_LEFT  | MV_DOWN },
6795         { -1,  0,       MV_LEFT            },
6796       };
6797       int start_pos, check_order;
6798       boolean can_clone = FALSE;
6799       int i;
6800
6801       /* check if there is any free field around current position */
6802       for (i = 0; i < 8; i++)
6803       {
6804         int newx = x + check_xy[i].dx;
6805         int newy = y + check_xy[i].dy;
6806
6807         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6808         {
6809           can_clone = TRUE;
6810
6811           break;
6812         }
6813       }
6814
6815       if (can_clone)            /* randomly find an element to clone */
6816       {
6817         can_clone = FALSE;
6818
6819         start_pos = check_pos[RND(8)];
6820         check_order = (RND(2) ? -1 : +1);
6821
6822         for (i = 0; i < 8; i++)
6823         {
6824           int pos_raw = start_pos + i * check_order;
6825           int pos = (pos_raw + 8) % 8;
6826           int newx = x + check_xy[pos].dx;
6827           int newy = y + check_xy[pos].dy;
6828
6829           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6830           {
6831             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6832             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6833
6834             Store[x][y] = Feld[newx][newy];
6835
6836             can_clone = TRUE;
6837
6838             break;
6839           }
6840         }
6841       }
6842
6843       if (can_clone)            /* randomly find a direction to move */
6844       {
6845         can_clone = FALSE;
6846
6847         start_pos = check_pos[RND(8)];
6848         check_order = (RND(2) ? -1 : +1);
6849
6850         for (i = 0; i < 8; i++)
6851         {
6852           int pos_raw = start_pos + i * check_order;
6853           int pos = (pos_raw + 8) % 8;
6854           int newx = x + check_xy[pos].dx;
6855           int newy = y + check_xy[pos].dy;
6856           int new_move_dir = check_xy[pos].dir;
6857
6858           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6859           {
6860             MovDir[x][y] = new_move_dir;
6861             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6862
6863             can_clone = TRUE;
6864
6865             break;
6866           }
6867         }
6868       }
6869
6870       if (can_clone)            /* cloning and moving successful */
6871         return;
6872
6873       /* cannot clone -- try to move towards player */
6874
6875       start_pos = check_pos[MovDir[x][y] & 0x0f];
6876       check_order = (RND(2) ? -1 : +1);
6877
6878       for (i = 0; i < 3; i++)
6879       {
6880         /* first check start_pos, then previous/next or (next/previous) pos */
6881         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6882         int pos = (pos_raw + 8) % 8;
6883         int newx = x + check_xy[pos].dx;
6884         int newy = y + check_xy[pos].dy;
6885         int new_move_dir = check_xy[pos].dir;
6886
6887         if (IS_PLAYER(newx, newy))
6888           break;
6889
6890         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6891         {
6892           MovDir[x][y] = new_move_dir;
6893           MovDelay[x][y] = level.android_move_time * 8 + 1;
6894
6895           break;
6896         }
6897       }
6898     }
6899   }
6900   else if (move_pattern == MV_TURNING_LEFT ||
6901            move_pattern == MV_TURNING_RIGHT ||
6902            move_pattern == MV_TURNING_LEFT_RIGHT ||
6903            move_pattern == MV_TURNING_RIGHT_LEFT ||
6904            move_pattern == MV_TURNING_RANDOM ||
6905            move_pattern == MV_ALL_DIRECTIONS)
6906   {
6907     boolean can_turn_left =
6908       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6909     boolean can_turn_right =
6910       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6911
6912     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6913       return;
6914
6915     if (move_pattern == MV_TURNING_LEFT)
6916       MovDir[x][y] = left_dir;
6917     else if (move_pattern == MV_TURNING_RIGHT)
6918       MovDir[x][y] = right_dir;
6919     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6920       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6921     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6922       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6923     else if (move_pattern == MV_TURNING_RANDOM)
6924       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6925                       can_turn_right && !can_turn_left ? right_dir :
6926                       RND(2) ? left_dir : right_dir);
6927     else if (can_turn_left && can_turn_right)
6928       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6929     else if (can_turn_left)
6930       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6931     else if (can_turn_right)
6932       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6933     else
6934       MovDir[x][y] = back_dir;
6935
6936     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6937   }
6938   else if (move_pattern == MV_HORIZONTAL ||
6939            move_pattern == MV_VERTICAL)
6940   {
6941     if (move_pattern & old_move_dir)
6942       MovDir[x][y] = back_dir;
6943     else if (move_pattern == MV_HORIZONTAL)
6944       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6945     else if (move_pattern == MV_VERTICAL)
6946       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6947
6948     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6949   }
6950   else if (move_pattern & MV_ANY_DIRECTION)
6951   {
6952     MovDir[x][y] = move_pattern;
6953     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6954   }
6955   else if (move_pattern & MV_WIND_DIRECTION)
6956   {
6957     MovDir[x][y] = game.wind_direction;
6958     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6959   }
6960   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6961   {
6962     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6963       MovDir[x][y] = left_dir;
6964     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6965       MovDir[x][y] = right_dir;
6966
6967     if (MovDir[x][y] != old_move_dir)
6968       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6969   }
6970   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6971   {
6972     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6973       MovDir[x][y] = right_dir;
6974     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6975       MovDir[x][y] = left_dir;
6976
6977     if (MovDir[x][y] != old_move_dir)
6978       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6979   }
6980   else if (move_pattern == MV_TOWARDS_PLAYER ||
6981            move_pattern == MV_AWAY_FROM_PLAYER)
6982   {
6983     int attr_x = -1, attr_y = -1;
6984     int newx, newy;
6985     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6986
6987     if (AllPlayersGone)
6988     {
6989       attr_x = ExitX;
6990       attr_y = ExitY;
6991     }
6992     else
6993     {
6994       int i;
6995
6996       for (i = 0; i < MAX_PLAYERS; i++)
6997       {
6998         struct PlayerInfo *player = &stored_player[i];
6999         int jx = player->jx, jy = player->jy;
7000
7001         if (!player->active)
7002           continue;
7003
7004         if (attr_x == -1 ||
7005             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7006         {
7007           attr_x = jx;
7008           attr_y = jy;
7009         }
7010       }
7011     }
7012
7013     MovDir[x][y] = MV_NONE;
7014     if (attr_x < x)
7015       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7016     else if (attr_x > x)
7017       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7018     if (attr_y < y)
7019       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7020     else if (attr_y > y)
7021       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7022
7023     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7024
7025     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7026     {
7027       boolean first_horiz = RND(2);
7028       int new_move_dir = MovDir[x][y];
7029
7030       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7031       {
7032         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7033         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7034
7035         return;
7036       }
7037
7038       MovDir[x][y] =
7039         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7040       Moving2Blocked(x, y, &newx, &newy);
7041
7042       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7043         return;
7044
7045       MovDir[x][y] =
7046         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7047       Moving2Blocked(x, y, &newx, &newy);
7048
7049       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7050         return;
7051
7052       MovDir[x][y] = old_move_dir;
7053     }
7054   }
7055   else if (move_pattern == MV_WHEN_PUSHED ||
7056            move_pattern == MV_WHEN_DROPPED)
7057   {
7058     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7059       MovDir[x][y] = MV_NONE;
7060
7061     MovDelay[x][y] = 0;
7062   }
7063   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7064   {
7065     static int test_xy[7][2] =
7066     {
7067       { 0, -1 },
7068       { -1, 0 },
7069       { +1, 0 },
7070       { 0, +1 },
7071       { 0, -1 },
7072       { -1, 0 },
7073       { +1, 0 },
7074     };
7075     static int test_dir[7] =
7076     {
7077       MV_UP,
7078       MV_LEFT,
7079       MV_RIGHT,
7080       MV_DOWN,
7081       MV_UP,
7082       MV_LEFT,
7083       MV_RIGHT,
7084     };
7085     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7086     int move_preference = -1000000;     /* start with very low preference */
7087     int new_move_dir = MV_NONE;
7088     int start_test = RND(4);
7089     int i;
7090
7091     for (i = 0; i < NUM_DIRECTIONS; i++)
7092     {
7093       int move_dir = test_dir[start_test + i];
7094       int move_dir_preference;
7095
7096       xx = x + test_xy[start_test + i][0];
7097       yy = y + test_xy[start_test + i][1];
7098
7099       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7100           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7101       {
7102         new_move_dir = move_dir;
7103
7104         break;
7105       }
7106
7107       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7108         continue;
7109
7110       move_dir_preference = -1 * RunnerVisit[xx][yy];
7111       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7112         move_dir_preference = PlayerVisit[xx][yy];
7113
7114       if (move_dir_preference > move_preference)
7115       {
7116         /* prefer field that has not been visited for the longest time */
7117         move_preference = move_dir_preference;
7118         new_move_dir = move_dir;
7119       }
7120       else if (move_dir_preference == move_preference &&
7121                move_dir == old_move_dir)
7122       {
7123         /* prefer last direction when all directions are preferred equally */
7124         move_preference = move_dir_preference;
7125         new_move_dir = move_dir;
7126       }
7127     }
7128
7129     MovDir[x][y] = new_move_dir;
7130     if (old_move_dir != new_move_dir)
7131       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7132   }
7133 }
7134
7135 static void TurnRound(int x, int y)
7136 {
7137   int direction = MovDir[x][y];
7138
7139   TurnRoundExt(x, y);
7140
7141   GfxDir[x][y] = MovDir[x][y];
7142
7143   if (direction != MovDir[x][y])
7144     GfxFrame[x][y] = 0;
7145
7146   if (MovDelay[x][y])
7147     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7148
7149   ResetGfxFrame(x, y, FALSE);
7150 }
7151
7152 static boolean JustBeingPushed(int x, int y)
7153 {
7154   int i;
7155
7156   for (i = 0; i < MAX_PLAYERS; i++)
7157   {
7158     struct PlayerInfo *player = &stored_player[i];
7159
7160     if (player->active && player->is_pushing && player->MovPos)
7161     {
7162       int next_jx = player->jx + (player->jx - player->last_jx);
7163       int next_jy = player->jy + (player->jy - player->last_jy);
7164
7165       if (x == next_jx && y == next_jy)
7166         return TRUE;
7167     }
7168   }
7169
7170   return FALSE;
7171 }
7172
7173 void StartMoving(int x, int y)
7174 {
7175   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7176   int element = Feld[x][y];
7177
7178   if (Stop[x][y])
7179     return;
7180
7181   if (MovDelay[x][y] == 0)
7182     GfxAction[x][y] = ACTION_DEFAULT;
7183
7184   if (CAN_FALL(element) && y < lev_fieldy - 1)
7185   {
7186     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7187         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7188       if (JustBeingPushed(x, y))
7189         return;
7190
7191     if (element == EL_QUICKSAND_FULL)
7192     {
7193       if (IS_FREE(x, y + 1))
7194       {
7195         InitMovingField(x, y, MV_DOWN);
7196         started_moving = TRUE;
7197
7198         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7199 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7200         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7201           Store[x][y] = EL_ROCK;
7202 #else
7203         Store[x][y] = EL_ROCK;
7204 #endif
7205
7206         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7207       }
7208       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7209       {
7210         if (!MovDelay[x][y])
7211         {
7212           MovDelay[x][y] = TILEY + 1;
7213
7214           ResetGfxAnimation(x, y);
7215           ResetGfxAnimation(x, y + 1);
7216         }
7217
7218         if (MovDelay[x][y])
7219         {
7220           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7221           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7222
7223           MovDelay[x][y]--;
7224           if (MovDelay[x][y])
7225             return;
7226         }
7227
7228         Feld[x][y] = EL_QUICKSAND_EMPTY;
7229         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7230         Store[x][y + 1] = Store[x][y];
7231         Store[x][y] = 0;
7232
7233         PlayLevelSoundAction(x, y, ACTION_FILLING);
7234       }
7235       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7236       {
7237         if (!MovDelay[x][y])
7238         {
7239           MovDelay[x][y] = TILEY + 1;
7240
7241           ResetGfxAnimation(x, y);
7242           ResetGfxAnimation(x, y + 1);
7243         }
7244
7245         if (MovDelay[x][y])
7246         {
7247           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7248           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7249
7250           MovDelay[x][y]--;
7251           if (MovDelay[x][y])
7252             return;
7253         }
7254
7255         Feld[x][y] = EL_QUICKSAND_EMPTY;
7256         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7257         Store[x][y + 1] = Store[x][y];
7258         Store[x][y] = 0;
7259
7260         PlayLevelSoundAction(x, y, ACTION_FILLING);
7261       }
7262     }
7263     else if (element == EL_QUICKSAND_FAST_FULL)
7264     {
7265       if (IS_FREE(x, y + 1))
7266       {
7267         InitMovingField(x, y, MV_DOWN);
7268         started_moving = TRUE;
7269
7270         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7271 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7272         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7273           Store[x][y] = EL_ROCK;
7274 #else
7275         Store[x][y] = EL_ROCK;
7276 #endif
7277
7278         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7279       }
7280       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7281       {
7282         if (!MovDelay[x][y])
7283         {
7284           MovDelay[x][y] = TILEY + 1;
7285
7286           ResetGfxAnimation(x, y);
7287           ResetGfxAnimation(x, y + 1);
7288         }
7289
7290         if (MovDelay[x][y])
7291         {
7292           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7293           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7294
7295           MovDelay[x][y]--;
7296           if (MovDelay[x][y])
7297             return;
7298         }
7299
7300         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7301         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7302         Store[x][y + 1] = Store[x][y];
7303         Store[x][y] = 0;
7304
7305         PlayLevelSoundAction(x, y, ACTION_FILLING);
7306       }
7307       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7308       {
7309         if (!MovDelay[x][y])
7310         {
7311           MovDelay[x][y] = TILEY + 1;
7312
7313           ResetGfxAnimation(x, y);
7314           ResetGfxAnimation(x, y + 1);
7315         }
7316
7317         if (MovDelay[x][y])
7318         {
7319           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7320           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7321
7322           MovDelay[x][y]--;
7323           if (MovDelay[x][y])
7324             return;
7325         }
7326
7327         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7328         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7329         Store[x][y + 1] = Store[x][y];
7330         Store[x][y] = 0;
7331
7332         PlayLevelSoundAction(x, y, ACTION_FILLING);
7333       }
7334     }
7335     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7336              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7337     {
7338       InitMovingField(x, y, MV_DOWN);
7339       started_moving = TRUE;
7340
7341       Feld[x][y] = EL_QUICKSAND_FILLING;
7342       Store[x][y] = element;
7343
7344       PlayLevelSoundAction(x, y, ACTION_FILLING);
7345     }
7346     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7347              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7348     {
7349       InitMovingField(x, y, MV_DOWN);
7350       started_moving = TRUE;
7351
7352       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7353       Store[x][y] = element;
7354
7355       PlayLevelSoundAction(x, y, ACTION_FILLING);
7356     }
7357     else if (element == EL_MAGIC_WALL_FULL)
7358     {
7359       if (IS_FREE(x, y + 1))
7360       {
7361         InitMovingField(x, y, MV_DOWN);
7362         started_moving = TRUE;
7363
7364         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7365         Store[x][y] = EL_CHANGED(Store[x][y]);
7366       }
7367       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7368       {
7369         if (!MovDelay[x][y])
7370           MovDelay[x][y] = TILEY / 4 + 1;
7371
7372         if (MovDelay[x][y])
7373         {
7374           MovDelay[x][y]--;
7375           if (MovDelay[x][y])
7376             return;
7377         }
7378
7379         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7380         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7381         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7382         Store[x][y] = 0;
7383       }
7384     }
7385     else if (element == EL_BD_MAGIC_WALL_FULL)
7386     {
7387       if (IS_FREE(x, y + 1))
7388       {
7389         InitMovingField(x, y, MV_DOWN);
7390         started_moving = TRUE;
7391
7392         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7393         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7394       }
7395       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7396       {
7397         if (!MovDelay[x][y])
7398           MovDelay[x][y] = TILEY / 4 + 1;
7399
7400         if (MovDelay[x][y])
7401         {
7402           MovDelay[x][y]--;
7403           if (MovDelay[x][y])
7404             return;
7405         }
7406
7407         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7408         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7409         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7410         Store[x][y] = 0;
7411       }
7412     }
7413     else if (element == EL_DC_MAGIC_WALL_FULL)
7414     {
7415       if (IS_FREE(x, y + 1))
7416       {
7417         InitMovingField(x, y, MV_DOWN);
7418         started_moving = TRUE;
7419
7420         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7421         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7422       }
7423       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7424       {
7425         if (!MovDelay[x][y])
7426           MovDelay[x][y] = TILEY / 4 + 1;
7427
7428         if (MovDelay[x][y])
7429         {
7430           MovDelay[x][y]--;
7431           if (MovDelay[x][y])
7432             return;
7433         }
7434
7435         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7436         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7437         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7438         Store[x][y] = 0;
7439       }
7440     }
7441     else if ((CAN_PASS_MAGIC_WALL(element) &&
7442               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7443                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7444              (CAN_PASS_DC_MAGIC_WALL(element) &&
7445               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7446
7447     {
7448       InitMovingField(x, y, MV_DOWN);
7449       started_moving = TRUE;
7450
7451       Feld[x][y] =
7452         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7453          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7454          EL_DC_MAGIC_WALL_FILLING);
7455       Store[x][y] = element;
7456     }
7457     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7458     {
7459       SplashAcid(x, y + 1);
7460
7461       InitMovingField(x, y, MV_DOWN);
7462       started_moving = TRUE;
7463
7464       Store[x][y] = EL_ACID;
7465     }
7466     else if (
7467              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7468               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7469              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7470               CAN_FALL(element) && WasJustFalling[x][y] &&
7471               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7472
7473              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7474               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7475               (Feld[x][y + 1] == EL_BLOCKED)))
7476     {
7477       /* this is needed for a special case not covered by calling "Impact()"
7478          from "ContinueMoving()": if an element moves to a tile directly below
7479          another element which was just falling on that tile (which was empty
7480          in the previous frame), the falling element above would just stop
7481          instead of smashing the element below (in previous version, the above
7482          element was just checked for "moving" instead of "falling", resulting
7483          in incorrect smashes caused by horizontal movement of the above
7484          element; also, the case of the player being the element to smash was
7485          simply not covered here... :-/ ) */
7486
7487       CheckCollision[x][y] = 0;
7488       CheckImpact[x][y] = 0;
7489
7490       Impact(x, y);
7491     }
7492     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7493     {
7494       if (MovDir[x][y] == MV_NONE)
7495       {
7496         InitMovingField(x, y, MV_DOWN);
7497         started_moving = TRUE;
7498       }
7499     }
7500     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7501     {
7502       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7503         MovDir[x][y] = MV_DOWN;
7504
7505       InitMovingField(x, y, MV_DOWN);
7506       started_moving = TRUE;
7507     }
7508     else if (element == EL_AMOEBA_DROP)
7509     {
7510       Feld[x][y] = EL_AMOEBA_GROWING;
7511       Store[x][y] = EL_AMOEBA_WET;
7512     }
7513     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7514               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7515              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7516              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7517     {
7518       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7519                                 (IS_FREE(x - 1, y + 1) ||
7520                                  Feld[x - 1][y + 1] == EL_ACID));
7521       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7522                                 (IS_FREE(x + 1, y + 1) ||
7523                                  Feld[x + 1][y + 1] == EL_ACID));
7524       boolean can_fall_any  = (can_fall_left || can_fall_right);
7525       boolean can_fall_both = (can_fall_left && can_fall_right);
7526       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7527
7528       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7529       {
7530         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7531           can_fall_right = FALSE;
7532         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7533           can_fall_left = FALSE;
7534         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7535           can_fall_right = FALSE;
7536         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7537           can_fall_left = FALSE;
7538
7539         can_fall_any  = (can_fall_left || can_fall_right);
7540         can_fall_both = FALSE;
7541       }
7542
7543       if (can_fall_both)
7544       {
7545         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7546           can_fall_right = FALSE;       /* slip down on left side */
7547         else
7548           can_fall_left = !(can_fall_right = RND(2));
7549
7550         can_fall_both = FALSE;
7551       }
7552
7553       if (can_fall_any)
7554       {
7555         /* if not determined otherwise, prefer left side for slipping down */
7556         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7557         started_moving = TRUE;
7558       }
7559     }
7560     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7561     {
7562       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7563       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7564       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7565       int belt_dir = game.belt_dir[belt_nr];
7566
7567       if ((belt_dir == MV_LEFT  && left_is_free) ||
7568           (belt_dir == MV_RIGHT && right_is_free))
7569       {
7570         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7571
7572         InitMovingField(x, y, belt_dir);
7573         started_moving = TRUE;
7574
7575         Pushed[x][y] = TRUE;
7576         Pushed[nextx][y] = TRUE;
7577
7578         GfxAction[x][y] = ACTION_DEFAULT;
7579       }
7580       else
7581       {
7582         MovDir[x][y] = 0;       /* if element was moving, stop it */
7583       }
7584     }
7585   }
7586
7587   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7588   if (CAN_MOVE(element) && !started_moving)
7589   {
7590     int move_pattern = element_info[element].move_pattern;
7591     int newx, newy;
7592
7593     Moving2Blocked(x, y, &newx, &newy);
7594
7595     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7596       return;
7597
7598     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7599         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7600     {
7601       WasJustMoving[x][y] = 0;
7602       CheckCollision[x][y] = 0;
7603
7604       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7605
7606       if (Feld[x][y] != element)        /* element has changed */
7607         return;
7608     }
7609
7610     if (!MovDelay[x][y])        /* start new movement phase */
7611     {
7612       /* all objects that can change their move direction after each step
7613          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7614
7615       if (element != EL_YAMYAM &&
7616           element != EL_DARK_YAMYAM &&
7617           element != EL_PACMAN &&
7618           !(move_pattern & MV_ANY_DIRECTION) &&
7619           move_pattern != MV_TURNING_LEFT &&
7620           move_pattern != MV_TURNING_RIGHT &&
7621           move_pattern != MV_TURNING_LEFT_RIGHT &&
7622           move_pattern != MV_TURNING_RIGHT_LEFT &&
7623           move_pattern != MV_TURNING_RANDOM)
7624       {
7625         TurnRound(x, y);
7626
7627         if (MovDelay[x][y] && (element == EL_BUG ||
7628                                element == EL_SPACESHIP ||
7629                                element == EL_SP_SNIKSNAK ||
7630                                element == EL_SP_ELECTRON ||
7631                                element == EL_MOLE))
7632           TEST_DrawLevelField(x, y);
7633       }
7634     }
7635
7636     if (MovDelay[x][y])         /* wait some time before next movement */
7637     {
7638       MovDelay[x][y]--;
7639
7640       if (element == EL_ROBOT ||
7641           element == EL_YAMYAM ||
7642           element == EL_DARK_YAMYAM)
7643       {
7644         DrawLevelElementAnimationIfNeeded(x, y, element);
7645         PlayLevelSoundAction(x, y, ACTION_WAITING);
7646       }
7647       else if (element == EL_SP_ELECTRON)
7648         DrawLevelElementAnimationIfNeeded(x, y, element);
7649       else if (element == EL_DRAGON)
7650       {
7651         int i;
7652         int dir = MovDir[x][y];
7653         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7654         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7655         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7656                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7657                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7658                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7659         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7660
7661         GfxAction[x][y] = ACTION_ATTACKING;
7662
7663         if (IS_PLAYER(x, y))
7664           DrawPlayerField(x, y);
7665         else
7666           TEST_DrawLevelField(x, y);
7667
7668         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7669
7670         for (i = 1; i <= 3; i++)
7671         {
7672           int xx = x + i * dx;
7673           int yy = y + i * dy;
7674           int sx = SCREENX(xx);
7675           int sy = SCREENY(yy);
7676           int flame_graphic = graphic + (i - 1);
7677
7678           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7679             break;
7680
7681           if (MovDelay[x][y])
7682           {
7683             int flamed = MovingOrBlocked2Element(xx, yy);
7684
7685             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7686               Bang(xx, yy);
7687             else
7688               RemoveMovingField(xx, yy);
7689
7690             ChangeDelay[xx][yy] = 0;
7691
7692             Feld[xx][yy] = EL_FLAMES;
7693
7694             if (IN_SCR_FIELD(sx, sy))
7695             {
7696               TEST_DrawLevelFieldCrumbled(xx, yy);
7697               DrawGraphic(sx, sy, flame_graphic, frame);
7698             }
7699           }
7700           else
7701           {
7702             if (Feld[xx][yy] == EL_FLAMES)
7703               Feld[xx][yy] = EL_EMPTY;
7704             TEST_DrawLevelField(xx, yy);
7705           }
7706         }
7707       }
7708
7709       if (MovDelay[x][y])       /* element still has to wait some time */
7710       {
7711         PlayLevelSoundAction(x, y, ACTION_WAITING);
7712
7713         return;
7714       }
7715     }
7716
7717     /* now make next step */
7718
7719     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7720
7721     if (DONT_COLLIDE_WITH(element) &&
7722         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7723         !PLAYER_ENEMY_PROTECTED(newx, newy))
7724     {
7725       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7726
7727       return;
7728     }
7729
7730     else if (CAN_MOVE_INTO_ACID(element) &&
7731              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7732              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7733              (MovDir[x][y] == MV_DOWN ||
7734               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7735     {
7736       SplashAcid(newx, newy);
7737       Store[x][y] = EL_ACID;
7738     }
7739     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7740     {
7741       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7742           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7743           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7744           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7745       {
7746         RemoveField(x, y);
7747         TEST_DrawLevelField(x, y);
7748
7749         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7750         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7751           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7752
7753         local_player->friends_still_needed--;
7754         if (!local_player->friends_still_needed &&
7755             !local_player->GameOver && AllPlayersGone)
7756           PlayerWins(local_player);
7757
7758         return;
7759       }
7760       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7761       {
7762         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7763           TEST_DrawLevelField(newx, newy);
7764         else
7765           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7766       }
7767       else if (!IS_FREE(newx, newy))
7768       {
7769         GfxAction[x][y] = ACTION_WAITING;
7770
7771         if (IS_PLAYER(x, y))
7772           DrawPlayerField(x, y);
7773         else
7774           TEST_DrawLevelField(x, y);
7775
7776         return;
7777       }
7778     }
7779     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7780     {
7781       if (IS_FOOD_PIG(Feld[newx][newy]))
7782       {
7783         if (IS_MOVING(newx, newy))
7784           RemoveMovingField(newx, newy);
7785         else
7786         {
7787           Feld[newx][newy] = EL_EMPTY;
7788           TEST_DrawLevelField(newx, newy);
7789         }
7790
7791         PlayLevelSound(x, y, SND_PIG_DIGGING);
7792       }
7793       else if (!IS_FREE(newx, newy))
7794       {
7795         if (IS_PLAYER(x, y))
7796           DrawPlayerField(x, y);
7797         else
7798           TEST_DrawLevelField(x, y);
7799
7800         return;
7801       }
7802     }
7803     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7804     {
7805       if (Store[x][y] != EL_EMPTY)
7806       {
7807         boolean can_clone = FALSE;
7808         int xx, yy;
7809
7810         /* check if element to clone is still there */
7811         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7812         {
7813           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7814           {
7815             can_clone = TRUE;
7816
7817             break;
7818           }
7819         }
7820
7821         /* cannot clone or target field not free anymore -- do not clone */
7822         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7823           Store[x][y] = EL_EMPTY;
7824       }
7825
7826       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7827       {
7828         if (IS_MV_DIAGONAL(MovDir[x][y]))
7829         {
7830           int diagonal_move_dir = MovDir[x][y];
7831           int stored = Store[x][y];
7832           int change_delay = 8;
7833           int graphic;
7834
7835           /* android is moving diagonally */
7836
7837           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7838
7839           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7840           GfxElement[x][y] = EL_EMC_ANDROID;
7841           GfxAction[x][y] = ACTION_SHRINKING;
7842           GfxDir[x][y] = diagonal_move_dir;
7843           ChangeDelay[x][y] = change_delay;
7844
7845           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7846                                    GfxDir[x][y]);
7847
7848           DrawLevelGraphicAnimation(x, y, graphic);
7849           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7850
7851           if (Feld[newx][newy] == EL_ACID)
7852           {
7853             SplashAcid(newx, newy);
7854
7855             return;
7856           }
7857
7858           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7859
7860           Store[newx][newy] = EL_EMC_ANDROID;
7861           GfxElement[newx][newy] = EL_EMC_ANDROID;
7862           GfxAction[newx][newy] = ACTION_GROWING;
7863           GfxDir[newx][newy] = diagonal_move_dir;
7864           ChangeDelay[newx][newy] = change_delay;
7865
7866           graphic = el_act_dir2img(GfxElement[newx][newy],
7867                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7868
7869           DrawLevelGraphicAnimation(newx, newy, graphic);
7870           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7871
7872           return;
7873         }
7874         else
7875         {
7876           Feld[newx][newy] = EL_EMPTY;
7877           TEST_DrawLevelField(newx, newy);
7878
7879           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7880         }
7881       }
7882       else if (!IS_FREE(newx, newy))
7883       {
7884         return;
7885       }
7886     }
7887     else if (IS_CUSTOM_ELEMENT(element) &&
7888              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7889     {
7890       if (!DigFieldByCE(newx, newy, element))
7891         return;
7892
7893       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7894       {
7895         RunnerVisit[x][y] = FrameCounter;
7896         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7897       }
7898     }
7899     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7900     {
7901       if (!IS_FREE(newx, newy))
7902       {
7903         if (IS_PLAYER(x, y))
7904           DrawPlayerField(x, y);
7905         else
7906           TEST_DrawLevelField(x, y);
7907
7908         return;
7909       }
7910       else
7911       {
7912         boolean wanna_flame = !RND(10);
7913         int dx = newx - x, dy = newy - y;
7914         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7915         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7916         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7917                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7918         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7919                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7920
7921         if ((wanna_flame ||
7922              IS_CLASSIC_ENEMY(element1) ||
7923              IS_CLASSIC_ENEMY(element2)) &&
7924             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7925             element1 != EL_FLAMES && element2 != EL_FLAMES)
7926         {
7927           ResetGfxAnimation(x, y);
7928           GfxAction[x][y] = ACTION_ATTACKING;
7929
7930           if (IS_PLAYER(x, y))
7931             DrawPlayerField(x, y);
7932           else
7933             TEST_DrawLevelField(x, y);
7934
7935           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7936
7937           MovDelay[x][y] = 50;
7938
7939           Feld[newx][newy] = EL_FLAMES;
7940           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7941             Feld[newx1][newy1] = EL_FLAMES;
7942           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7943             Feld[newx2][newy2] = EL_FLAMES;
7944
7945           return;
7946         }
7947       }
7948     }
7949     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7950              Feld[newx][newy] == EL_DIAMOND)
7951     {
7952       if (IS_MOVING(newx, newy))
7953         RemoveMovingField(newx, newy);
7954       else
7955       {
7956         Feld[newx][newy] = EL_EMPTY;
7957         TEST_DrawLevelField(newx, newy);
7958       }
7959
7960       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7961     }
7962     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7963              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7964     {
7965       if (AmoebaNr[newx][newy])
7966       {
7967         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7968         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7969             Feld[newx][newy] == EL_BD_AMOEBA)
7970           AmoebaCnt[AmoebaNr[newx][newy]]--;
7971       }
7972
7973       if (IS_MOVING(newx, newy))
7974       {
7975         RemoveMovingField(newx, newy);
7976       }
7977       else
7978       {
7979         Feld[newx][newy] = EL_EMPTY;
7980         TEST_DrawLevelField(newx, newy);
7981       }
7982
7983       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7984     }
7985     else if ((element == EL_PACMAN || element == EL_MOLE)
7986              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7987     {
7988       if (AmoebaNr[newx][newy])
7989       {
7990         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7991         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7992             Feld[newx][newy] == EL_BD_AMOEBA)
7993           AmoebaCnt[AmoebaNr[newx][newy]]--;
7994       }
7995
7996       if (element == EL_MOLE)
7997       {
7998         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7999         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8000
8001         ResetGfxAnimation(x, y);
8002         GfxAction[x][y] = ACTION_DIGGING;
8003         TEST_DrawLevelField(x, y);
8004
8005         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8006
8007         return;                         /* wait for shrinking amoeba */
8008       }
8009       else      /* element == EL_PACMAN */
8010       {
8011         Feld[newx][newy] = EL_EMPTY;
8012         TEST_DrawLevelField(newx, newy);
8013         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8014       }
8015     }
8016     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8017              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8018               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8019     {
8020       /* wait for shrinking amoeba to completely disappear */
8021       return;
8022     }
8023     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8024     {
8025       /* object was running against a wall */
8026
8027       TurnRound(x, y);
8028
8029       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8030         DrawLevelElementAnimation(x, y, element);
8031
8032       if (DONT_TOUCH(element))
8033         TestIfBadThingTouchesPlayer(x, y);
8034
8035       return;
8036     }
8037
8038     InitMovingField(x, y, MovDir[x][y]);
8039
8040     PlayLevelSoundAction(x, y, ACTION_MOVING);
8041   }
8042
8043   if (MovDir[x][y])
8044     ContinueMoving(x, y);
8045 }
8046
8047 void ContinueMoving(int x, int y)
8048 {
8049   int element = Feld[x][y];
8050   struct ElementInfo *ei = &element_info[element];
8051   int direction = MovDir[x][y];
8052   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8053   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8054   int newx = x + dx, newy = y + dy;
8055   int stored = Store[x][y];
8056   int stored_new = Store[newx][newy];
8057   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8058   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8059   boolean last_line = (newy == lev_fieldy - 1);
8060
8061   MovPos[x][y] += getElementMoveStepsize(x, y);
8062
8063   if (pushed_by_player) /* special case: moving object pushed by player */
8064     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8065
8066   if (ABS(MovPos[x][y]) < TILEX)
8067   {
8068     TEST_DrawLevelField(x, y);
8069
8070     return;     /* element is still moving */
8071   }
8072
8073   /* element reached destination field */
8074
8075   Feld[x][y] = EL_EMPTY;
8076   Feld[newx][newy] = element;
8077   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8078
8079   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8080   {
8081     element = Feld[newx][newy] = EL_ACID;
8082   }
8083   else if (element == EL_MOLE)
8084   {
8085     Feld[x][y] = EL_SAND;
8086
8087     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8088   }
8089   else if (element == EL_QUICKSAND_FILLING)
8090   {
8091     element = Feld[newx][newy] = get_next_element(element);
8092     Store[newx][newy] = Store[x][y];
8093   }
8094   else if (element == EL_QUICKSAND_EMPTYING)
8095   {
8096     Feld[x][y] = get_next_element(element);
8097     element = Feld[newx][newy] = Store[x][y];
8098   }
8099   else if (element == EL_QUICKSAND_FAST_FILLING)
8100   {
8101     element = Feld[newx][newy] = get_next_element(element);
8102     Store[newx][newy] = Store[x][y];
8103   }
8104   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8105   {
8106     Feld[x][y] = get_next_element(element);
8107     element = Feld[newx][newy] = Store[x][y];
8108   }
8109   else if (element == EL_MAGIC_WALL_FILLING)
8110   {
8111     element = Feld[newx][newy] = get_next_element(element);
8112     if (!game.magic_wall_active)
8113       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8114     Store[newx][newy] = Store[x][y];
8115   }
8116   else if (element == EL_MAGIC_WALL_EMPTYING)
8117   {
8118     Feld[x][y] = get_next_element(element);
8119     if (!game.magic_wall_active)
8120       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8121     element = Feld[newx][newy] = Store[x][y];
8122
8123     InitField(newx, newy, FALSE);
8124   }
8125   else if (element == EL_BD_MAGIC_WALL_FILLING)
8126   {
8127     element = Feld[newx][newy] = get_next_element(element);
8128     if (!game.magic_wall_active)
8129       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8130     Store[newx][newy] = Store[x][y];
8131   }
8132   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8133   {
8134     Feld[x][y] = get_next_element(element);
8135     if (!game.magic_wall_active)
8136       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8137     element = Feld[newx][newy] = Store[x][y];
8138
8139     InitField(newx, newy, FALSE);
8140   }
8141   else if (element == EL_DC_MAGIC_WALL_FILLING)
8142   {
8143     element = Feld[newx][newy] = get_next_element(element);
8144     if (!game.magic_wall_active)
8145       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8146     Store[newx][newy] = Store[x][y];
8147   }
8148   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8149   {
8150     Feld[x][y] = get_next_element(element);
8151     if (!game.magic_wall_active)
8152       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8153     element = Feld[newx][newy] = Store[x][y];
8154
8155     InitField(newx, newy, FALSE);
8156   }
8157   else if (element == EL_AMOEBA_DROPPING)
8158   {
8159     Feld[x][y] = get_next_element(element);
8160     element = Feld[newx][newy] = Store[x][y];
8161   }
8162   else if (element == EL_SOKOBAN_OBJECT)
8163   {
8164     if (Back[x][y])
8165       Feld[x][y] = Back[x][y];
8166
8167     if (Back[newx][newy])
8168       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8169
8170     Back[x][y] = Back[newx][newy] = 0;
8171   }
8172
8173   Store[x][y] = EL_EMPTY;
8174   MovPos[x][y] = 0;
8175   MovDir[x][y] = 0;
8176   MovDelay[x][y] = 0;
8177
8178   MovDelay[newx][newy] = 0;
8179
8180   if (CAN_CHANGE_OR_HAS_ACTION(element))
8181   {
8182     /* copy element change control values to new field */
8183     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8184     ChangePage[newx][newy]  = ChangePage[x][y];
8185     ChangeCount[newx][newy] = ChangeCount[x][y];
8186     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8187   }
8188
8189   CustomValue[newx][newy] = CustomValue[x][y];
8190
8191   ChangeDelay[x][y] = 0;
8192   ChangePage[x][y] = -1;
8193   ChangeCount[x][y] = 0;
8194   ChangeEvent[x][y] = -1;
8195
8196   CustomValue[x][y] = 0;
8197
8198   /* copy animation control values to new field */
8199   GfxFrame[newx][newy]  = GfxFrame[x][y];
8200   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8201   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8202   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8203
8204   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8205
8206   /* some elements can leave other elements behind after moving */
8207   if (ei->move_leave_element != EL_EMPTY &&
8208       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8209       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8210   {
8211     int move_leave_element = ei->move_leave_element;
8212
8213     /* this makes it possible to leave the removed element again */
8214     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8215       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8216
8217     Feld[x][y] = move_leave_element;
8218
8219     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8220       MovDir[x][y] = direction;
8221
8222     InitField(x, y, FALSE);
8223
8224     if (GFX_CRUMBLED(Feld[x][y]))
8225       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8226
8227     if (ELEM_IS_PLAYER(move_leave_element))
8228       RelocatePlayer(x, y, move_leave_element);
8229   }
8230
8231   /* do this after checking for left-behind element */
8232   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8233
8234   if (!CAN_MOVE(element) ||
8235       (CAN_FALL(element) && direction == MV_DOWN &&
8236        (element == EL_SPRING ||
8237         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8238         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8239     GfxDir[x][y] = MovDir[newx][newy] = 0;
8240
8241   TEST_DrawLevelField(x, y);
8242   TEST_DrawLevelField(newx, newy);
8243
8244   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8245
8246   /* prevent pushed element from moving on in pushed direction */
8247   if (pushed_by_player && CAN_MOVE(element) &&
8248       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8249       !(element_info[element].move_pattern & direction))
8250     TurnRound(newx, newy);
8251
8252   /* prevent elements on conveyor belt from moving on in last direction */
8253   if (pushed_by_conveyor && CAN_FALL(element) &&
8254       direction & MV_HORIZONTAL)
8255     MovDir[newx][newy] = 0;
8256
8257   if (!pushed_by_player)
8258   {
8259     int nextx = newx + dx, nexty = newy + dy;
8260     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8261
8262     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8263
8264     if (CAN_FALL(element) && direction == MV_DOWN)
8265       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8266
8267     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8268       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8269
8270     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8271       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8272   }
8273
8274   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8275   {
8276     TestIfBadThingTouchesPlayer(newx, newy);
8277     TestIfBadThingTouchesFriend(newx, newy);
8278
8279     if (!IS_CUSTOM_ELEMENT(element))
8280       TestIfBadThingTouchesOtherBadThing(newx, newy);
8281   }
8282   else if (element == EL_PENGUIN)
8283     TestIfFriendTouchesBadThing(newx, newy);
8284
8285   if (DONT_GET_HIT_BY(element))
8286   {
8287     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8288   }
8289
8290   /* give the player one last chance (one more frame) to move away */
8291   if (CAN_FALL(element) && direction == MV_DOWN &&
8292       (last_line || (!IS_FREE(x, newy + 1) &&
8293                      (!IS_PLAYER(x, newy + 1) ||
8294                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8295     Impact(x, newy);
8296
8297   if (pushed_by_player && !game.use_change_when_pushing_bug)
8298   {
8299     int push_side = MV_DIR_OPPOSITE(direction);
8300     struct PlayerInfo *player = PLAYERINFO(x, y);
8301
8302     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8303                                player->index_bit, push_side);
8304     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8305                                         player->index_bit, push_side);
8306   }
8307
8308   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8309     MovDelay[newx][newy] = 1;
8310
8311   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8312
8313   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8314   TestIfElementHitsCustomElement(newx, newy, direction);
8315   TestIfPlayerTouchesCustomElement(newx, newy);
8316   TestIfElementTouchesCustomElement(newx, newy);
8317
8318   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8319       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8320     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8321                              MV_DIR_OPPOSITE(direction));
8322 }
8323
8324 int AmoebeNachbarNr(int ax, int ay)
8325 {
8326   int i;
8327   int element = Feld[ax][ay];
8328   int group_nr = 0;
8329   static int xy[4][2] =
8330   {
8331     { 0, -1 },
8332     { -1, 0 },
8333     { +1, 0 },
8334     { 0, +1 }
8335   };
8336
8337   for (i = 0; i < NUM_DIRECTIONS; i++)
8338   {
8339     int x = ax + xy[i][0];
8340     int y = ay + xy[i][1];
8341
8342     if (!IN_LEV_FIELD(x, y))
8343       continue;
8344
8345     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8346       group_nr = AmoebaNr[x][y];
8347   }
8348
8349   return group_nr;
8350 }
8351
8352 void AmoebenVereinigen(int ax, int ay)
8353 {
8354   int i, x, y, xx, yy;
8355   int new_group_nr = AmoebaNr[ax][ay];
8356   static int xy[4][2] =
8357   {
8358     { 0, -1 },
8359     { -1, 0 },
8360     { +1, 0 },
8361     { 0, +1 }
8362   };
8363
8364   if (new_group_nr == 0)
8365     return;
8366
8367   for (i = 0; i < NUM_DIRECTIONS; i++)
8368   {
8369     x = ax + xy[i][0];
8370     y = ay + xy[i][1];
8371
8372     if (!IN_LEV_FIELD(x, y))
8373       continue;
8374
8375     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8376          Feld[x][y] == EL_BD_AMOEBA ||
8377          Feld[x][y] == EL_AMOEBA_DEAD) &&
8378         AmoebaNr[x][y] != new_group_nr)
8379     {
8380       int old_group_nr = AmoebaNr[x][y];
8381
8382       if (old_group_nr == 0)
8383         return;
8384
8385       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8386       AmoebaCnt[old_group_nr] = 0;
8387       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8388       AmoebaCnt2[old_group_nr] = 0;
8389
8390       SCAN_PLAYFIELD(xx, yy)
8391       {
8392         if (AmoebaNr[xx][yy] == old_group_nr)
8393           AmoebaNr[xx][yy] = new_group_nr;
8394       }
8395     }
8396   }
8397 }
8398
8399 void AmoebeUmwandeln(int ax, int ay)
8400 {
8401   int i, x, y;
8402
8403   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8404   {
8405     int group_nr = AmoebaNr[ax][ay];
8406
8407 #ifdef DEBUG
8408     if (group_nr == 0)
8409     {
8410       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8411       printf("AmoebeUmwandeln(): This should never happen!\n");
8412       return;
8413     }
8414 #endif
8415
8416     SCAN_PLAYFIELD(x, y)
8417     {
8418       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8419       {
8420         AmoebaNr[x][y] = 0;
8421         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8422       }
8423     }
8424
8425     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8426                             SND_AMOEBA_TURNING_TO_GEM :
8427                             SND_AMOEBA_TURNING_TO_ROCK));
8428     Bang(ax, ay);
8429   }
8430   else
8431   {
8432     static int xy[4][2] =
8433     {
8434       { 0, -1 },
8435       { -1, 0 },
8436       { +1, 0 },
8437       { 0, +1 }
8438     };
8439
8440     for (i = 0; i < NUM_DIRECTIONS; i++)
8441     {
8442       x = ax + xy[i][0];
8443       y = ay + xy[i][1];
8444
8445       if (!IN_LEV_FIELD(x, y))
8446         continue;
8447
8448       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8449       {
8450         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8451                               SND_AMOEBA_TURNING_TO_GEM :
8452                               SND_AMOEBA_TURNING_TO_ROCK));
8453         Bang(x, y);
8454       }
8455     }
8456   }
8457 }
8458
8459 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8460 {
8461   int x, y;
8462   int group_nr = AmoebaNr[ax][ay];
8463   boolean done = FALSE;
8464
8465 #ifdef DEBUG
8466   if (group_nr == 0)
8467   {
8468     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8469     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8470     return;
8471   }
8472 #endif
8473
8474   SCAN_PLAYFIELD(x, y)
8475   {
8476     if (AmoebaNr[x][y] == group_nr &&
8477         (Feld[x][y] == EL_AMOEBA_DEAD ||
8478          Feld[x][y] == EL_BD_AMOEBA ||
8479          Feld[x][y] == EL_AMOEBA_GROWING))
8480     {
8481       AmoebaNr[x][y] = 0;
8482       Feld[x][y] = new_element;
8483       InitField(x, y, FALSE);
8484       TEST_DrawLevelField(x, y);
8485       done = TRUE;
8486     }
8487   }
8488
8489   if (done)
8490     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8491                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8492                             SND_BD_AMOEBA_TURNING_TO_GEM));
8493 }
8494
8495 void AmoebeWaechst(int x, int y)
8496 {
8497   static unsigned int sound_delay = 0;
8498   static unsigned int sound_delay_value = 0;
8499
8500   if (!MovDelay[x][y])          /* start new growing cycle */
8501   {
8502     MovDelay[x][y] = 7;
8503
8504     if (DelayReached(&sound_delay, sound_delay_value))
8505     {
8506       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8507       sound_delay_value = 30;
8508     }
8509   }
8510
8511   if (MovDelay[x][y])           /* wait some time before growing bigger */
8512   {
8513     MovDelay[x][y]--;
8514     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8515     {
8516       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8517                                            6 - MovDelay[x][y]);
8518
8519       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8520     }
8521
8522     if (!MovDelay[x][y])
8523     {
8524       Feld[x][y] = Store[x][y];
8525       Store[x][y] = 0;
8526       TEST_DrawLevelField(x, y);
8527     }
8528   }
8529 }
8530
8531 void AmoebaDisappearing(int x, int y)
8532 {
8533   static unsigned int sound_delay = 0;
8534   static unsigned int sound_delay_value = 0;
8535
8536   if (!MovDelay[x][y])          /* start new shrinking cycle */
8537   {
8538     MovDelay[x][y] = 7;
8539
8540     if (DelayReached(&sound_delay, sound_delay_value))
8541       sound_delay_value = 30;
8542   }
8543
8544   if (MovDelay[x][y])           /* wait some time before shrinking */
8545   {
8546     MovDelay[x][y]--;
8547     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8548     {
8549       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8550                                            6 - MovDelay[x][y]);
8551
8552       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8553     }
8554
8555     if (!MovDelay[x][y])
8556     {
8557       Feld[x][y] = EL_EMPTY;
8558       TEST_DrawLevelField(x, y);
8559
8560       /* don't let mole enter this field in this cycle;
8561          (give priority to objects falling to this field from above) */
8562       Stop[x][y] = TRUE;
8563     }
8564   }
8565 }
8566
8567 void AmoebeAbleger(int ax, int ay)
8568 {
8569   int i;
8570   int element = Feld[ax][ay];
8571   int graphic = el2img(element);
8572   int newax = ax, neway = ay;
8573   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8574   static int xy[4][2] =
8575   {
8576     { 0, -1 },
8577     { -1, 0 },
8578     { +1, 0 },
8579     { 0, +1 }
8580   };
8581
8582   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8583   {
8584     Feld[ax][ay] = EL_AMOEBA_DEAD;
8585     TEST_DrawLevelField(ax, ay);
8586     return;
8587   }
8588
8589   if (IS_ANIMATED(graphic))
8590     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8591
8592   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8593     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8594
8595   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8596   {
8597     MovDelay[ax][ay]--;
8598     if (MovDelay[ax][ay])
8599       return;
8600   }
8601
8602   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8603   {
8604     int start = RND(4);
8605     int x = ax + xy[start][0];
8606     int y = ay + xy[start][1];
8607
8608     if (!IN_LEV_FIELD(x, y))
8609       return;
8610
8611     if (IS_FREE(x, y) ||
8612         CAN_GROW_INTO(Feld[x][y]) ||
8613         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8614         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8615     {
8616       newax = x;
8617       neway = y;
8618     }
8619
8620     if (newax == ax && neway == ay)
8621       return;
8622   }
8623   else                          /* normal or "filled" (BD style) amoeba */
8624   {
8625     int start = RND(4);
8626     boolean waiting_for_player = FALSE;
8627
8628     for (i = 0; i < NUM_DIRECTIONS; i++)
8629     {
8630       int j = (start + i) % 4;
8631       int x = ax + xy[j][0];
8632       int y = ay + xy[j][1];
8633
8634       if (!IN_LEV_FIELD(x, y))
8635         continue;
8636
8637       if (IS_FREE(x, y) ||
8638           CAN_GROW_INTO(Feld[x][y]) ||
8639           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8640           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8641       {
8642         newax = x;
8643         neway = y;
8644         break;
8645       }
8646       else if (IS_PLAYER(x, y))
8647         waiting_for_player = TRUE;
8648     }
8649
8650     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8651     {
8652       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8653       {
8654         Feld[ax][ay] = EL_AMOEBA_DEAD;
8655         TEST_DrawLevelField(ax, ay);
8656         AmoebaCnt[AmoebaNr[ax][ay]]--;
8657
8658         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8659         {
8660           if (element == EL_AMOEBA_FULL)
8661             AmoebeUmwandeln(ax, ay);
8662           else if (element == EL_BD_AMOEBA)
8663             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8664         }
8665       }
8666       return;
8667     }
8668     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8669     {
8670       /* amoeba gets larger by growing in some direction */
8671
8672       int new_group_nr = AmoebaNr[ax][ay];
8673
8674 #ifdef DEBUG
8675   if (new_group_nr == 0)
8676   {
8677     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8678     printf("AmoebeAbleger(): This should never happen!\n");
8679     return;
8680   }
8681 #endif
8682
8683       AmoebaNr[newax][neway] = new_group_nr;
8684       AmoebaCnt[new_group_nr]++;
8685       AmoebaCnt2[new_group_nr]++;
8686
8687       /* if amoeba touches other amoeba(s) after growing, unify them */
8688       AmoebenVereinigen(newax, neway);
8689
8690       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8691       {
8692         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8693         return;
8694       }
8695     }
8696   }
8697
8698   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8699       (neway == lev_fieldy - 1 && newax != ax))
8700   {
8701     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8702     Store[newax][neway] = element;
8703   }
8704   else if (neway == ay || element == EL_EMC_DRIPPER)
8705   {
8706     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8707
8708     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8709   }
8710   else
8711   {
8712     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8713     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8714     Store[ax][ay] = EL_AMOEBA_DROP;
8715     ContinueMoving(ax, ay);
8716     return;
8717   }
8718
8719   TEST_DrawLevelField(newax, neway);
8720 }
8721
8722 void Life(int ax, int ay)
8723 {
8724   int x1, y1, x2, y2;
8725   int life_time = 40;
8726   int element = Feld[ax][ay];
8727   int graphic = el2img(element);
8728   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8729                          level.biomaze);
8730   boolean changed = FALSE;
8731
8732   if (IS_ANIMATED(graphic))
8733     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8734
8735   if (Stop[ax][ay])
8736     return;
8737
8738   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8739     MovDelay[ax][ay] = life_time;
8740
8741   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8742   {
8743     MovDelay[ax][ay]--;
8744     if (MovDelay[ax][ay])
8745       return;
8746   }
8747
8748   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8749   {
8750     int xx = ax+x1, yy = ay+y1;
8751     int nachbarn = 0;
8752
8753     if (!IN_LEV_FIELD(xx, yy))
8754       continue;
8755
8756     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8757     {
8758       int x = xx+x2, y = yy+y2;
8759
8760       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8761         continue;
8762
8763       if (((Feld[x][y] == element ||
8764             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8765            !Stop[x][y]) ||
8766           (IS_FREE(x, y) && Stop[x][y]))
8767         nachbarn++;
8768     }
8769
8770     if (xx == ax && yy == ay)           /* field in the middle */
8771     {
8772       if (nachbarn < life_parameter[0] ||
8773           nachbarn > life_parameter[1])
8774       {
8775         Feld[xx][yy] = EL_EMPTY;
8776         if (!Stop[xx][yy])
8777           TEST_DrawLevelField(xx, yy);
8778         Stop[xx][yy] = TRUE;
8779         changed = TRUE;
8780       }
8781     }
8782     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8783     {                                   /* free border field */
8784       if (nachbarn >= life_parameter[2] &&
8785           nachbarn <= life_parameter[3])
8786       {
8787         Feld[xx][yy] = element;
8788         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8789         if (!Stop[xx][yy])
8790           TEST_DrawLevelField(xx, yy);
8791         Stop[xx][yy] = TRUE;
8792         changed = TRUE;
8793       }
8794     }
8795   }
8796
8797   if (changed)
8798     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8799                    SND_GAME_OF_LIFE_GROWING);
8800 }
8801
8802 static void InitRobotWheel(int x, int y)
8803 {
8804   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8805 }
8806
8807 static void RunRobotWheel(int x, int y)
8808 {
8809   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8810 }
8811
8812 static void StopRobotWheel(int x, int y)
8813 {
8814   if (ZX == x && ZY == y)
8815   {
8816     ZX = ZY = -1;
8817
8818     game.robot_wheel_active = FALSE;
8819   }
8820 }
8821
8822 static void InitTimegateWheel(int x, int y)
8823 {
8824   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8825 }
8826
8827 static void RunTimegateWheel(int x, int y)
8828 {
8829   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8830 }
8831
8832 static void InitMagicBallDelay(int x, int y)
8833 {
8834   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8835 }
8836
8837 static void ActivateMagicBall(int bx, int by)
8838 {
8839   int x, y;
8840
8841   if (level.ball_random)
8842   {
8843     int pos_border = RND(8);    /* select one of the eight border elements */
8844     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8845     int xx = pos_content % 3;
8846     int yy = pos_content / 3;
8847
8848     x = bx - 1 + xx;
8849     y = by - 1 + yy;
8850
8851     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8852       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8853   }
8854   else
8855   {
8856     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8857     {
8858       int xx = x - bx + 1;
8859       int yy = y - by + 1;
8860
8861       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8862         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8863     }
8864   }
8865
8866   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8867 }
8868
8869 void CheckExit(int x, int y)
8870 {
8871   if (local_player->gems_still_needed > 0 ||
8872       local_player->sokobanfields_still_needed > 0 ||
8873       local_player->lights_still_needed > 0)
8874   {
8875     int element = Feld[x][y];
8876     int graphic = el2img(element);
8877
8878     if (IS_ANIMATED(graphic))
8879       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8880
8881     return;
8882   }
8883
8884   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8885     return;
8886
8887   Feld[x][y] = EL_EXIT_OPENING;
8888
8889   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8890 }
8891
8892 void CheckExitEM(int x, int y)
8893 {
8894   if (local_player->gems_still_needed > 0 ||
8895       local_player->sokobanfields_still_needed > 0 ||
8896       local_player->lights_still_needed > 0)
8897   {
8898     int element = Feld[x][y];
8899     int graphic = el2img(element);
8900
8901     if (IS_ANIMATED(graphic))
8902       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8903
8904     return;
8905   }
8906
8907   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8908     return;
8909
8910   Feld[x][y] = EL_EM_EXIT_OPENING;
8911
8912   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8913 }
8914
8915 void CheckExitSteel(int x, int y)
8916 {
8917   if (local_player->gems_still_needed > 0 ||
8918       local_player->sokobanfields_still_needed > 0 ||
8919       local_player->lights_still_needed > 0)
8920   {
8921     int element = Feld[x][y];
8922     int graphic = el2img(element);
8923
8924     if (IS_ANIMATED(graphic))
8925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8926
8927     return;
8928   }
8929
8930   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8931     return;
8932
8933   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8934
8935   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8936 }
8937
8938 void CheckExitSteelEM(int x, int y)
8939 {
8940   if (local_player->gems_still_needed > 0 ||
8941       local_player->sokobanfields_still_needed > 0 ||
8942       local_player->lights_still_needed > 0)
8943   {
8944     int element = Feld[x][y];
8945     int graphic = el2img(element);
8946
8947     if (IS_ANIMATED(graphic))
8948       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8949
8950     return;
8951   }
8952
8953   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8954     return;
8955
8956   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8957
8958   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8959 }
8960
8961 void CheckExitSP(int x, int y)
8962 {
8963   if (local_player->gems_still_needed > 0)
8964   {
8965     int element = Feld[x][y];
8966     int graphic = el2img(element);
8967
8968     if (IS_ANIMATED(graphic))
8969       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8970
8971     return;
8972   }
8973
8974   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8975     return;
8976
8977   Feld[x][y] = EL_SP_EXIT_OPENING;
8978
8979   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8980 }
8981
8982 static void CloseAllOpenTimegates()
8983 {
8984   int x, y;
8985
8986   SCAN_PLAYFIELD(x, y)
8987   {
8988     int element = Feld[x][y];
8989
8990     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8991     {
8992       Feld[x][y] = EL_TIMEGATE_CLOSING;
8993
8994       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8995     }
8996   }
8997 }
8998
8999 void DrawTwinkleOnField(int x, int y)
9000 {
9001   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9002     return;
9003
9004   if (Feld[x][y] == EL_BD_DIAMOND)
9005     return;
9006
9007   if (MovDelay[x][y] == 0)      /* next animation frame */
9008     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9009
9010   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9011   {
9012     MovDelay[x][y]--;
9013
9014     DrawLevelElementAnimation(x, y, Feld[x][y]);
9015
9016     if (MovDelay[x][y] != 0)
9017     {
9018       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9019                                            10 - MovDelay[x][y]);
9020
9021       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9022     }
9023   }
9024 }
9025
9026 void MauerWaechst(int x, int y)
9027 {
9028   int delay = 6;
9029
9030   if (!MovDelay[x][y])          /* next animation frame */
9031     MovDelay[x][y] = 3 * delay;
9032
9033   if (MovDelay[x][y])           /* wait some time before next frame */
9034   {
9035     MovDelay[x][y]--;
9036
9037     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9038     {
9039       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9040       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9041
9042       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9043     }
9044
9045     if (!MovDelay[x][y])
9046     {
9047       if (MovDir[x][y] == MV_LEFT)
9048       {
9049         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9050           TEST_DrawLevelField(x - 1, y);
9051       }
9052       else if (MovDir[x][y] == MV_RIGHT)
9053       {
9054         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9055           TEST_DrawLevelField(x + 1, y);
9056       }
9057       else if (MovDir[x][y] == MV_UP)
9058       {
9059         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9060           TEST_DrawLevelField(x, y - 1);
9061       }
9062       else
9063       {
9064         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9065           TEST_DrawLevelField(x, y + 1);
9066       }
9067
9068       Feld[x][y] = Store[x][y];
9069       Store[x][y] = 0;
9070       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9071       TEST_DrawLevelField(x, y);
9072     }
9073   }
9074 }
9075
9076 void MauerAbleger(int ax, int ay)
9077 {
9078   int element = Feld[ax][ay];
9079   int graphic = el2img(element);
9080   boolean oben_frei = FALSE, unten_frei = FALSE;
9081   boolean links_frei = FALSE, rechts_frei = FALSE;
9082   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9083   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9084   boolean new_wall = FALSE;
9085
9086   if (IS_ANIMATED(graphic))
9087     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9088
9089   if (!MovDelay[ax][ay])        /* start building new wall */
9090     MovDelay[ax][ay] = 6;
9091
9092   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9093   {
9094     MovDelay[ax][ay]--;
9095     if (MovDelay[ax][ay])
9096       return;
9097   }
9098
9099   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9100     oben_frei = TRUE;
9101   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9102     unten_frei = TRUE;
9103   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9104     links_frei = TRUE;
9105   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9106     rechts_frei = TRUE;
9107
9108   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9109       element == EL_EXPANDABLE_WALL_ANY)
9110   {
9111     if (oben_frei)
9112     {
9113       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9114       Store[ax][ay-1] = element;
9115       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9116       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9117         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9118                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9119       new_wall = TRUE;
9120     }
9121     if (unten_frei)
9122     {
9123       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9124       Store[ax][ay+1] = element;
9125       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9126       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9127         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9128                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9129       new_wall = TRUE;
9130     }
9131   }
9132
9133   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9134       element == EL_EXPANDABLE_WALL_ANY ||
9135       element == EL_EXPANDABLE_WALL ||
9136       element == EL_BD_EXPANDABLE_WALL)
9137   {
9138     if (links_frei)
9139     {
9140       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9141       Store[ax-1][ay] = element;
9142       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9143       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9144         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9145                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9146       new_wall = TRUE;
9147     }
9148
9149     if (rechts_frei)
9150     {
9151       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9152       Store[ax+1][ay] = element;
9153       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9154       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9155         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9156                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9157       new_wall = TRUE;
9158     }
9159   }
9160
9161   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9162     TEST_DrawLevelField(ax, ay);
9163
9164   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9165     oben_massiv = TRUE;
9166   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9167     unten_massiv = TRUE;
9168   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9169     links_massiv = TRUE;
9170   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9171     rechts_massiv = TRUE;
9172
9173   if (((oben_massiv && unten_massiv) ||
9174        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9175        element == EL_EXPANDABLE_WALL) &&
9176       ((links_massiv && rechts_massiv) ||
9177        element == EL_EXPANDABLE_WALL_VERTICAL))
9178     Feld[ax][ay] = EL_WALL;
9179
9180   if (new_wall)
9181     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9182 }
9183
9184 void MauerAblegerStahl(int ax, int ay)
9185 {
9186   int element = Feld[ax][ay];
9187   int graphic = el2img(element);
9188   boolean oben_frei = FALSE, unten_frei = FALSE;
9189   boolean links_frei = FALSE, rechts_frei = FALSE;
9190   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9191   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9192   boolean new_wall = FALSE;
9193
9194   if (IS_ANIMATED(graphic))
9195     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9196
9197   if (!MovDelay[ax][ay])        /* start building new wall */
9198     MovDelay[ax][ay] = 6;
9199
9200   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9201   {
9202     MovDelay[ax][ay]--;
9203     if (MovDelay[ax][ay])
9204       return;
9205   }
9206
9207   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9208     oben_frei = TRUE;
9209   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9210     unten_frei = TRUE;
9211   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9212     links_frei = TRUE;
9213   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9214     rechts_frei = TRUE;
9215
9216   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9217       element == EL_EXPANDABLE_STEELWALL_ANY)
9218   {
9219     if (oben_frei)
9220     {
9221       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9222       Store[ax][ay-1] = element;
9223       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9224       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9225         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9226                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9227       new_wall = TRUE;
9228     }
9229     if (unten_frei)
9230     {
9231       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9232       Store[ax][ay+1] = element;
9233       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9234       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9235         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9236                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9237       new_wall = TRUE;
9238     }
9239   }
9240
9241   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9242       element == EL_EXPANDABLE_STEELWALL_ANY)
9243   {
9244     if (links_frei)
9245     {
9246       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9247       Store[ax-1][ay] = element;
9248       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9249       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9250         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9251                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9252       new_wall = TRUE;
9253     }
9254
9255     if (rechts_frei)
9256     {
9257       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9258       Store[ax+1][ay] = element;
9259       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9260       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9261         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9262                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9263       new_wall = TRUE;
9264     }
9265   }
9266
9267   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9268     oben_massiv = TRUE;
9269   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9270     unten_massiv = TRUE;
9271   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9272     links_massiv = TRUE;
9273   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9274     rechts_massiv = TRUE;
9275
9276   if (((oben_massiv && unten_massiv) ||
9277        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9278       ((links_massiv && rechts_massiv) ||
9279        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9280     Feld[ax][ay] = EL_STEELWALL;
9281
9282   if (new_wall)
9283     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9284 }
9285
9286 void CheckForDragon(int x, int y)
9287 {
9288   int i, j;
9289   boolean dragon_found = FALSE;
9290   static int xy[4][2] =
9291   {
9292     { 0, -1 },
9293     { -1, 0 },
9294     { +1, 0 },
9295     { 0, +1 }
9296   };
9297
9298   for (i = 0; i < NUM_DIRECTIONS; i++)
9299   {
9300     for (j = 0; j < 4; j++)
9301     {
9302       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9303
9304       if (IN_LEV_FIELD(xx, yy) &&
9305           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9306       {
9307         if (Feld[xx][yy] == EL_DRAGON)
9308           dragon_found = TRUE;
9309       }
9310       else
9311         break;
9312     }
9313   }
9314
9315   if (!dragon_found)
9316   {
9317     for (i = 0; i < NUM_DIRECTIONS; i++)
9318     {
9319       for (j = 0; j < 3; j++)
9320       {
9321         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9322   
9323         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9324         {
9325           Feld[xx][yy] = EL_EMPTY;
9326           TEST_DrawLevelField(xx, yy);
9327         }
9328         else
9329           break;
9330       }
9331     }
9332   }
9333 }
9334
9335 static void InitBuggyBase(int x, int y)
9336 {
9337   int element = Feld[x][y];
9338   int activating_delay = FRAMES_PER_SECOND / 4;
9339
9340   ChangeDelay[x][y] =
9341     (element == EL_SP_BUGGY_BASE ?
9342      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9343      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9344      activating_delay :
9345      element == EL_SP_BUGGY_BASE_ACTIVE ?
9346      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9347 }
9348
9349 static void WarnBuggyBase(int x, int y)
9350 {
9351   int i;
9352   static int xy[4][2] =
9353   {
9354     { 0, -1 },
9355     { -1, 0 },
9356     { +1, 0 },
9357     { 0, +1 }
9358   };
9359
9360   for (i = 0; i < NUM_DIRECTIONS; i++)
9361   {
9362     int xx = x + xy[i][0];
9363     int yy = y + xy[i][1];
9364
9365     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9366     {
9367       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9368
9369       break;
9370     }
9371   }
9372 }
9373
9374 static void InitTrap(int x, int y)
9375 {
9376   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9377 }
9378
9379 static void ActivateTrap(int x, int y)
9380 {
9381   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9382 }
9383
9384 static void ChangeActiveTrap(int x, int y)
9385 {
9386   int graphic = IMG_TRAP_ACTIVE;
9387
9388   /* if new animation frame was drawn, correct crumbled sand border */
9389   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9390     TEST_DrawLevelFieldCrumbled(x, y);
9391 }
9392
9393 static int getSpecialActionElement(int element, int number, int base_element)
9394 {
9395   return (element != EL_EMPTY ? element :
9396           number != -1 ? base_element + number - 1 :
9397           EL_EMPTY);
9398 }
9399
9400 static int getModifiedActionNumber(int value_old, int operator, int operand,
9401                                    int value_min, int value_max)
9402 {
9403   int value_new = (operator == CA_MODE_SET      ? operand :
9404                    operator == CA_MODE_ADD      ? value_old + operand :
9405                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9406                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9407                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9408                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9409                    value_old);
9410
9411   return (value_new < value_min ? value_min :
9412           value_new > value_max ? value_max :
9413           value_new);
9414 }
9415
9416 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9417 {
9418   struct ElementInfo *ei = &element_info[element];
9419   struct ElementChangeInfo *change = &ei->change_page[page];
9420   int target_element = change->target_element;
9421   int action_type = change->action_type;
9422   int action_mode = change->action_mode;
9423   int action_arg = change->action_arg;
9424   int action_element = change->action_element;
9425   int i;
9426
9427   if (!change->has_action)
9428     return;
9429
9430   /* ---------- determine action paramater values -------------------------- */
9431
9432   int level_time_value =
9433     (level.time > 0 ? TimeLeft :
9434      TimePlayed);
9435
9436   int action_arg_element_raw =
9437     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9438      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9439      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9440      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9441      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9442      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9443      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9444      EL_EMPTY);
9445   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9446
9447   int action_arg_direction =
9448     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9449      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9450      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9451      change->actual_trigger_side :
9452      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9453      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9454      MV_NONE);
9455
9456   int action_arg_number_min =
9457     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9458      CA_ARG_MIN);
9459
9460   int action_arg_number_max =
9461     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9462      action_type == CA_SET_LEVEL_GEMS ? 999 :
9463      action_type == CA_SET_LEVEL_TIME ? 9999 :
9464      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9465      action_type == CA_SET_CE_VALUE ? 9999 :
9466      action_type == CA_SET_CE_SCORE ? 9999 :
9467      CA_ARG_MAX);
9468
9469   int action_arg_number_reset =
9470     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9471      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9472      action_type == CA_SET_LEVEL_TIME ? level.time :
9473      action_type == CA_SET_LEVEL_SCORE ? 0 :
9474      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9475      action_type == CA_SET_CE_SCORE ? 0 :
9476      0);
9477
9478   int action_arg_number =
9479     (action_arg <= CA_ARG_MAX ? action_arg :
9480      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9481      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9482      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9483      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9484      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9485      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9486      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9487      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9488      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9489      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9490      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9491      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9492      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9493      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9494      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9495      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9496      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9497      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9498      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9499      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9500      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9501      -1);
9502
9503   int action_arg_number_old =
9504     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9505      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9506      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9507      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9508      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9509      0);
9510
9511   int action_arg_number_new =
9512     getModifiedActionNumber(action_arg_number_old,
9513                             action_mode, action_arg_number,
9514                             action_arg_number_min, action_arg_number_max);
9515
9516   int trigger_player_bits =
9517     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9518      change->actual_trigger_player_bits : change->trigger_player);
9519
9520   int action_arg_player_bits =
9521     (action_arg >= CA_ARG_PLAYER_1 &&
9522      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9523      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9524      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9525      PLAYER_BITS_ANY);
9526
9527   /* ---------- execute action  -------------------------------------------- */
9528
9529   switch (action_type)
9530   {
9531     case CA_NO_ACTION:
9532     {
9533       return;
9534     }
9535
9536     /* ---------- level actions  ------------------------------------------- */
9537
9538     case CA_RESTART_LEVEL:
9539     {
9540       game.restart_level = TRUE;
9541
9542       break;
9543     }
9544
9545     case CA_SHOW_ENVELOPE:
9546     {
9547       int element = getSpecialActionElement(action_arg_element,
9548                                             action_arg_number, EL_ENVELOPE_1);
9549
9550       if (IS_ENVELOPE(element))
9551         local_player->show_envelope = element;
9552
9553       break;
9554     }
9555
9556     case CA_SET_LEVEL_TIME:
9557     {
9558       if (level.time > 0)       /* only modify limited time value */
9559       {
9560         TimeLeft = action_arg_number_new;
9561
9562         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9563
9564         DisplayGameControlValues();
9565
9566         if (!TimeLeft && setup.time_limit)
9567           for (i = 0; i < MAX_PLAYERS; i++)
9568             KillPlayer(&stored_player[i]);
9569       }
9570
9571       break;
9572     }
9573
9574     case CA_SET_LEVEL_SCORE:
9575     {
9576       local_player->score = action_arg_number_new;
9577
9578       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9579
9580       DisplayGameControlValues();
9581
9582       break;
9583     }
9584
9585     case CA_SET_LEVEL_GEMS:
9586     {
9587       local_player->gems_still_needed = action_arg_number_new;
9588
9589       game_panel_controls[GAME_PANEL_GEMS].value =
9590         local_player->gems_still_needed;
9591
9592       DisplayGameControlValues();
9593
9594       break;
9595     }
9596
9597     case CA_SET_LEVEL_WIND:
9598     {
9599       game.wind_direction = action_arg_direction;
9600
9601       break;
9602     }
9603
9604     case CA_SET_LEVEL_RANDOM_SEED:
9605     {
9606       /* ensure that setting a new random seed while playing is predictable */
9607       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9608
9609       break;
9610     }
9611
9612     /* ---------- player actions  ------------------------------------------ */
9613
9614     case CA_MOVE_PLAYER:
9615     {
9616       /* automatically move to the next field in specified direction */
9617       for (i = 0; i < MAX_PLAYERS; i++)
9618         if (trigger_player_bits & (1 << i))
9619           stored_player[i].programmed_action = action_arg_direction;
9620
9621       break;
9622     }
9623
9624     case CA_EXIT_PLAYER:
9625     {
9626       for (i = 0; i < MAX_PLAYERS; i++)
9627         if (action_arg_player_bits & (1 << i))
9628           PlayerWins(&stored_player[i]);
9629
9630       break;
9631     }
9632
9633     case CA_KILL_PLAYER:
9634     {
9635       for (i = 0; i < MAX_PLAYERS; i++)
9636         if (action_arg_player_bits & (1 << i))
9637           KillPlayer(&stored_player[i]);
9638
9639       break;
9640     }
9641
9642     case CA_SET_PLAYER_KEYS:
9643     {
9644       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9645       int element = getSpecialActionElement(action_arg_element,
9646                                             action_arg_number, EL_KEY_1);
9647
9648       if (IS_KEY(element))
9649       {
9650         for (i = 0; i < MAX_PLAYERS; i++)
9651         {
9652           if (trigger_player_bits & (1 << i))
9653           {
9654             stored_player[i].key[KEY_NR(element)] = key_state;
9655
9656             DrawGameDoorValues();
9657           }
9658         }
9659       }
9660
9661       break;
9662     }
9663
9664     case CA_SET_PLAYER_SPEED:
9665     {
9666       for (i = 0; i < MAX_PLAYERS; i++)
9667       {
9668         if (trigger_player_bits & (1 << i))
9669         {
9670           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9671
9672           if (action_arg == CA_ARG_SPEED_FASTER &&
9673               stored_player[i].cannot_move)
9674           {
9675             action_arg_number = STEPSIZE_VERY_SLOW;
9676           }
9677           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9678                    action_arg == CA_ARG_SPEED_FASTER)
9679           {
9680             action_arg_number = 2;
9681             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9682                            CA_MODE_MULTIPLY);
9683           }
9684           else if (action_arg == CA_ARG_NUMBER_RESET)
9685           {
9686             action_arg_number = level.initial_player_stepsize[i];
9687           }
9688
9689           move_stepsize =
9690             getModifiedActionNumber(move_stepsize,
9691                                     action_mode,
9692                                     action_arg_number,
9693                                     action_arg_number_min,
9694                                     action_arg_number_max);
9695
9696           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9697         }
9698       }
9699
9700       break;
9701     }
9702
9703     case CA_SET_PLAYER_SHIELD:
9704     {
9705       for (i = 0; i < MAX_PLAYERS; i++)
9706       {
9707         if (trigger_player_bits & (1 << i))
9708         {
9709           if (action_arg == CA_ARG_SHIELD_OFF)
9710           {
9711             stored_player[i].shield_normal_time_left = 0;
9712             stored_player[i].shield_deadly_time_left = 0;
9713           }
9714           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9715           {
9716             stored_player[i].shield_normal_time_left = 999999;
9717           }
9718           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9719           {
9720             stored_player[i].shield_normal_time_left = 999999;
9721             stored_player[i].shield_deadly_time_left = 999999;
9722           }
9723         }
9724       }
9725
9726       break;
9727     }
9728
9729     case CA_SET_PLAYER_GRAVITY:
9730     {
9731       for (i = 0; i < MAX_PLAYERS; i++)
9732       {
9733         if (trigger_player_bits & (1 << i))
9734         {
9735           stored_player[i].gravity =
9736             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9737              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9738              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9739              stored_player[i].gravity);
9740         }
9741       }
9742
9743       break;
9744     }
9745
9746     case CA_SET_PLAYER_ARTWORK:
9747     {
9748       for (i = 0; i < MAX_PLAYERS; i++)
9749       {
9750         if (trigger_player_bits & (1 << i))
9751         {
9752           int artwork_element = action_arg_element;
9753
9754           if (action_arg == CA_ARG_ELEMENT_RESET)
9755             artwork_element =
9756               (level.use_artwork_element[i] ? level.artwork_element[i] :
9757                stored_player[i].element_nr);
9758
9759           if (stored_player[i].artwork_element != artwork_element)
9760             stored_player[i].Frame = 0;
9761
9762           stored_player[i].artwork_element = artwork_element;
9763
9764           SetPlayerWaiting(&stored_player[i], FALSE);
9765
9766           /* set number of special actions for bored and sleeping animation */
9767           stored_player[i].num_special_action_bored =
9768             get_num_special_action(artwork_element,
9769                                    ACTION_BORING_1, ACTION_BORING_LAST);
9770           stored_player[i].num_special_action_sleeping =
9771             get_num_special_action(artwork_element,
9772                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9773         }
9774       }
9775
9776       break;
9777     }
9778
9779     case CA_SET_PLAYER_INVENTORY:
9780     {
9781       for (i = 0; i < MAX_PLAYERS; i++)
9782       {
9783         struct PlayerInfo *player = &stored_player[i];
9784         int j, k;
9785
9786         if (trigger_player_bits & (1 << i))
9787         {
9788           int inventory_element = action_arg_element;
9789
9790           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9791               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9792               action_arg == CA_ARG_ELEMENT_ACTION)
9793           {
9794             int element = inventory_element;
9795             int collect_count = element_info[element].collect_count_initial;
9796
9797             if (!IS_CUSTOM_ELEMENT(element))
9798               collect_count = 1;
9799
9800             if (collect_count == 0)
9801               player->inventory_infinite_element = element;
9802             else
9803               for (k = 0; k < collect_count; k++)
9804                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9805                   player->inventory_element[player->inventory_size++] =
9806                     element;
9807           }
9808           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9809                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9810                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9811           {
9812             if (player->inventory_infinite_element != EL_UNDEFINED &&
9813                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9814                                      action_arg_element_raw))
9815               player->inventory_infinite_element = EL_UNDEFINED;
9816
9817             for (k = 0, j = 0; j < player->inventory_size; j++)
9818             {
9819               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9820                                         action_arg_element_raw))
9821                 player->inventory_element[k++] = player->inventory_element[j];
9822             }
9823
9824             player->inventory_size = k;
9825           }
9826           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9827           {
9828             if (player->inventory_size > 0)
9829             {
9830               for (j = 0; j < player->inventory_size - 1; j++)
9831                 player->inventory_element[j] = player->inventory_element[j + 1];
9832
9833               player->inventory_size--;
9834             }
9835           }
9836           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9837           {
9838             if (player->inventory_size > 0)
9839               player->inventory_size--;
9840           }
9841           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9842           {
9843             player->inventory_infinite_element = EL_UNDEFINED;
9844             player->inventory_size = 0;
9845           }
9846           else if (action_arg == CA_ARG_INVENTORY_RESET)
9847           {
9848             player->inventory_infinite_element = EL_UNDEFINED;
9849             player->inventory_size = 0;
9850
9851             if (level.use_initial_inventory[i])
9852             {
9853               for (j = 0; j < level.initial_inventory_size[i]; j++)
9854               {
9855                 int element = level.initial_inventory_content[i][j];
9856                 int collect_count = element_info[element].collect_count_initial;
9857
9858                 if (!IS_CUSTOM_ELEMENT(element))
9859                   collect_count = 1;
9860
9861                 if (collect_count == 0)
9862                   player->inventory_infinite_element = element;
9863                 else
9864                   for (k = 0; k < collect_count; k++)
9865                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9866                       player->inventory_element[player->inventory_size++] =
9867                         element;
9868               }
9869             }
9870           }
9871         }
9872       }
9873
9874       break;
9875     }
9876
9877     /* ---------- CE actions  ---------------------------------------------- */
9878
9879     case CA_SET_CE_VALUE:
9880     {
9881       int last_ce_value = CustomValue[x][y];
9882
9883       CustomValue[x][y] = action_arg_number_new;
9884
9885       if (CustomValue[x][y] != last_ce_value)
9886       {
9887         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9888         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9889
9890         if (CustomValue[x][y] == 0)
9891         {
9892           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9893           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9894         }
9895       }
9896
9897       break;
9898     }
9899
9900     case CA_SET_CE_SCORE:
9901     {
9902       int last_ce_score = ei->collect_score;
9903
9904       ei->collect_score = action_arg_number_new;
9905
9906       if (ei->collect_score != last_ce_score)
9907       {
9908         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9909         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9910
9911         if (ei->collect_score == 0)
9912         {
9913           int xx, yy;
9914
9915           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9916           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9917
9918           /*
9919             This is a very special case that seems to be a mixture between
9920             CheckElementChange() and CheckTriggeredElementChange(): while
9921             the first one only affects single elements that are triggered
9922             directly, the second one affects multiple elements in the playfield
9923             that are triggered indirectly by another element. This is a third
9924             case: Changing the CE score always affects multiple identical CEs,
9925             so every affected CE must be checked, not only the single CE for
9926             which the CE score was changed in the first place (as every instance
9927             of that CE shares the same CE score, and therefore also can change)!
9928           */
9929           SCAN_PLAYFIELD(xx, yy)
9930           {
9931             if (Feld[xx][yy] == element)
9932               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9933                                  CE_SCORE_GETS_ZERO);
9934           }
9935         }
9936       }
9937
9938       break;
9939     }
9940
9941     case CA_SET_CE_ARTWORK:
9942     {
9943       int artwork_element = action_arg_element;
9944       boolean reset_frame = FALSE;
9945       int xx, yy;
9946
9947       if (action_arg == CA_ARG_ELEMENT_RESET)
9948         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9949                            element);
9950
9951       if (ei->gfx_element != artwork_element)
9952         reset_frame = TRUE;
9953
9954       ei->gfx_element = artwork_element;
9955
9956       SCAN_PLAYFIELD(xx, yy)
9957       {
9958         if (Feld[xx][yy] == element)
9959         {
9960           if (reset_frame)
9961           {
9962             ResetGfxAnimation(xx, yy);
9963             ResetRandomAnimationValue(xx, yy);
9964           }
9965
9966           TEST_DrawLevelField(xx, yy);
9967         }
9968       }
9969
9970       break;
9971     }
9972
9973     /* ---------- engine actions  ------------------------------------------ */
9974
9975     case CA_SET_ENGINE_SCAN_MODE:
9976     {
9977       InitPlayfieldScanMode(action_arg);
9978
9979       break;
9980     }
9981
9982     default:
9983       break;
9984   }
9985 }
9986
9987 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9988 {
9989   int old_element = Feld[x][y];
9990   int new_element = GetElementFromGroupElement(element);
9991   int previous_move_direction = MovDir[x][y];
9992   int last_ce_value = CustomValue[x][y];
9993   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9994   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9995   boolean add_player_onto_element = (new_element_is_player &&
9996                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9997                                      IS_WALKABLE(old_element));
9998
9999   if (!add_player_onto_element)
10000   {
10001     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10002       RemoveMovingField(x, y);
10003     else
10004       RemoveField(x, y);
10005
10006     Feld[x][y] = new_element;
10007
10008     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10009       MovDir[x][y] = previous_move_direction;
10010
10011     if (element_info[new_element].use_last_ce_value)
10012       CustomValue[x][y] = last_ce_value;
10013
10014     InitField_WithBug1(x, y, FALSE);
10015
10016     new_element = Feld[x][y];   /* element may have changed */
10017
10018     ResetGfxAnimation(x, y);
10019     ResetRandomAnimationValue(x, y);
10020
10021 #if 0
10022   if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
10023     printf(" (RESET X)");
10024 #endif
10025
10026     TEST_DrawLevelField(x, y);
10027
10028     if (GFX_CRUMBLED(new_element))
10029       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10030   }
10031
10032   /* check if element under the player changes from accessible to unaccessible
10033      (needed for special case of dropping element which then changes) */
10034   /* (must be checked after creating new element for walkable group elements) */
10035   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10036       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10037   {
10038     Bang(x, y);
10039
10040     return;
10041   }
10042
10043   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10044   if (new_element_is_player)
10045     RelocatePlayer(x, y, new_element);
10046
10047   if (is_change)
10048     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10049
10050   TestIfBadThingTouchesPlayer(x, y);
10051   TestIfPlayerTouchesCustomElement(x, y);
10052   TestIfElementTouchesCustomElement(x, y);
10053 }
10054
10055 static void CreateField(int x, int y, int element)
10056 {
10057   CreateFieldExt(x, y, element, FALSE);
10058 }
10059
10060 static void CreateElementFromChange(int x, int y, int element)
10061 {
10062   element = GET_VALID_RUNTIME_ELEMENT(element);
10063
10064   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10065   {
10066     int old_element = Feld[x][y];
10067
10068     /* prevent changed element from moving in same engine frame
10069        unless both old and new element can either fall or move */
10070     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10071         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10072       Stop[x][y] = TRUE;
10073   }
10074
10075   CreateFieldExt(x, y, element, TRUE);
10076 }
10077
10078 static boolean ChangeElement(int x, int y, int element, int page)
10079 {
10080   struct ElementInfo *ei = &element_info[element];
10081   struct ElementChangeInfo *change = &ei->change_page[page];
10082   int ce_value = CustomValue[x][y];
10083   int ce_score = ei->collect_score;
10084   int target_element;
10085   int old_element = Feld[x][y];
10086
10087   /* always use default change event to prevent running into a loop */
10088   if (ChangeEvent[x][y] == -1)
10089     ChangeEvent[x][y] = CE_DELAY;
10090
10091   if (ChangeEvent[x][y] == CE_DELAY)
10092   {
10093     /* reset actual trigger element, trigger player and action element */
10094     change->actual_trigger_element = EL_EMPTY;
10095     change->actual_trigger_player = EL_EMPTY;
10096     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10097     change->actual_trigger_side = CH_SIDE_NONE;
10098     change->actual_trigger_ce_value = 0;
10099     change->actual_trigger_ce_score = 0;
10100   }
10101
10102   /* do not change elements more than a specified maximum number of changes */
10103   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10104     return FALSE;
10105
10106   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10107
10108   if (change->explode)
10109   {
10110     Bang(x, y);
10111
10112     return TRUE;
10113   }
10114
10115   if (change->use_target_content)
10116   {
10117     boolean complete_replace = TRUE;
10118     boolean can_replace[3][3];
10119     int xx, yy;
10120
10121     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10122     {
10123       boolean is_empty;
10124       boolean is_walkable;
10125       boolean is_diggable;
10126       boolean is_collectible;
10127       boolean is_removable;
10128       boolean is_destructible;
10129       int ex = x + xx - 1;
10130       int ey = y + yy - 1;
10131       int content_element = change->target_content.e[xx][yy];
10132       int e;
10133
10134       can_replace[xx][yy] = TRUE;
10135
10136       if (ex == x && ey == y)   /* do not check changing element itself */
10137         continue;
10138
10139       if (content_element == EL_EMPTY_SPACE)
10140       {
10141         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10142
10143         continue;
10144       }
10145
10146       if (!IN_LEV_FIELD(ex, ey))
10147       {
10148         can_replace[xx][yy] = FALSE;
10149         complete_replace = FALSE;
10150
10151         continue;
10152       }
10153
10154       e = Feld[ex][ey];
10155
10156       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10157         e = MovingOrBlocked2Element(ex, ey);
10158
10159       is_empty = (IS_FREE(ex, ey) ||
10160                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10161
10162       is_walkable     = (is_empty || IS_WALKABLE(e));
10163       is_diggable     = (is_empty || IS_DIGGABLE(e));
10164       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10165       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10166       is_removable    = (is_diggable || is_collectible);
10167
10168       can_replace[xx][yy] =
10169         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10170           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10171           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10172           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10173           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10174           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10175          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10176
10177       if (!can_replace[xx][yy])
10178         complete_replace = FALSE;
10179     }
10180
10181     if (!change->only_if_complete || complete_replace)
10182     {
10183       boolean something_has_changed = FALSE;
10184
10185       if (change->only_if_complete && change->use_random_replace &&
10186           RND(100) < change->random_percentage)
10187         return FALSE;
10188
10189       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10190       {
10191         int ex = x + xx - 1;
10192         int ey = y + yy - 1;
10193         int content_element;
10194
10195         if (can_replace[xx][yy] && (!change->use_random_replace ||
10196                                     RND(100) < change->random_percentage))
10197         {
10198           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10199             RemoveMovingField(ex, ey);
10200
10201           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10202
10203           content_element = change->target_content.e[xx][yy];
10204           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10205                                               ce_value, ce_score);
10206
10207           CreateElementFromChange(ex, ey, target_element);
10208
10209           something_has_changed = TRUE;
10210
10211           /* for symmetry reasons, freeze newly created border elements */
10212           if (ex != x || ey != y)
10213             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10214         }
10215       }
10216
10217       if (something_has_changed)
10218       {
10219         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10220         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10221       }
10222     }
10223   }
10224   else
10225   {
10226     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10227                                         ce_value, ce_score);
10228
10229     if (element == EL_DIAGONAL_GROWING ||
10230         element == EL_DIAGONAL_SHRINKING)
10231     {
10232       target_element = Store[x][y];
10233
10234       Store[x][y] = EL_EMPTY;
10235     }
10236
10237     CreateElementFromChange(x, y, target_element);
10238
10239     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10240     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10241   }
10242
10243   /* this uses direct change before indirect change */
10244   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10245
10246   return TRUE;
10247 }
10248
10249 static void HandleElementChange(int x, int y, int page)
10250 {
10251   int element = MovingOrBlocked2Element(x, y);
10252   struct ElementInfo *ei = &element_info[element];
10253   struct ElementChangeInfo *change = &ei->change_page[page];
10254   boolean handle_action_before_change = FALSE;
10255
10256 #ifdef DEBUG
10257   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10258       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10259   {
10260     printf("\n\n");
10261     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10262            x, y, element, element_info[element].token_name);
10263     printf("HandleElementChange(): This should never happen!\n");
10264     printf("\n\n");
10265   }
10266 #endif
10267
10268   /* this can happen with classic bombs on walkable, changing elements */
10269   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10270   {
10271     return;
10272   }
10273
10274   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10275   {
10276     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10277
10278     if (change->can_change)
10279     {
10280       /* !!! not clear why graphic animation should be reset at all here !!! */
10281       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10282       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10283
10284       /*
10285         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10286
10287         When using an animation frame delay of 1 (this only happens with
10288         "sp_zonk.moving.left/right" in the classic graphics), the default
10289         (non-moving) animation shows wrong animation frames (while the
10290         moving animation, like "sp_zonk.moving.left/right", is correct,
10291         so this graphical bug never shows up with the classic graphics).
10292         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10293         be drawn instead of the correct frames 0,1,2,3. This is caused by
10294         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10295         an element change: First when the change delay ("ChangeDelay[][]")
10296         counter has reached zero after decrementing (see "RESET 1" below),
10297         then a second time in the next frame (after "GfxFrame[][]" was
10298         already incremented) when "ChangeDelay[][]" is reset to the initial
10299         delay value again (see "RESET 2" below).
10300
10301         This causes frame 0 to be drawn twice, while the last frame won't
10302         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10303
10304         As some animations may already be cleverly designed around this bug
10305         (at least the "Snake Bite" snake tail animation does this), it cannot
10306         simply be fixed here without breaking such existing animations.
10307         Unfortunately, it cannot easily be detected if a graphics set was
10308         designed "before" or "after" the bug was fixed. As a workaround,
10309         a new graphics set option "game.graphics_engine_version" was added
10310         to be able to specify the game's major release version for which the
10311         graphics set was designed, which can then be used to decide if the
10312         bugfix should be used (version 4 and above) or not (version 3 or
10313         below, or if no version was specified at all, as with old sets).
10314
10315         (The wrong/fixed animation frames can be tested with the test level set
10316         "test_gfxframe" and level "000", which contains a specially prepared
10317         custom element at level position (x/y) == (11/9) which uses the zonk
10318         animation mentioned above. Using "game.graphics_engine_version: 4"
10319         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10320         This can also be seen from the debug output for this test element.)
10321       */
10322
10323       /* when a custom element is about to change (for example by change delay),
10324          do not reset graphic animation when the custom element is moving */
10325       if (game.graphics_engine_version < 4 &&
10326           !IS_MOVING(x, y))
10327       {
10328         ResetGfxAnimation(x, y);
10329         ResetRandomAnimationValue(x, y);
10330
10331 #if 1
10332         if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
10333           printf(" (RESET 2)");
10334 #endif
10335
10336       }
10337
10338       if (change->pre_change_function)
10339         change->pre_change_function(x, y);
10340     }
10341   }
10342
10343   ChangeDelay[x][y]--;
10344
10345   if (ChangeDelay[x][y] != 0)           /* continue element change */
10346   {
10347     if (change->can_change)
10348     {
10349       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10350
10351       if (IS_ANIMATED(graphic))
10352         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10353
10354       if (change->change_function)
10355         change->change_function(x, y);
10356     }
10357   }
10358   else                                  /* finish element change */
10359   {
10360     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10361     {
10362       page = ChangePage[x][y];
10363       ChangePage[x][y] = -1;
10364
10365       change = &ei->change_page[page];
10366     }
10367
10368     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10369     {
10370       ChangeDelay[x][y] = 1;            /* try change after next move step */
10371       ChangePage[x][y] = page;          /* remember page to use for change */
10372
10373       return;
10374     }
10375
10376     /* special case: set new level random seed before changing element */
10377     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10378       handle_action_before_change = TRUE;
10379
10380     if (change->has_action && handle_action_before_change)
10381       ExecuteCustomElementAction(x, y, element, page);
10382
10383     if (change->can_change)
10384     {
10385       if (ChangeElement(x, y, element, page))
10386       {
10387         if (change->post_change_function)
10388           change->post_change_function(x, y);
10389       }
10390
10391 #if 1
10392       if (x == DEBUG_GFXFRAME_X && y == DEBUG_GFXFRAME_Y)
10393         printf(" (RESET 1)");
10394 #endif
10395
10396     }
10397
10398     if (change->has_action && !handle_action_before_change)
10399       ExecuteCustomElementAction(x, y, element, page);
10400   }
10401 }
10402
10403 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10404                                               int trigger_element,
10405                                               int trigger_event,
10406                                               int trigger_player,
10407                                               int trigger_side,
10408                                               int trigger_page)
10409 {
10410   boolean change_done_any = FALSE;
10411   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10412   int i;
10413
10414   if (!(trigger_events[trigger_element][trigger_event]))
10415     return FALSE;
10416
10417   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10418
10419   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10420   {
10421     int element = EL_CUSTOM_START + i;
10422     boolean change_done = FALSE;
10423     int p;
10424
10425     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10426         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10427       continue;
10428
10429     for (p = 0; p < element_info[element].num_change_pages; p++)
10430     {
10431       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10432
10433       if (change->can_change_or_has_action &&
10434           change->has_event[trigger_event] &&
10435           change->trigger_side & trigger_side &&
10436           change->trigger_player & trigger_player &&
10437           change->trigger_page & trigger_page_bits &&
10438           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10439       {
10440         change->actual_trigger_element = trigger_element;
10441         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10442         change->actual_trigger_player_bits = trigger_player;
10443         change->actual_trigger_side = trigger_side;
10444         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10445         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10446
10447         if ((change->can_change && !change_done) || change->has_action)
10448         {
10449           int x, y;
10450
10451           SCAN_PLAYFIELD(x, y)
10452           {
10453             if (Feld[x][y] == element)
10454             {
10455               if (change->can_change && !change_done)
10456               {
10457                 /* if element already changed in this frame, not only prevent
10458                    another element change (checked in ChangeElement()), but
10459                    also prevent additional element actions for this element */
10460
10461                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10462                     !level.use_action_after_change_bug)
10463                   continue;
10464
10465                 ChangeDelay[x][y] = 1;
10466                 ChangeEvent[x][y] = trigger_event;
10467
10468                 HandleElementChange(x, y, p);
10469               }
10470               else if (change->has_action)
10471               {
10472                 /* if element already changed in this frame, not only prevent
10473                    another element change (checked in ChangeElement()), but
10474                    also prevent additional element actions for this element */
10475
10476                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10477                     !level.use_action_after_change_bug)
10478                   continue;
10479
10480                 ExecuteCustomElementAction(x, y, element, p);
10481                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10482               }
10483             }
10484           }
10485
10486           if (change->can_change)
10487           {
10488             change_done = TRUE;
10489             change_done_any = TRUE;
10490           }
10491         }
10492       }
10493     }
10494   }
10495
10496   RECURSION_LOOP_DETECTION_END();
10497
10498   return change_done_any;
10499 }
10500
10501 static boolean CheckElementChangeExt(int x, int y,
10502                                      int element,
10503                                      int trigger_element,
10504                                      int trigger_event,
10505                                      int trigger_player,
10506                                      int trigger_side)
10507 {
10508   boolean change_done = FALSE;
10509   int p;
10510
10511   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10512       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10513     return FALSE;
10514
10515   if (Feld[x][y] == EL_BLOCKED)
10516   {
10517     Blocked2Moving(x, y, &x, &y);
10518     element = Feld[x][y];
10519   }
10520
10521   /* check if element has already changed or is about to change after moving */
10522   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10523        Feld[x][y] != element) ||
10524
10525       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10526        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10527         ChangePage[x][y] != -1)))
10528     return FALSE;
10529
10530   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10531
10532   for (p = 0; p < element_info[element].num_change_pages; p++)
10533   {
10534     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10535
10536     /* check trigger element for all events where the element that is checked
10537        for changing interacts with a directly adjacent element -- this is
10538        different to element changes that affect other elements to change on the
10539        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10540     boolean check_trigger_element =
10541       (trigger_event == CE_TOUCHING_X ||
10542        trigger_event == CE_HITTING_X ||
10543        trigger_event == CE_HIT_BY_X ||
10544        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10545
10546     if (change->can_change_or_has_action &&
10547         change->has_event[trigger_event] &&
10548         change->trigger_side & trigger_side &&
10549         change->trigger_player & trigger_player &&
10550         (!check_trigger_element ||
10551          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10552     {
10553       change->actual_trigger_element = trigger_element;
10554       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10555       change->actual_trigger_player_bits = trigger_player;
10556       change->actual_trigger_side = trigger_side;
10557       change->actual_trigger_ce_value = CustomValue[x][y];
10558       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10559
10560       /* special case: trigger element not at (x,y) position for some events */
10561       if (check_trigger_element)
10562       {
10563         static struct
10564         {
10565           int dx, dy;
10566         } move_xy[] =
10567           {
10568             {  0,  0 },
10569             { -1,  0 },
10570             { +1,  0 },
10571             {  0,  0 },
10572             {  0, -1 },
10573             {  0,  0 }, { 0, 0 }, { 0, 0 },
10574             {  0, +1 }
10575           };
10576
10577         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10578         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10579
10580         change->actual_trigger_ce_value = CustomValue[xx][yy];
10581         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10582       }
10583
10584       if (change->can_change && !change_done)
10585       {
10586         ChangeDelay[x][y] = 1;
10587         ChangeEvent[x][y] = trigger_event;
10588
10589         HandleElementChange(x, y, p);
10590
10591         change_done = TRUE;
10592       }
10593       else if (change->has_action)
10594       {
10595         ExecuteCustomElementAction(x, y, element, p);
10596         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10597       }
10598     }
10599   }
10600
10601   RECURSION_LOOP_DETECTION_END();
10602
10603   return change_done;
10604 }
10605
10606 static void PlayPlayerSound(struct PlayerInfo *player)
10607 {
10608   int jx = player->jx, jy = player->jy;
10609   int sound_element = player->artwork_element;
10610   int last_action = player->last_action_waiting;
10611   int action = player->action_waiting;
10612
10613   if (player->is_waiting)
10614   {
10615     if (action != last_action)
10616       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10617     else
10618       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10619   }
10620   else
10621   {
10622     if (action != last_action)
10623       StopSound(element_info[sound_element].sound[last_action]);
10624
10625     if (last_action == ACTION_SLEEPING)
10626       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10627   }
10628 }
10629
10630 static void PlayAllPlayersSound()
10631 {
10632   int i;
10633
10634   for (i = 0; i < MAX_PLAYERS; i++)
10635     if (stored_player[i].active)
10636       PlayPlayerSound(&stored_player[i]);
10637 }
10638
10639 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10640 {
10641   boolean last_waiting = player->is_waiting;
10642   int move_dir = player->MovDir;
10643
10644   player->dir_waiting = move_dir;
10645   player->last_action_waiting = player->action_waiting;
10646
10647   if (is_waiting)
10648   {
10649     if (!last_waiting)          /* not waiting -> waiting */
10650     {
10651       player->is_waiting = TRUE;
10652
10653       player->frame_counter_bored =
10654         FrameCounter +
10655         game.player_boring_delay_fixed +
10656         GetSimpleRandom(game.player_boring_delay_random);
10657       player->frame_counter_sleeping =
10658         FrameCounter +
10659         game.player_sleeping_delay_fixed +
10660         GetSimpleRandom(game.player_sleeping_delay_random);
10661
10662       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10663     }
10664
10665     if (game.player_sleeping_delay_fixed +
10666         game.player_sleeping_delay_random > 0 &&
10667         player->anim_delay_counter == 0 &&
10668         player->post_delay_counter == 0 &&
10669         FrameCounter >= player->frame_counter_sleeping)
10670       player->is_sleeping = TRUE;
10671     else if (game.player_boring_delay_fixed +
10672              game.player_boring_delay_random > 0 &&
10673              FrameCounter >= player->frame_counter_bored)
10674       player->is_bored = TRUE;
10675
10676     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10677                               player->is_bored ? ACTION_BORING :
10678                               ACTION_WAITING);
10679
10680     if (player->is_sleeping && player->use_murphy)
10681     {
10682       /* special case for sleeping Murphy when leaning against non-free tile */
10683
10684       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10685           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10686            !IS_MOVING(player->jx - 1, player->jy)))
10687         move_dir = MV_LEFT;
10688       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10689                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10690                 !IS_MOVING(player->jx + 1, player->jy)))
10691         move_dir = MV_RIGHT;
10692       else
10693         player->is_sleeping = FALSE;
10694
10695       player->dir_waiting = move_dir;
10696     }
10697
10698     if (player->is_sleeping)
10699     {
10700       if (player->num_special_action_sleeping > 0)
10701       {
10702         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10703         {
10704           int last_special_action = player->special_action_sleeping;
10705           int num_special_action = player->num_special_action_sleeping;
10706           int special_action =
10707             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10708              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10709              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10710              last_special_action + 1 : ACTION_SLEEPING);
10711           int special_graphic =
10712             el_act_dir2img(player->artwork_element, special_action, move_dir);
10713
10714           player->anim_delay_counter =
10715             graphic_info[special_graphic].anim_delay_fixed +
10716             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10717           player->post_delay_counter =
10718             graphic_info[special_graphic].post_delay_fixed +
10719             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10720
10721           player->special_action_sleeping = special_action;
10722         }
10723
10724         if (player->anim_delay_counter > 0)
10725         {
10726           player->action_waiting = player->special_action_sleeping;
10727           player->anim_delay_counter--;
10728         }
10729         else if (player->post_delay_counter > 0)
10730         {
10731           player->post_delay_counter--;
10732         }
10733       }
10734     }
10735     else if (player->is_bored)
10736     {
10737       if (player->num_special_action_bored > 0)
10738       {
10739         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10740         {
10741           int special_action =
10742             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10743           int special_graphic =
10744             el_act_dir2img(player->artwork_element, special_action, move_dir);
10745
10746           player->anim_delay_counter =
10747             graphic_info[special_graphic].anim_delay_fixed +
10748             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10749           player->post_delay_counter =
10750             graphic_info[special_graphic].post_delay_fixed +
10751             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10752
10753           player->special_action_bored = special_action;
10754         }
10755
10756         if (player->anim_delay_counter > 0)
10757         {
10758           player->action_waiting = player->special_action_bored;
10759           player->anim_delay_counter--;
10760         }
10761         else if (player->post_delay_counter > 0)
10762         {
10763           player->post_delay_counter--;
10764         }
10765       }
10766     }
10767   }
10768   else if (last_waiting)        /* waiting -> not waiting */
10769   {
10770     player->is_waiting = FALSE;
10771     player->is_bored = FALSE;
10772     player->is_sleeping = FALSE;
10773
10774     player->frame_counter_bored = -1;
10775     player->frame_counter_sleeping = -1;
10776
10777     player->anim_delay_counter = 0;
10778     player->post_delay_counter = 0;
10779
10780     player->dir_waiting = player->MovDir;
10781     player->action_waiting = ACTION_DEFAULT;
10782
10783     player->special_action_bored = ACTION_DEFAULT;
10784     player->special_action_sleeping = ACTION_DEFAULT;
10785   }
10786 }
10787
10788 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10789 {
10790   static boolean player_was_moving = FALSE;
10791   static boolean player_was_snapping = FALSE;
10792   static boolean player_was_dropping = FALSE;
10793
10794   if ((!player->is_moving  && player_was_moving) ||
10795       (player->MovPos == 0 && player_was_moving) ||
10796       (player->is_snapping && !player_was_snapping) ||
10797       (player->is_dropping && !player_was_dropping))
10798   {
10799     if (!SaveEngineSnapshotToList())
10800       return;
10801
10802     player_was_moving = FALSE;
10803     player_was_snapping = TRUE;
10804     player_was_dropping = TRUE;
10805   }
10806   else
10807   {
10808     if (player->is_moving)
10809       player_was_moving = TRUE;
10810
10811     if (!player->is_snapping)
10812       player_was_snapping = FALSE;
10813
10814     if (!player->is_dropping)
10815       player_was_dropping = FALSE;
10816   }
10817 }
10818
10819 static void CheckSingleStepMode(struct PlayerInfo *player)
10820 {
10821   if (tape.single_step && tape.recording && !tape.pausing)
10822   {
10823     /* as it is called "single step mode", just return to pause mode when the
10824        player stopped moving after one tile (or never starts moving at all) */
10825     if (!player->is_moving && !player->is_pushing)
10826     {
10827       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10828       SnapField(player, 0, 0);                  /* stop snapping */
10829     }
10830   }
10831
10832   CheckSaveEngineSnapshot(player);
10833 }
10834
10835 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10836 {
10837   int left      = player_action & JOY_LEFT;
10838   int right     = player_action & JOY_RIGHT;
10839   int up        = player_action & JOY_UP;
10840   int down      = player_action & JOY_DOWN;
10841   int button1   = player_action & JOY_BUTTON_1;
10842   int button2   = player_action & JOY_BUTTON_2;
10843   int dx        = (left ? -1 : right ? 1 : 0);
10844   int dy        = (up   ? -1 : down  ? 1 : 0);
10845
10846   if (!player->active || tape.pausing)
10847     return 0;
10848
10849   if (player_action)
10850   {
10851     if (button1)
10852       SnapField(player, dx, dy);
10853     else
10854     {
10855       if (button2)
10856         DropElement(player);
10857
10858       MovePlayer(player, dx, dy);
10859     }
10860
10861     CheckSingleStepMode(player);
10862
10863     SetPlayerWaiting(player, FALSE);
10864
10865     return player_action;
10866   }
10867   else
10868   {
10869     /* no actions for this player (no input at player's configured device) */
10870
10871     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10872     SnapField(player, 0, 0);
10873     CheckGravityMovementWhenNotMoving(player);
10874
10875     if (player->MovPos == 0)
10876       SetPlayerWaiting(player, TRUE);
10877
10878     if (player->MovPos == 0)    /* needed for tape.playing */
10879       player->is_moving = FALSE;
10880
10881     player->is_dropping = FALSE;
10882     player->is_dropping_pressed = FALSE;
10883     player->drop_pressed_delay = 0;
10884
10885     CheckSingleStepMode(player);
10886
10887     return 0;
10888   }
10889 }
10890
10891 static void CheckLevelTime()
10892 {
10893   int i;
10894
10895   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10896   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10897   {
10898     if (level.native_em_level->lev->home == 0)  /* all players at home */
10899     {
10900       PlayerWins(local_player);
10901
10902       AllPlayersGone = TRUE;
10903
10904       level.native_em_level->lev->home = -1;
10905     }
10906
10907     if (level.native_em_level->ply[0]->alive == 0 &&
10908         level.native_em_level->ply[1]->alive == 0 &&
10909         level.native_em_level->ply[2]->alive == 0 &&
10910         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10911       AllPlayersGone = TRUE;
10912   }
10913   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10914   {
10915     if (game_sp.LevelSolved &&
10916         !game_sp.GameOver)                              /* game won */
10917     {
10918       PlayerWins(local_player);
10919
10920       game_sp.GameOver = TRUE;
10921
10922       AllPlayersGone = TRUE;
10923     }
10924
10925     if (game_sp.GameOver)                               /* game lost */
10926       AllPlayersGone = TRUE;
10927   }
10928
10929   if (TimeFrames >= FRAMES_PER_SECOND)
10930   {
10931     TimeFrames = 0;
10932     TapeTime++;
10933
10934     for (i = 0; i < MAX_PLAYERS; i++)
10935     {
10936       struct PlayerInfo *player = &stored_player[i];
10937
10938       if (SHIELD_ON(player))
10939       {
10940         player->shield_normal_time_left--;
10941
10942         if (player->shield_deadly_time_left > 0)
10943           player->shield_deadly_time_left--;
10944       }
10945     }
10946
10947     if (!local_player->LevelSolved && !level.use_step_counter)
10948     {
10949       TimePlayed++;
10950
10951       if (TimeLeft > 0)
10952       {
10953         TimeLeft--;
10954
10955         if (TimeLeft <= 10 && setup.time_limit)
10956           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10957
10958         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10959            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10960
10961         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10962
10963         if (!TimeLeft && setup.time_limit)
10964         {
10965           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10966             level.native_em_level->lev->killed_out_of_time = TRUE;
10967           else
10968             for (i = 0; i < MAX_PLAYERS; i++)
10969               KillPlayer(&stored_player[i]);
10970         }
10971       }
10972       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10973       {
10974         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10975       }
10976
10977       level.native_em_level->lev->time =
10978         (game.no_time_limit ? TimePlayed : TimeLeft);
10979     }
10980
10981     if (tape.recording || tape.playing)
10982       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10983   }
10984
10985   if (tape.recording || tape.playing)
10986     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10987
10988   UpdateAndDisplayGameControlValues();
10989 }
10990
10991 void AdvanceFrameAndPlayerCounters(int player_nr)
10992 {
10993   int i;
10994
10995   /* advance frame counters (global frame counter and time frame counter) */
10996   FrameCounter++;
10997   TimeFrames++;
10998
10999   /* advance player counters (counters for move delay, move animation etc.) */
11000   for (i = 0; i < MAX_PLAYERS; i++)
11001   {
11002     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11003     int move_delay_value = stored_player[i].move_delay_value;
11004     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11005
11006     if (!advance_player_counters)       /* not all players may be affected */
11007       continue;
11008
11009     if (move_frames == 0)       /* less than one move per game frame */
11010     {
11011       int stepsize = TILEX / move_delay_value;
11012       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11013       int count = (stored_player[i].is_moving ?
11014                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11015
11016       if (count % delay == 0)
11017         move_frames = 1;
11018     }
11019
11020     stored_player[i].Frame += move_frames;
11021
11022     if (stored_player[i].MovPos != 0)
11023       stored_player[i].StepFrame += move_frames;
11024
11025     if (stored_player[i].move_delay > 0)
11026       stored_player[i].move_delay--;
11027
11028     /* due to bugs in previous versions, counter must count up, not down */
11029     if (stored_player[i].push_delay != -1)
11030       stored_player[i].push_delay++;
11031
11032     if (stored_player[i].drop_delay > 0)
11033       stored_player[i].drop_delay--;
11034
11035     if (stored_player[i].is_dropping_pressed)
11036       stored_player[i].drop_pressed_delay++;
11037   }
11038 }
11039
11040 void StartGameActions(boolean init_network_game, boolean record_tape,
11041                       int random_seed)
11042 {
11043   unsigned int new_random_seed = InitRND(random_seed);
11044
11045   if (record_tape)
11046     TapeStartRecording(new_random_seed);
11047
11048 #if defined(NETWORK_AVALIABLE)
11049   if (init_network_game)
11050   {
11051     SendToServer_StartPlaying();
11052
11053     return;
11054   }
11055 #endif
11056
11057   InitGame();
11058 }
11059
11060 void GameActions()
11061 {
11062   static unsigned int game_frame_delay = 0;
11063   unsigned int game_frame_delay_value;
11064   byte *recorded_player_action;
11065   byte summarized_player_action = 0;
11066   byte tape_action[MAX_PLAYERS];
11067   int i;
11068
11069   /* detect endless loops, caused by custom element programming */
11070   if (recursion_loop_detected && recursion_loop_depth == 0)
11071   {
11072     char *message = getStringCat3("Internal Error! Element ",
11073                                   EL_NAME(recursion_loop_element),
11074                                   " caused endless loop! Quit the game?");
11075
11076     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11077           EL_NAME(recursion_loop_element));
11078
11079     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11080
11081     recursion_loop_detected = FALSE;    /* if game should be continued */
11082
11083     free(message);
11084
11085     return;
11086   }
11087
11088   if (game.restart_level)
11089     StartGameActions(options.network, setup.autorecord, level.random_seed);
11090
11091   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11092   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11093   {
11094     if (level.native_em_level->lev->home == 0)  /* all players at home */
11095     {
11096       PlayerWins(local_player);
11097
11098       AllPlayersGone = TRUE;
11099
11100       level.native_em_level->lev->home = -1;
11101     }
11102
11103     if (level.native_em_level->ply[0]->alive == 0 &&
11104         level.native_em_level->ply[1]->alive == 0 &&
11105         level.native_em_level->ply[2]->alive == 0 &&
11106         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11107       AllPlayersGone = TRUE;
11108   }
11109   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11110   {
11111     if (game_sp.LevelSolved &&
11112         !game_sp.GameOver)                              /* game won */
11113     {
11114       PlayerWins(local_player);
11115
11116       game_sp.GameOver = TRUE;
11117
11118       AllPlayersGone = TRUE;
11119     }
11120
11121     if (game_sp.GameOver)                               /* game lost */
11122       AllPlayersGone = TRUE;
11123   }
11124
11125   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11126     GameWon();
11127
11128   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11129     TapeStop();
11130
11131   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11132     return;
11133
11134   game_frame_delay_value =
11135     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11136
11137   if (tape.playing && tape.warp_forward && !tape.pausing)
11138     game_frame_delay_value = 0;
11139
11140 #if 0
11141   /* ---------- main game synchronization point ---------- */
11142
11143   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11144
11145   printf("::: skip == %d\n", skip);
11146
11147 #else
11148   /* ---------- main game synchronization point ---------- */
11149
11150   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11151 #endif
11152
11153   if (network_playing && !network_player_action_received)
11154   {
11155     /* try to get network player actions in time */
11156
11157 #if defined(NETWORK_AVALIABLE)
11158     /* last chance to get network player actions without main loop delay */
11159     HandleNetworking();
11160 #endif
11161
11162     /* game was quit by network peer */
11163     if (game_status != GAME_MODE_PLAYING)
11164       return;
11165
11166     if (!network_player_action_received)
11167       return;           /* failed to get network player actions in time */
11168
11169     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11170   }
11171
11172   if (tape.pausing)
11173     return;
11174
11175   /* at this point we know that we really continue executing the game */
11176
11177   network_player_action_received = FALSE;
11178
11179   /* when playing tape, read previously recorded player input from tape data */
11180   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11181
11182   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11183   if (tape.pausing)
11184     return;
11185
11186   if (tape.set_centered_player)
11187   {
11188     game.centered_player_nr_next = tape.centered_player_nr_next;
11189     game.set_centered_player = TRUE;
11190   }
11191
11192   for (i = 0; i < MAX_PLAYERS; i++)
11193   {
11194     summarized_player_action |= stored_player[i].action;
11195
11196     if (!network_playing && (game.team_mode || tape.playing))
11197       stored_player[i].effective_action = stored_player[i].action;
11198   }
11199
11200 #if defined(NETWORK_AVALIABLE)
11201   if (network_playing)
11202     SendToServer_MovePlayer(summarized_player_action);
11203 #endif
11204
11205   if (!options.network && !game.team_mode)
11206     local_player->effective_action = summarized_player_action;
11207
11208   if (tape.recording &&
11209       setup.team_mode &&
11210       setup.input_on_focus &&
11211       game.centered_player_nr != -1)
11212   {
11213     for (i = 0; i < MAX_PLAYERS; i++)
11214       stored_player[i].effective_action =
11215         (i == game.centered_player_nr ? summarized_player_action : 0);
11216   }
11217
11218   if (recorded_player_action != NULL)
11219     for (i = 0; i < MAX_PLAYERS; i++)
11220       stored_player[i].effective_action = recorded_player_action[i];
11221
11222   for (i = 0; i < MAX_PLAYERS; i++)
11223   {
11224     tape_action[i] = stored_player[i].effective_action;
11225
11226     /* (this may happen in the RND game engine if a player was not present on
11227        the playfield on level start, but appeared later from a custom element */
11228     if (setup.team_mode &&
11229         tape.recording &&
11230         tape_action[i] &&
11231         !tape.player_participates[i])
11232       tape.player_participates[i] = TRUE;
11233   }
11234
11235   /* only record actions from input devices, but not programmed actions */
11236   if (tape.recording)
11237     TapeRecordAction(tape_action);
11238
11239 #if USE_NEW_PLAYER_ASSIGNMENTS
11240   // !!! also map player actions in single player mode !!!
11241   // if (game.team_mode)
11242   {
11243     byte mapped_action[MAX_PLAYERS];
11244
11245 #if DEBUG_PLAYER_ACTIONS
11246     printf(":::");
11247     for (i = 0; i < MAX_PLAYERS; i++)
11248       printf(" %d, ", stored_player[i].effective_action);
11249 #endif
11250
11251     for (i = 0; i < MAX_PLAYERS; i++)
11252       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11253
11254     for (i = 0; i < MAX_PLAYERS; i++)
11255       stored_player[i].effective_action = mapped_action[i];
11256
11257 #if DEBUG_PLAYER_ACTIONS
11258     printf(" =>");
11259     for (i = 0; i < MAX_PLAYERS; i++)
11260       printf(" %d, ", stored_player[i].effective_action);
11261     printf("\n");
11262 #endif
11263   }
11264 #if DEBUG_PLAYER_ACTIONS
11265   else
11266   {
11267     printf(":::");
11268     for (i = 0; i < MAX_PLAYERS; i++)
11269       printf(" %d, ", stored_player[i].effective_action);
11270     printf("\n");
11271   }
11272 #endif
11273 #endif
11274
11275   for (i = 0; i < MAX_PLAYERS; i++)
11276   {
11277     // allow engine snapshot in case of changed movement attempt
11278     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11279         (stored_player[i].effective_action & KEY_MOTION))
11280       game.snapshot.changed_action = TRUE;
11281
11282     // allow engine snapshot in case of snapping/dropping attempt
11283     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11284         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11285       game.snapshot.changed_action = TRUE;
11286
11287     game.snapshot.last_action[i] = stored_player[i].effective_action;
11288   }
11289
11290   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11291   {
11292     GameActions_EM_Main();
11293   }
11294   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11295   {
11296     GameActions_SP_Main();
11297   }
11298   else
11299   {
11300     GameActions_RND_Main();
11301   }
11302
11303   BlitScreenToBitmap(backbuffer);
11304
11305   CheckLevelTime();
11306
11307   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11308
11309   if (options.debug)                    /* calculate frames per second */
11310   {
11311     static unsigned int fps_counter = 0;
11312     static int fps_frames = 0;
11313     unsigned int fps_delay_ms = Counter() - fps_counter;
11314
11315     fps_frames++;
11316
11317     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11318     {
11319       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11320
11321       fps_frames = 0;
11322       fps_counter = Counter();
11323     }
11324
11325     redraw_mask |= REDRAW_FPS;
11326   }
11327 }
11328
11329 void GameActions_EM_Main()
11330 {
11331   byte effective_action[MAX_PLAYERS];
11332   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11333   int i;
11334
11335   for (i = 0; i < MAX_PLAYERS; i++)
11336     effective_action[i] = stored_player[i].effective_action;
11337
11338   GameActions_EM(effective_action, warp_mode);
11339 }
11340
11341 void GameActions_SP_Main()
11342 {
11343   byte effective_action[MAX_PLAYERS];
11344   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11345   int i;
11346
11347   for (i = 0; i < MAX_PLAYERS; i++)
11348     effective_action[i] = stored_player[i].effective_action;
11349
11350   GameActions_SP(effective_action, warp_mode);
11351 }
11352
11353 void GameActions_RND_Main()
11354 {
11355   GameActions_RND();
11356 }
11357
11358 void GameActions_RND()
11359 {
11360   int magic_wall_x = 0, magic_wall_y = 0;
11361   int i, x, y, element, graphic;
11362
11363   InitPlayfieldScanModeVars();
11364
11365   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11366   {
11367     SCAN_PLAYFIELD(x, y)
11368     {
11369       ChangeCount[x][y] = 0;
11370       ChangeEvent[x][y] = -1;
11371     }
11372   }
11373
11374   if (game.set_centered_player)
11375   {
11376     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11377
11378     /* switching to "all players" only possible if all players fit to screen */
11379     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11380     {
11381       game.centered_player_nr_next = game.centered_player_nr;
11382       game.set_centered_player = FALSE;
11383     }
11384
11385     /* do not switch focus to non-existing (or non-active) player */
11386     if (game.centered_player_nr_next >= 0 &&
11387         !stored_player[game.centered_player_nr_next].active)
11388     {
11389       game.centered_player_nr_next = game.centered_player_nr;
11390       game.set_centered_player = FALSE;
11391     }
11392   }
11393
11394   if (game.set_centered_player &&
11395       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11396   {
11397     int sx, sy;
11398
11399     if (game.centered_player_nr_next == -1)
11400     {
11401       setScreenCenteredToAllPlayers(&sx, &sy);
11402     }
11403     else
11404     {
11405       sx = stored_player[game.centered_player_nr_next].jx;
11406       sy = stored_player[game.centered_player_nr_next].jy;
11407     }
11408
11409     game.centered_player_nr = game.centered_player_nr_next;
11410     game.set_centered_player = FALSE;
11411
11412     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11413     DrawGameDoorValues();
11414   }
11415
11416   for (i = 0; i < MAX_PLAYERS; i++)
11417   {
11418     int actual_player_action = stored_player[i].effective_action;
11419
11420 #if 1
11421     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11422        - rnd_equinox_tetrachloride 048
11423        - rnd_equinox_tetrachloride_ii 096
11424        - rnd_emanuel_schmieg 002
11425        - doctor_sloan_ww 001, 020
11426     */
11427     if (stored_player[i].MovPos == 0)
11428       CheckGravityMovement(&stored_player[i]);
11429 #endif
11430
11431     /* overwrite programmed action with tape action */
11432     if (stored_player[i].programmed_action)
11433       actual_player_action = stored_player[i].programmed_action;
11434
11435     PlayerActions(&stored_player[i], actual_player_action);
11436
11437     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11438   }
11439
11440   ScrollScreen(NULL, SCROLL_GO_ON);
11441
11442   /* for backwards compatibility, the following code emulates a fixed bug that
11443      occured when pushing elements (causing elements that just made their last
11444      pushing step to already (if possible) make their first falling step in the
11445      same game frame, which is bad); this code is also needed to use the famous
11446      "spring push bug" which is used in older levels and might be wanted to be
11447      used also in newer levels, but in this case the buggy pushing code is only
11448      affecting the "spring" element and no other elements */
11449
11450   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11451   {
11452     for (i = 0; i < MAX_PLAYERS; i++)
11453     {
11454       struct PlayerInfo *player = &stored_player[i];
11455       int x = player->jx;
11456       int y = player->jy;
11457
11458       if (player->active && player->is_pushing && player->is_moving &&
11459           IS_MOVING(x, y) &&
11460           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11461            Feld[x][y] == EL_SPRING))
11462       {
11463         ContinueMoving(x, y);
11464
11465         /* continue moving after pushing (this is actually a bug) */
11466         if (!IS_MOVING(x, y))
11467           Stop[x][y] = FALSE;
11468       }
11469     }
11470   }
11471
11472 #if 1
11473   printf("::: %d", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
11474 #endif
11475
11476   SCAN_PLAYFIELD(x, y)
11477   {
11478     ChangeCount[x][y] = 0;
11479     ChangeEvent[x][y] = -1;
11480
11481     /* this must be handled before main playfield loop */
11482     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11483     {
11484       MovDelay[x][y]--;
11485       if (MovDelay[x][y] <= 0)
11486         RemoveField(x, y);
11487     }
11488
11489     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11490     {
11491       MovDelay[x][y]--;
11492       if (MovDelay[x][y] <= 0)
11493       {
11494         RemoveField(x, y);
11495         TEST_DrawLevelField(x, y);
11496
11497         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11498       }
11499     }
11500
11501 #if DEBUG
11502     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11503     {
11504       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11505       printf("GameActions(): This should never happen!\n");
11506
11507       ChangePage[x][y] = -1;
11508     }
11509 #endif
11510
11511     Stop[x][y] = FALSE;
11512     if (WasJustMoving[x][y] > 0)
11513       WasJustMoving[x][y]--;
11514     if (WasJustFalling[x][y] > 0)
11515       WasJustFalling[x][y]--;
11516     if (CheckCollision[x][y] > 0)
11517       CheckCollision[x][y]--;
11518     if (CheckImpact[x][y] > 0)
11519       CheckImpact[x][y]--;
11520
11521     GfxFrame[x][y]++;
11522
11523     /* reset finished pushing action (not done in ContinueMoving() to allow
11524        continuous pushing animation for elements with zero push delay) */
11525     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11526     {
11527       ResetGfxAnimation(x, y);
11528       TEST_DrawLevelField(x, y);
11529     }
11530
11531 #if DEBUG
11532     if (IS_BLOCKED(x, y))
11533     {
11534       int oldx, oldy;
11535
11536       Blocked2Moving(x, y, &oldx, &oldy);
11537       if (!IS_MOVING(oldx, oldy))
11538       {
11539         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11540         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11541         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11542         printf("GameActions(): This should never happen!\n");
11543       }
11544     }
11545 #endif
11546   }
11547
11548 #if 1
11549   printf(" -> %d", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
11550 #endif
11551
11552   SCAN_PLAYFIELD(x, y)
11553   {
11554     element = Feld[x][y];
11555     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11556
11557     ResetGfxFrame(x, y, TRUE);
11558
11559     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11560         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11561       ResetRandomAnimationValue(x, y);
11562
11563     SetRandomAnimationValue(x, y);
11564
11565     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11566
11567     if (IS_INACTIVE(element))
11568     {
11569       if (IS_ANIMATED(graphic))
11570         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11571
11572       continue;
11573     }
11574
11575     /* this may take place after moving, so 'element' may have changed */
11576     if (IS_CHANGING(x, y) &&
11577         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11578     {
11579       int page = element_info[element].event_page_nr[CE_DELAY];
11580
11581       HandleElementChange(x, y, page);
11582
11583       element = Feld[x][y];
11584       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11585     }
11586
11587     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11588     {
11589       StartMoving(x, y);
11590
11591       element = Feld[x][y];
11592       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11593
11594       if (IS_ANIMATED(graphic) &&
11595           !IS_MOVING(x, y) &&
11596           !Stop[x][y])
11597         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11598
11599       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11600         TEST_DrawTwinkleOnField(x, y);
11601     }
11602     else if ((element == EL_ACID ||
11603               element == EL_EXIT_OPEN ||
11604               element == EL_EM_EXIT_OPEN ||
11605               element == EL_SP_EXIT_OPEN ||
11606               element == EL_STEEL_EXIT_OPEN ||
11607               element == EL_EM_STEEL_EXIT_OPEN ||
11608               element == EL_SP_TERMINAL ||
11609               element == EL_SP_TERMINAL_ACTIVE ||
11610               element == EL_EXTRA_TIME ||
11611               element == EL_SHIELD_NORMAL ||
11612               element == EL_SHIELD_DEADLY) &&
11613              IS_ANIMATED(graphic))
11614       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11615     else if (IS_MOVING(x, y))
11616       ContinueMoving(x, y);
11617     else if (IS_ACTIVE_BOMB(element))
11618       CheckDynamite(x, y);
11619     else if (element == EL_AMOEBA_GROWING)
11620       AmoebeWaechst(x, y);
11621     else if (element == EL_AMOEBA_SHRINKING)
11622       AmoebaDisappearing(x, y);
11623
11624 #if !USE_NEW_AMOEBA_CODE
11625     else if (IS_AMOEBALIVE(element))
11626       AmoebeAbleger(x, y);
11627 #endif
11628
11629     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11630       Life(x, y);
11631     else if (element == EL_EXIT_CLOSED)
11632       CheckExit(x, y);
11633     else if (element == EL_EM_EXIT_CLOSED)
11634       CheckExitEM(x, y);
11635     else if (element == EL_STEEL_EXIT_CLOSED)
11636       CheckExitSteel(x, y);
11637     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11638       CheckExitSteelEM(x, y);
11639     else if (element == EL_SP_EXIT_CLOSED)
11640       CheckExitSP(x, y);
11641     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11642              element == EL_EXPANDABLE_STEELWALL_GROWING)
11643       MauerWaechst(x, y);
11644     else if (element == EL_EXPANDABLE_WALL ||
11645              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11646              element == EL_EXPANDABLE_WALL_VERTICAL ||
11647              element == EL_EXPANDABLE_WALL_ANY ||
11648              element == EL_BD_EXPANDABLE_WALL)
11649       MauerAbleger(x, y);
11650     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11651              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11652              element == EL_EXPANDABLE_STEELWALL_ANY)
11653       MauerAblegerStahl(x, y);
11654     else if (element == EL_FLAMES)
11655       CheckForDragon(x, y);
11656     else if (element == EL_EXPLOSION)
11657       ; /* drawing of correct explosion animation is handled separately */
11658     else if (element == EL_ELEMENT_SNAPPING ||
11659              element == EL_DIAGONAL_SHRINKING ||
11660              element == EL_DIAGONAL_GROWING)
11661     {
11662       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11663
11664       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11665     }
11666     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11667       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11668
11669     if (IS_BELT_ACTIVE(element))
11670       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11671
11672     if (game.magic_wall_active)
11673     {
11674       int jx = local_player->jx, jy = local_player->jy;
11675
11676       /* play the element sound at the position nearest to the player */
11677       if ((element == EL_MAGIC_WALL_FULL ||
11678            element == EL_MAGIC_WALL_ACTIVE ||
11679            element == EL_MAGIC_WALL_EMPTYING ||
11680            element == EL_BD_MAGIC_WALL_FULL ||
11681            element == EL_BD_MAGIC_WALL_ACTIVE ||
11682            element == EL_BD_MAGIC_WALL_EMPTYING ||
11683            element == EL_DC_MAGIC_WALL_FULL ||
11684            element == EL_DC_MAGIC_WALL_ACTIVE ||
11685            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11686           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11687       {
11688         magic_wall_x = x;
11689         magic_wall_y = y;
11690       }
11691     }
11692   }
11693
11694 #if 1
11695   printf(" -> %d\n", GfxFrame[DEBUG_GFXFRAME_X][DEBUG_GFXFRAME_Y]);
11696 #endif
11697
11698 #if USE_NEW_AMOEBA_CODE
11699   /* new experimental amoeba growth stuff */
11700   if (!(FrameCounter % 8))
11701   {
11702     static unsigned int random = 1684108901;
11703
11704     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11705     {
11706       x = RND(lev_fieldx);
11707       y = RND(lev_fieldy);
11708       element = Feld[x][y];
11709
11710       if (!IS_PLAYER(x,y) &&
11711           (element == EL_EMPTY ||
11712            CAN_GROW_INTO(element) ||
11713            element == EL_QUICKSAND_EMPTY ||
11714            element == EL_QUICKSAND_FAST_EMPTY ||
11715            element == EL_ACID_SPLASH_LEFT ||
11716            element == EL_ACID_SPLASH_RIGHT))
11717       {
11718         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11719             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11720             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11721             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11722           Feld[x][y] = EL_AMOEBA_DROP;
11723       }
11724
11725       random = random * 129 + 1;
11726     }
11727   }
11728 #endif
11729
11730   game.explosions_delayed = FALSE;
11731
11732   SCAN_PLAYFIELD(x, y)
11733   {
11734     element = Feld[x][y];
11735
11736     if (ExplodeField[x][y])
11737       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11738     else if (element == EL_EXPLOSION)
11739       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11740
11741     ExplodeField[x][y] = EX_TYPE_NONE;
11742   }
11743
11744   game.explosions_delayed = TRUE;
11745
11746   if (game.magic_wall_active)
11747   {
11748     if (!(game.magic_wall_time_left % 4))
11749     {
11750       int element = Feld[magic_wall_x][magic_wall_y];
11751
11752       if (element == EL_BD_MAGIC_WALL_FULL ||
11753           element == EL_BD_MAGIC_WALL_ACTIVE ||
11754           element == EL_BD_MAGIC_WALL_EMPTYING)
11755         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11756       else if (element == EL_DC_MAGIC_WALL_FULL ||
11757                element == EL_DC_MAGIC_WALL_ACTIVE ||
11758                element == EL_DC_MAGIC_WALL_EMPTYING)
11759         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11760       else
11761         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11762     }
11763
11764     if (game.magic_wall_time_left > 0)
11765     {
11766       game.magic_wall_time_left--;
11767
11768       if (!game.magic_wall_time_left)
11769       {
11770         SCAN_PLAYFIELD(x, y)
11771         {
11772           element = Feld[x][y];
11773
11774           if (element == EL_MAGIC_WALL_ACTIVE ||
11775               element == EL_MAGIC_WALL_FULL)
11776           {
11777             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11778             TEST_DrawLevelField(x, y);
11779           }
11780           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11781                    element == EL_BD_MAGIC_WALL_FULL)
11782           {
11783             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11784             TEST_DrawLevelField(x, y);
11785           }
11786           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11787                    element == EL_DC_MAGIC_WALL_FULL)
11788           {
11789             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11790             TEST_DrawLevelField(x, y);
11791           }
11792         }
11793
11794         game.magic_wall_active = FALSE;
11795       }
11796     }
11797   }
11798
11799   if (game.light_time_left > 0)
11800   {
11801     game.light_time_left--;
11802
11803     if (game.light_time_left == 0)
11804       RedrawAllLightSwitchesAndInvisibleElements();
11805   }
11806
11807   if (game.timegate_time_left > 0)
11808   {
11809     game.timegate_time_left--;
11810
11811     if (game.timegate_time_left == 0)
11812       CloseAllOpenTimegates();
11813   }
11814
11815   if (game.lenses_time_left > 0)
11816   {
11817     game.lenses_time_left--;
11818
11819     if (game.lenses_time_left == 0)
11820       RedrawAllInvisibleElementsForLenses();
11821   }
11822
11823   if (game.magnify_time_left > 0)
11824   {
11825     game.magnify_time_left--;
11826
11827     if (game.magnify_time_left == 0)
11828       RedrawAllInvisibleElementsForMagnifier();
11829   }
11830
11831   for (i = 0; i < MAX_PLAYERS; i++)
11832   {
11833     struct PlayerInfo *player = &stored_player[i];
11834
11835     if (SHIELD_ON(player))
11836     {
11837       if (player->shield_deadly_time_left)
11838         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11839       else if (player->shield_normal_time_left)
11840         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11841     }
11842   }
11843
11844 #if USE_DELAYED_GFX_REDRAW
11845   SCAN_PLAYFIELD(x, y)
11846   {
11847     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11848     {
11849       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11850          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11851
11852       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11853         DrawLevelField(x, y);
11854
11855       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11856         DrawLevelFieldCrumbled(x, y);
11857
11858       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11859         DrawLevelFieldCrumbledNeighbours(x, y);
11860
11861       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11862         DrawTwinkleOnField(x, y);
11863     }
11864
11865     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11866   }
11867 #endif
11868
11869   DrawAllPlayers();
11870   PlayAllPlayersSound();
11871
11872   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11873   {
11874     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11875
11876     local_player->show_envelope = 0;
11877   }
11878
11879   /* use random number generator in every frame to make it less predictable */
11880   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11881     RND(1);
11882 }
11883
11884 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11885 {
11886   int min_x = x, min_y = y, max_x = x, max_y = y;
11887   int i;
11888
11889   for (i = 0; i < MAX_PLAYERS; i++)
11890   {
11891     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11892
11893     if (!stored_player[i].active || &stored_player[i] == player)
11894       continue;
11895
11896     min_x = MIN(min_x, jx);
11897     min_y = MIN(min_y, jy);
11898     max_x = MAX(max_x, jx);
11899     max_y = MAX(max_y, jy);
11900   }
11901
11902   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11903 }
11904
11905 static boolean AllPlayersInVisibleScreen()
11906 {
11907   int i;
11908
11909   for (i = 0; i < MAX_PLAYERS; i++)
11910   {
11911     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11912
11913     if (!stored_player[i].active)
11914       continue;
11915
11916     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11917       return FALSE;
11918   }
11919
11920   return TRUE;
11921 }
11922
11923 void ScrollLevel(int dx, int dy)
11924 {
11925   int scroll_offset = 2 * TILEX_VAR;
11926   int x, y;
11927
11928   BlitBitmap(drawto_field, drawto_field,
11929              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11930              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11931              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11932              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11933              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11934              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11935
11936   if (dx != 0)
11937   {
11938     x = (dx == 1 ? BX1 : BX2);
11939     for (y = BY1; y <= BY2; y++)
11940       DrawScreenField(x, y);
11941   }
11942
11943   if (dy != 0)
11944   {
11945     y = (dy == 1 ? BY1 : BY2);
11946     for (x = BX1; x <= BX2; x++)
11947       DrawScreenField(x, y);
11948   }
11949
11950   redraw_mask |= REDRAW_FIELD;
11951 }
11952
11953 static boolean canFallDown(struct PlayerInfo *player)
11954 {
11955   int jx = player->jx, jy = player->jy;
11956
11957   return (IN_LEV_FIELD(jx, jy + 1) &&
11958           (IS_FREE(jx, jy + 1) ||
11959            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11960           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11961           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11962 }
11963
11964 static boolean canPassField(int x, int y, int move_dir)
11965 {
11966   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11967   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11968   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11969   int nextx = x + dx;
11970   int nexty = y + dy;
11971   int element = Feld[x][y];
11972
11973   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11974           !CAN_MOVE(element) &&
11975           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11976           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11977           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11978 }
11979
11980 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11981 {
11982   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11983   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11984   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11985   int newx = x + dx;
11986   int newy = y + dy;
11987
11988   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11989           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11990           (IS_DIGGABLE(Feld[newx][newy]) ||
11991            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11992            canPassField(newx, newy, move_dir)));
11993 }
11994
11995 static void CheckGravityMovement(struct PlayerInfo *player)
11996 {
11997   if (player->gravity && !player->programmed_action)
11998   {
11999     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12000     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12001     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12002     int jx = player->jx, jy = player->jy;
12003     boolean player_is_moving_to_valid_field =
12004       (!player_is_snapping &&
12005        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12006         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12007     boolean player_can_fall_down = canFallDown(player);
12008
12009     if (player_can_fall_down &&
12010         !player_is_moving_to_valid_field)
12011       player->programmed_action = MV_DOWN;
12012   }
12013 }
12014
12015 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12016 {
12017   return CheckGravityMovement(player);
12018
12019   if (player->gravity && !player->programmed_action)
12020   {
12021     int jx = player->jx, jy = player->jy;
12022     boolean field_under_player_is_free =
12023       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12024     boolean player_is_standing_on_valid_field =
12025       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12026        (IS_WALKABLE(Feld[jx][jy]) &&
12027         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12028
12029     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12030       player->programmed_action = MV_DOWN;
12031   }
12032 }
12033
12034 /*
12035   MovePlayerOneStep()
12036   -----------------------------------------------------------------------------
12037   dx, dy:               direction (non-diagonal) to try to move the player to
12038   real_dx, real_dy:     direction as read from input device (can be diagonal)
12039 */
12040
12041 boolean MovePlayerOneStep(struct PlayerInfo *player,
12042                           int dx, int dy, int real_dx, int real_dy)
12043 {
12044   int jx = player->jx, jy = player->jy;
12045   int new_jx = jx + dx, new_jy = jy + dy;
12046   int can_move;
12047   boolean player_can_move = !player->cannot_move;
12048
12049   if (!player->active || (!dx && !dy))
12050     return MP_NO_ACTION;
12051
12052   player->MovDir = (dx < 0 ? MV_LEFT :
12053                     dx > 0 ? MV_RIGHT :
12054                     dy < 0 ? MV_UP :
12055                     dy > 0 ? MV_DOWN :  MV_NONE);
12056
12057   if (!IN_LEV_FIELD(new_jx, new_jy))
12058     return MP_NO_ACTION;
12059
12060   if (!player_can_move)
12061   {
12062     if (player->MovPos == 0)
12063     {
12064       player->is_moving = FALSE;
12065       player->is_digging = FALSE;
12066       player->is_collecting = FALSE;
12067       player->is_snapping = FALSE;
12068       player->is_pushing = FALSE;
12069     }
12070   }
12071
12072   if (!options.network && game.centered_player_nr == -1 &&
12073       !AllPlayersInSight(player, new_jx, new_jy))
12074     return MP_NO_ACTION;
12075
12076   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12077   if (can_move != MP_MOVING)
12078     return can_move;
12079
12080   /* check if DigField() has caused relocation of the player */
12081   if (player->jx != jx || player->jy != jy)
12082     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12083
12084   StorePlayer[jx][jy] = 0;
12085   player->last_jx = jx;
12086   player->last_jy = jy;
12087   player->jx = new_jx;
12088   player->jy = new_jy;
12089   StorePlayer[new_jx][new_jy] = player->element_nr;
12090
12091   if (player->move_delay_value_next != -1)
12092   {
12093     player->move_delay_value = player->move_delay_value_next;
12094     player->move_delay_value_next = -1;
12095   }
12096
12097   player->MovPos =
12098     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12099
12100   player->step_counter++;
12101
12102   PlayerVisit[jx][jy] = FrameCounter;
12103
12104   player->is_moving = TRUE;
12105
12106 #if 1
12107   /* should better be called in MovePlayer(), but this breaks some tapes */
12108   ScrollPlayer(player, SCROLL_INIT);
12109 #endif
12110
12111   return MP_MOVING;
12112 }
12113
12114 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12115 {
12116   int jx = player->jx, jy = player->jy;
12117   int old_jx = jx, old_jy = jy;
12118   int moved = MP_NO_ACTION;
12119
12120   if (!player->active)
12121     return FALSE;
12122
12123   if (!dx && !dy)
12124   {
12125     if (player->MovPos == 0)
12126     {
12127       player->is_moving = FALSE;
12128       player->is_digging = FALSE;
12129       player->is_collecting = FALSE;
12130       player->is_snapping = FALSE;
12131       player->is_pushing = FALSE;
12132     }
12133
12134     return FALSE;
12135   }
12136
12137   if (player->move_delay > 0)
12138     return FALSE;
12139
12140   player->move_delay = -1;              /* set to "uninitialized" value */
12141
12142   /* store if player is automatically moved to next field */
12143   player->is_auto_moving = (player->programmed_action != MV_NONE);
12144
12145   /* remove the last programmed player action */
12146   player->programmed_action = 0;
12147
12148   if (player->MovPos)
12149   {
12150     /* should only happen if pre-1.2 tape recordings are played */
12151     /* this is only for backward compatibility */
12152
12153     int original_move_delay_value = player->move_delay_value;
12154
12155 #if DEBUG
12156     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12157            tape.counter);
12158 #endif
12159
12160     /* scroll remaining steps with finest movement resolution */
12161     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12162
12163     while (player->MovPos)
12164     {
12165       ScrollPlayer(player, SCROLL_GO_ON);
12166       ScrollScreen(NULL, SCROLL_GO_ON);
12167
12168       AdvanceFrameAndPlayerCounters(player->index_nr);
12169
12170       DrawAllPlayers();
12171       BackToFront();
12172     }
12173
12174     player->move_delay_value = original_move_delay_value;
12175   }
12176
12177   player->is_active = FALSE;
12178
12179   if (player->last_move_dir & MV_HORIZONTAL)
12180   {
12181     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12182       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12183   }
12184   else
12185   {
12186     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12187       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12188   }
12189
12190   if (!moved && !player->is_active)
12191   {
12192     player->is_moving = FALSE;
12193     player->is_digging = FALSE;
12194     player->is_collecting = FALSE;
12195     player->is_snapping = FALSE;
12196     player->is_pushing = FALSE;
12197   }
12198
12199   jx = player->jx;
12200   jy = player->jy;
12201
12202   if (moved & MP_MOVING && !ScreenMovPos &&
12203       (player->index_nr == game.centered_player_nr ||
12204        game.centered_player_nr == -1))
12205   {
12206     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12207     int offset = game.scroll_delay_value;
12208
12209     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12210     {
12211       /* actual player has left the screen -- scroll in that direction */
12212       if (jx != old_jx)         /* player has moved horizontally */
12213         scroll_x += (jx - old_jx);
12214       else                      /* player has moved vertically */
12215         scroll_y += (jy - old_jy);
12216     }
12217     else
12218     {
12219       if (jx != old_jx)         /* player has moved horizontally */
12220       {
12221         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12222             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12223           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12224
12225         /* don't scroll over playfield boundaries */
12226         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12227           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12228
12229         /* don't scroll more than one field at a time */
12230         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12231
12232         /* don't scroll against the player's moving direction */
12233         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12234             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12235           scroll_x = old_scroll_x;
12236       }
12237       else                      /* player has moved vertically */
12238       {
12239         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12240             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12241           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12242
12243         /* don't scroll over playfield boundaries */
12244         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12245           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12246
12247         /* don't scroll more than one field at a time */
12248         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12249
12250         /* don't scroll against the player's moving direction */
12251         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12252             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12253           scroll_y = old_scroll_y;
12254       }
12255     }
12256
12257     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12258     {
12259       if (!options.network && game.centered_player_nr == -1 &&
12260           !AllPlayersInVisibleScreen())
12261       {
12262         scroll_x = old_scroll_x;
12263         scroll_y = old_scroll_y;
12264       }
12265       else
12266       {
12267         ScrollScreen(player, SCROLL_INIT);
12268         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12269       }
12270     }
12271   }
12272
12273   player->StepFrame = 0;
12274
12275   if (moved & MP_MOVING)
12276   {
12277     if (old_jx != jx && old_jy == jy)
12278       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12279     else if (old_jx == jx && old_jy != jy)
12280       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12281
12282     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12283
12284     player->last_move_dir = player->MovDir;
12285     player->is_moving = TRUE;
12286     player->is_snapping = FALSE;
12287     player->is_switching = FALSE;
12288     player->is_dropping = FALSE;
12289     player->is_dropping_pressed = FALSE;
12290     player->drop_pressed_delay = 0;
12291
12292 #if 0
12293     /* should better be called here than above, but this breaks some tapes */
12294     ScrollPlayer(player, SCROLL_INIT);
12295 #endif
12296   }
12297   else
12298   {
12299     CheckGravityMovementWhenNotMoving(player);
12300
12301     player->is_moving = FALSE;
12302
12303     /* at this point, the player is allowed to move, but cannot move right now
12304        (e.g. because of something blocking the way) -- ensure that the player
12305        is also allowed to move in the next frame (in old versions before 3.1.1,
12306        the player was forced to wait again for eight frames before next try) */
12307
12308     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12309       player->move_delay = 0;   /* allow direct movement in the next frame */
12310   }
12311
12312   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12313     player->move_delay = player->move_delay_value;
12314
12315   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12316   {
12317     TestIfPlayerTouchesBadThing(jx, jy);
12318     TestIfPlayerTouchesCustomElement(jx, jy);
12319   }
12320
12321   if (!player->active)
12322     RemovePlayer(player);
12323
12324   return moved;
12325 }
12326
12327 void ScrollPlayer(struct PlayerInfo *player, int mode)
12328 {
12329   int jx = player->jx, jy = player->jy;
12330   int last_jx = player->last_jx, last_jy = player->last_jy;
12331   int move_stepsize = TILEX / player->move_delay_value;
12332
12333   if (!player->active)
12334     return;
12335
12336   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12337     return;
12338
12339   if (mode == SCROLL_INIT)
12340   {
12341     player->actual_frame_counter = FrameCounter;
12342     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12343
12344     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12345         Feld[last_jx][last_jy] == EL_EMPTY)
12346     {
12347       int last_field_block_delay = 0;   /* start with no blocking at all */
12348       int block_delay_adjustment = player->block_delay_adjustment;
12349
12350       /* if player blocks last field, add delay for exactly one move */
12351       if (player->block_last_field)
12352       {
12353         last_field_block_delay += player->move_delay_value;
12354
12355         /* when blocking enabled, prevent moving up despite gravity */
12356         if (player->gravity && player->MovDir == MV_UP)
12357           block_delay_adjustment = -1;
12358       }
12359
12360       /* add block delay adjustment (also possible when not blocking) */
12361       last_field_block_delay += block_delay_adjustment;
12362
12363       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12364       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12365     }
12366
12367     if (player->MovPos != 0)    /* player has not yet reached destination */
12368       return;
12369   }
12370   else if (!FrameReached(&player->actual_frame_counter, 1))
12371     return;
12372
12373   if (player->MovPos != 0)
12374   {
12375     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12376     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12377
12378     /* before DrawPlayer() to draw correct player graphic for this case */
12379     if (player->MovPos == 0)
12380       CheckGravityMovement(player);
12381   }
12382
12383   if (player->MovPos == 0)      /* player reached destination field */
12384   {
12385     if (player->move_delay_reset_counter > 0)
12386     {
12387       player->move_delay_reset_counter--;
12388
12389       if (player->move_delay_reset_counter == 0)
12390       {
12391         /* continue with normal speed after quickly moving through gate */
12392         HALVE_PLAYER_SPEED(player);
12393
12394         /* be able to make the next move without delay */
12395         player->move_delay = 0;
12396       }
12397     }
12398
12399     player->last_jx = jx;
12400     player->last_jy = jy;
12401
12402     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12403         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12404         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12405         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12406         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12407         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12408         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12409         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12410     {
12411       DrawPlayer(player);       /* needed here only to cleanup last field */
12412       RemovePlayer(player);
12413
12414       if (local_player->friends_still_needed == 0 ||
12415           IS_SP_ELEMENT(Feld[jx][jy]))
12416         PlayerWins(player);
12417     }
12418
12419     /* this breaks one level: "machine", level 000 */
12420     {
12421       int move_direction = player->MovDir;
12422       int enter_side = MV_DIR_OPPOSITE(move_direction);
12423       int leave_side = move_direction;
12424       int old_jx = last_jx;
12425       int old_jy = last_jy;
12426       int old_element = Feld[old_jx][old_jy];
12427       int new_element = Feld[jx][jy];
12428
12429       if (IS_CUSTOM_ELEMENT(old_element))
12430         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12431                                    CE_LEFT_BY_PLAYER,
12432                                    player->index_bit, leave_side);
12433
12434       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12435                                           CE_PLAYER_LEAVES_X,
12436                                           player->index_bit, leave_side);
12437
12438       if (IS_CUSTOM_ELEMENT(new_element))
12439         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12440                                    player->index_bit, enter_side);
12441
12442       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12443                                           CE_PLAYER_ENTERS_X,
12444                                           player->index_bit, enter_side);
12445
12446       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12447                                         CE_MOVE_OF_X, move_direction);
12448     }
12449
12450     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12451     {
12452       TestIfPlayerTouchesBadThing(jx, jy);
12453       TestIfPlayerTouchesCustomElement(jx, jy);
12454
12455       /* needed because pushed element has not yet reached its destination,
12456          so it would trigger a change event at its previous field location */
12457       if (!player->is_pushing)
12458         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12459
12460       if (!player->active)
12461         RemovePlayer(player);
12462     }
12463
12464     if (!local_player->LevelSolved && level.use_step_counter)
12465     {
12466       int i;
12467
12468       TimePlayed++;
12469
12470       if (TimeLeft > 0)
12471       {
12472         TimeLeft--;
12473
12474         if (TimeLeft <= 10 && setup.time_limit)
12475           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12476
12477         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12478
12479         DisplayGameControlValues();
12480
12481         if (!TimeLeft && setup.time_limit)
12482           for (i = 0; i < MAX_PLAYERS; i++)
12483             KillPlayer(&stored_player[i]);
12484       }
12485       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12486       {
12487         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12488
12489         DisplayGameControlValues();
12490       }
12491     }
12492
12493     if (tape.single_step && tape.recording && !tape.pausing &&
12494         !player->programmed_action)
12495       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12496
12497     if (!player->programmed_action)
12498       CheckSaveEngineSnapshot(player);
12499   }
12500 }
12501
12502 void ScrollScreen(struct PlayerInfo *player, int mode)
12503 {
12504   static unsigned int screen_frame_counter = 0;
12505
12506   if (mode == SCROLL_INIT)
12507   {
12508     /* set scrolling step size according to actual player's moving speed */
12509     ScrollStepSize = TILEX / player->move_delay_value;
12510
12511     screen_frame_counter = FrameCounter;
12512     ScreenMovDir = player->MovDir;
12513     ScreenMovPos = player->MovPos;
12514     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12515     return;
12516   }
12517   else if (!FrameReached(&screen_frame_counter, 1))
12518     return;
12519
12520   if (ScreenMovPos)
12521   {
12522     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12523     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12524     redraw_mask |= REDRAW_FIELD;
12525   }
12526   else
12527     ScreenMovDir = MV_NONE;
12528 }
12529
12530 void TestIfPlayerTouchesCustomElement(int x, int y)
12531 {
12532   static int xy[4][2] =
12533   {
12534     { 0, -1 },
12535     { -1, 0 },
12536     { +1, 0 },
12537     { 0, +1 }
12538   };
12539   static int trigger_sides[4][2] =
12540   {
12541     /* center side       border side */
12542     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12543     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12544     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12545     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12546   };
12547   static int touch_dir[4] =
12548   {
12549     MV_LEFT | MV_RIGHT,
12550     MV_UP   | MV_DOWN,
12551     MV_UP   | MV_DOWN,
12552     MV_LEFT | MV_RIGHT
12553   };
12554   int center_element = Feld[x][y];      /* should always be non-moving! */
12555   int i;
12556
12557   for (i = 0; i < NUM_DIRECTIONS; i++)
12558   {
12559     int xx = x + xy[i][0];
12560     int yy = y + xy[i][1];
12561     int center_side = trigger_sides[i][0];
12562     int border_side = trigger_sides[i][1];
12563     int border_element;
12564
12565     if (!IN_LEV_FIELD(xx, yy))
12566       continue;
12567
12568     if (IS_PLAYER(x, y))                /* player found at center element */
12569     {
12570       struct PlayerInfo *player = PLAYERINFO(x, y);
12571
12572       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12573         border_element = Feld[xx][yy];          /* may be moving! */
12574       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12575         border_element = Feld[xx][yy];
12576       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12577         border_element = MovingOrBlocked2Element(xx, yy);
12578       else
12579         continue;               /* center and border element do not touch */
12580
12581       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12582                                  player->index_bit, border_side);
12583       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12584                                           CE_PLAYER_TOUCHES_X,
12585                                           player->index_bit, border_side);
12586
12587       {
12588         /* use player element that is initially defined in the level playfield,
12589            not the player element that corresponds to the runtime player number
12590            (example: a level that contains EL_PLAYER_3 as the only player would
12591            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12592         int player_element = PLAYERINFO(x, y)->initial_element;
12593
12594         CheckElementChangeBySide(xx, yy, border_element, player_element,
12595                                  CE_TOUCHING_X, border_side);
12596       }
12597     }
12598     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12599     {
12600       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12601
12602       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12603       {
12604         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12605           continue;             /* center and border element do not touch */
12606       }
12607
12608       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12609                                  player->index_bit, center_side);
12610       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12611                                           CE_PLAYER_TOUCHES_X,
12612                                           player->index_bit, center_side);
12613
12614       {
12615         /* use player element that is initially defined in the level playfield,
12616            not the player element that corresponds to the runtime player number
12617            (example: a level that contains EL_PLAYER_3 as the only player would
12618            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12619         int player_element = PLAYERINFO(xx, yy)->initial_element;
12620
12621         CheckElementChangeBySide(x, y, center_element, player_element,
12622                                  CE_TOUCHING_X, center_side);
12623       }
12624
12625       break;
12626     }
12627   }
12628 }
12629
12630 void TestIfElementTouchesCustomElement(int x, int y)
12631 {
12632   static int xy[4][2] =
12633   {
12634     { 0, -1 },
12635     { -1, 0 },
12636     { +1, 0 },
12637     { 0, +1 }
12638   };
12639   static int trigger_sides[4][2] =
12640   {
12641     /* center side      border side */
12642     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12643     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12644     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12645     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12646   };
12647   static int touch_dir[4] =
12648   {
12649     MV_LEFT | MV_RIGHT,
12650     MV_UP   | MV_DOWN,
12651     MV_UP   | MV_DOWN,
12652     MV_LEFT | MV_RIGHT
12653   };
12654   boolean change_center_element = FALSE;
12655   int center_element = Feld[x][y];      /* should always be non-moving! */
12656   int border_element_old[NUM_DIRECTIONS];
12657   int i;
12658
12659   for (i = 0; i < NUM_DIRECTIONS; i++)
12660   {
12661     int xx = x + xy[i][0];
12662     int yy = y + xy[i][1];
12663     int border_element;
12664
12665     border_element_old[i] = -1;
12666
12667     if (!IN_LEV_FIELD(xx, yy))
12668       continue;
12669
12670     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12671       border_element = Feld[xx][yy];    /* may be moving! */
12672     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12673       border_element = Feld[xx][yy];
12674     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12675       border_element = MovingOrBlocked2Element(xx, yy);
12676     else
12677       continue;                 /* center and border element do not touch */
12678
12679     border_element_old[i] = border_element;
12680   }
12681
12682   for (i = 0; i < NUM_DIRECTIONS; i++)
12683   {
12684     int xx = x + xy[i][0];
12685     int yy = y + xy[i][1];
12686     int center_side = trigger_sides[i][0];
12687     int border_element = border_element_old[i];
12688
12689     if (border_element == -1)
12690       continue;
12691
12692     /* check for change of border element */
12693     CheckElementChangeBySide(xx, yy, border_element, center_element,
12694                              CE_TOUCHING_X, center_side);
12695
12696     /* (center element cannot be player, so we dont have to check this here) */
12697   }
12698
12699   for (i = 0; i < NUM_DIRECTIONS; i++)
12700   {
12701     int xx = x + xy[i][0];
12702     int yy = y + xy[i][1];
12703     int border_side = trigger_sides[i][1];
12704     int border_element = border_element_old[i];
12705
12706     if (border_element == -1)
12707       continue;
12708
12709     /* check for change of center element (but change it only once) */
12710     if (!change_center_element)
12711       change_center_element =
12712         CheckElementChangeBySide(x, y, center_element, border_element,
12713                                  CE_TOUCHING_X, border_side);
12714
12715     if (IS_PLAYER(xx, yy))
12716     {
12717       /* use player element that is initially defined in the level playfield,
12718          not the player element that corresponds to the runtime player number
12719          (example: a level that contains EL_PLAYER_3 as the only player would
12720          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12721       int player_element = PLAYERINFO(xx, yy)->initial_element;
12722
12723       CheckElementChangeBySide(x, y, center_element, player_element,
12724                                CE_TOUCHING_X, border_side);
12725     }
12726   }
12727 }
12728
12729 void TestIfElementHitsCustomElement(int x, int y, int direction)
12730 {
12731   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12732   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12733   int hitx = x + dx, hity = y + dy;
12734   int hitting_element = Feld[x][y];
12735   int touched_element;
12736
12737   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12738     return;
12739
12740   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12741                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12742
12743   if (IN_LEV_FIELD(hitx, hity))
12744   {
12745     int opposite_direction = MV_DIR_OPPOSITE(direction);
12746     int hitting_side = direction;
12747     int touched_side = opposite_direction;
12748     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12749                           MovDir[hitx][hity] != direction ||
12750                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12751
12752     object_hit = TRUE;
12753
12754     if (object_hit)
12755     {
12756       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12757                                CE_HITTING_X, touched_side);
12758
12759       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12760                                CE_HIT_BY_X, hitting_side);
12761
12762       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12763                                CE_HIT_BY_SOMETHING, opposite_direction);
12764
12765       if (IS_PLAYER(hitx, hity))
12766       {
12767         /* use player element that is initially defined in the level playfield,
12768            not the player element that corresponds to the runtime player number
12769            (example: a level that contains EL_PLAYER_3 as the only player would
12770            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12771         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12772
12773         CheckElementChangeBySide(x, y, hitting_element, player_element,
12774                                  CE_HITTING_X, touched_side);
12775       }
12776     }
12777   }
12778
12779   /* "hitting something" is also true when hitting the playfield border */
12780   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12781                            CE_HITTING_SOMETHING, direction);
12782 }
12783
12784 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12785 {
12786   int i, kill_x = -1, kill_y = -1;
12787
12788   int bad_element = -1;
12789   static int test_xy[4][2] =
12790   {
12791     { 0, -1 },
12792     { -1, 0 },
12793     { +1, 0 },
12794     { 0, +1 }
12795   };
12796   static int test_dir[4] =
12797   {
12798     MV_UP,
12799     MV_LEFT,
12800     MV_RIGHT,
12801     MV_DOWN
12802   };
12803
12804   for (i = 0; i < NUM_DIRECTIONS; i++)
12805   {
12806     int test_x, test_y, test_move_dir, test_element;
12807
12808     test_x = good_x + test_xy[i][0];
12809     test_y = good_y + test_xy[i][1];
12810
12811     if (!IN_LEV_FIELD(test_x, test_y))
12812       continue;
12813
12814     test_move_dir =
12815       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12816
12817     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12818
12819     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12820        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12821     */
12822     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12823         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12824     {
12825       kill_x = test_x;
12826       kill_y = test_y;
12827       bad_element = test_element;
12828
12829       break;
12830     }
12831   }
12832
12833   if (kill_x != -1 || kill_y != -1)
12834   {
12835     if (IS_PLAYER(good_x, good_y))
12836     {
12837       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12838
12839       if (player->shield_deadly_time_left > 0 &&
12840           !IS_INDESTRUCTIBLE(bad_element))
12841         Bang(kill_x, kill_y);
12842       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12843         KillPlayer(player);
12844     }
12845     else
12846       Bang(good_x, good_y);
12847   }
12848 }
12849
12850 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12851 {
12852   int i, kill_x = -1, kill_y = -1;
12853   int bad_element = Feld[bad_x][bad_y];
12854   static int test_xy[4][2] =
12855   {
12856     { 0, -1 },
12857     { -1, 0 },
12858     { +1, 0 },
12859     { 0, +1 }
12860   };
12861   static int touch_dir[4] =
12862   {
12863     MV_LEFT | MV_RIGHT,
12864     MV_UP   | MV_DOWN,
12865     MV_UP   | MV_DOWN,
12866     MV_LEFT | MV_RIGHT
12867   };
12868   static int test_dir[4] =
12869   {
12870     MV_UP,
12871     MV_LEFT,
12872     MV_RIGHT,
12873     MV_DOWN
12874   };
12875
12876   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12877     return;
12878
12879   for (i = 0; i < NUM_DIRECTIONS; i++)
12880   {
12881     int test_x, test_y, test_move_dir, test_element;
12882
12883     test_x = bad_x + test_xy[i][0];
12884     test_y = bad_y + test_xy[i][1];
12885
12886     if (!IN_LEV_FIELD(test_x, test_y))
12887       continue;
12888
12889     test_move_dir =
12890       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12891
12892     test_element = Feld[test_x][test_y];
12893
12894     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12895        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12896     */
12897     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12898         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12899     {
12900       /* good thing is player or penguin that does not move away */
12901       if (IS_PLAYER(test_x, test_y))
12902       {
12903         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12904
12905         if (bad_element == EL_ROBOT && player->is_moving)
12906           continue;     /* robot does not kill player if he is moving */
12907
12908         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12909         {
12910           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12911             continue;           /* center and border element do not touch */
12912         }
12913
12914         kill_x = test_x;
12915         kill_y = test_y;
12916
12917         break;
12918       }
12919       else if (test_element == EL_PENGUIN)
12920       {
12921         kill_x = test_x;
12922         kill_y = test_y;
12923
12924         break;
12925       }
12926     }
12927   }
12928
12929   if (kill_x != -1 || kill_y != -1)
12930   {
12931     if (IS_PLAYER(kill_x, kill_y))
12932     {
12933       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12934
12935       if (player->shield_deadly_time_left > 0 &&
12936           !IS_INDESTRUCTIBLE(bad_element))
12937         Bang(bad_x, bad_y);
12938       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12939         KillPlayer(player);
12940     }
12941     else
12942       Bang(kill_x, kill_y);
12943   }
12944 }
12945
12946 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12947 {
12948   int bad_element = Feld[bad_x][bad_y];
12949   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12950   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12951   int test_x = bad_x + dx, test_y = bad_y + dy;
12952   int test_move_dir, test_element;
12953   int kill_x = -1, kill_y = -1;
12954
12955   if (!IN_LEV_FIELD(test_x, test_y))
12956     return;
12957
12958   test_move_dir =
12959     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12960
12961   test_element = Feld[test_x][test_y];
12962
12963   if (test_move_dir != bad_move_dir)
12964   {
12965     /* good thing can be player or penguin that does not move away */
12966     if (IS_PLAYER(test_x, test_y))
12967     {
12968       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12969
12970       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12971          player as being hit when he is moving towards the bad thing, because
12972          the "get hit by" condition would be lost after the player stops) */
12973       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12974         return;         /* player moves away from bad thing */
12975
12976       kill_x = test_x;
12977       kill_y = test_y;
12978     }
12979     else if (test_element == EL_PENGUIN)
12980     {
12981       kill_x = test_x;
12982       kill_y = test_y;
12983     }
12984   }
12985
12986   if (kill_x != -1 || kill_y != -1)
12987   {
12988     if (IS_PLAYER(kill_x, kill_y))
12989     {
12990       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12991
12992       if (player->shield_deadly_time_left > 0 &&
12993           !IS_INDESTRUCTIBLE(bad_element))
12994         Bang(bad_x, bad_y);
12995       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12996         KillPlayer(player);
12997     }
12998     else
12999       Bang(kill_x, kill_y);
13000   }
13001 }
13002
13003 void TestIfPlayerTouchesBadThing(int x, int y)
13004 {
13005   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13006 }
13007
13008 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13009 {
13010   TestIfGoodThingHitsBadThing(x, y, move_dir);
13011 }
13012
13013 void TestIfBadThingTouchesPlayer(int x, int y)
13014 {
13015   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13016 }
13017
13018 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13019 {
13020   TestIfBadThingHitsGoodThing(x, y, move_dir);
13021 }
13022
13023 void TestIfFriendTouchesBadThing(int x, int y)
13024 {
13025   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13026 }
13027
13028 void TestIfBadThingTouchesFriend(int x, int y)
13029 {
13030   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13031 }
13032
13033 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13034 {
13035   int i, kill_x = bad_x, kill_y = bad_y;
13036   static int xy[4][2] =
13037   {
13038     { 0, -1 },
13039     { -1, 0 },
13040     { +1, 0 },
13041     { 0, +1 }
13042   };
13043
13044   for (i = 0; i < NUM_DIRECTIONS; i++)
13045   {
13046     int x, y, element;
13047
13048     x = bad_x + xy[i][0];
13049     y = bad_y + xy[i][1];
13050     if (!IN_LEV_FIELD(x, y))
13051       continue;
13052
13053     element = Feld[x][y];
13054     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13055         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13056     {
13057       kill_x = x;
13058       kill_y = y;
13059       break;
13060     }
13061   }
13062
13063   if (kill_x != bad_x || kill_y != bad_y)
13064     Bang(bad_x, bad_y);
13065 }
13066
13067 void KillPlayer(struct PlayerInfo *player)
13068 {
13069   int jx = player->jx, jy = player->jy;
13070
13071   if (!player->active)
13072     return;
13073
13074 #if 0
13075   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13076          player->killed, player->active, player->reanimated);
13077 #endif
13078
13079   /* the following code was introduced to prevent an infinite loop when calling
13080      -> Bang()
13081      -> CheckTriggeredElementChangeExt()
13082      -> ExecuteCustomElementAction()
13083      -> KillPlayer()
13084      -> (infinitely repeating the above sequence of function calls)
13085      which occurs when killing the player while having a CE with the setting
13086      "kill player X when explosion of <player X>"; the solution using a new
13087      field "player->killed" was chosen for backwards compatibility, although
13088      clever use of the fields "player->active" etc. would probably also work */
13089 #if 1
13090   if (player->killed)
13091     return;
13092 #endif
13093
13094   player->killed = TRUE;
13095
13096   /* remove accessible field at the player's position */
13097   Feld[jx][jy] = EL_EMPTY;
13098
13099   /* deactivate shield (else Bang()/Explode() would not work right) */
13100   player->shield_normal_time_left = 0;
13101   player->shield_deadly_time_left = 0;
13102
13103 #if 0
13104   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13105          player->killed, player->active, player->reanimated);
13106 #endif
13107
13108   Bang(jx, jy);
13109
13110 #if 0
13111   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13112          player->killed, player->active, player->reanimated);
13113 #endif
13114
13115   if (player->reanimated)       /* killed player may have been reanimated */
13116     player->killed = player->reanimated = FALSE;
13117   else
13118     BuryPlayer(player);
13119 }
13120
13121 static void KillPlayerUnlessEnemyProtected(int x, int y)
13122 {
13123   if (!PLAYER_ENEMY_PROTECTED(x, y))
13124     KillPlayer(PLAYERINFO(x, y));
13125 }
13126
13127 static void KillPlayerUnlessExplosionProtected(int x, int y)
13128 {
13129   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13130     KillPlayer(PLAYERINFO(x, y));
13131 }
13132
13133 void BuryPlayer(struct PlayerInfo *player)
13134 {
13135   int jx = player->jx, jy = player->jy;
13136
13137   if (!player->active)
13138     return;
13139
13140   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13141   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13142
13143   player->GameOver = TRUE;
13144   RemovePlayer(player);
13145 }
13146
13147 void RemovePlayer(struct PlayerInfo *player)
13148 {
13149   int jx = player->jx, jy = player->jy;
13150   int i, found = FALSE;
13151
13152   player->present = FALSE;
13153   player->active = FALSE;
13154
13155   if (!ExplodeField[jx][jy])
13156     StorePlayer[jx][jy] = 0;
13157
13158   if (player->is_moving)
13159     TEST_DrawLevelField(player->last_jx, player->last_jy);
13160
13161   for (i = 0; i < MAX_PLAYERS; i++)
13162     if (stored_player[i].active)
13163       found = TRUE;
13164
13165   if (!found)
13166     AllPlayersGone = TRUE;
13167
13168   ExitX = ZX = jx;
13169   ExitY = ZY = jy;
13170 }
13171
13172 static void setFieldForSnapping(int x, int y, int element, int direction)
13173 {
13174   struct ElementInfo *ei = &element_info[element];
13175   int direction_bit = MV_DIR_TO_BIT(direction);
13176   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13177   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13178                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13179
13180   Feld[x][y] = EL_ELEMENT_SNAPPING;
13181   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13182
13183   ResetGfxAnimation(x, y);
13184
13185   GfxElement[x][y] = element;
13186   GfxAction[x][y] = action;
13187   GfxDir[x][y] = direction;
13188   GfxFrame[x][y] = -1;
13189 }
13190
13191 /*
13192   =============================================================================
13193   checkDiagonalPushing()
13194   -----------------------------------------------------------------------------
13195   check if diagonal input device direction results in pushing of object
13196   (by checking if the alternative direction is walkable, diggable, ...)
13197   =============================================================================
13198 */
13199
13200 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13201                                     int x, int y, int real_dx, int real_dy)
13202 {
13203   int jx, jy, dx, dy, xx, yy;
13204
13205   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13206     return TRUE;
13207
13208   /* diagonal direction: check alternative direction */
13209   jx = player->jx;
13210   jy = player->jy;
13211   dx = x - jx;
13212   dy = y - jy;
13213   xx = jx + (dx == 0 ? real_dx : 0);
13214   yy = jy + (dy == 0 ? real_dy : 0);
13215
13216   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13217 }
13218
13219 /*
13220   =============================================================================
13221   DigField()
13222   -----------------------------------------------------------------------------
13223   x, y:                 field next to player (non-diagonal) to try to dig to
13224   real_dx, real_dy:     direction as read from input device (can be diagonal)
13225   =============================================================================
13226 */
13227
13228 static int DigField(struct PlayerInfo *player,
13229                     int oldx, int oldy, int x, int y,
13230                     int real_dx, int real_dy, int mode)
13231 {
13232   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13233   boolean player_was_pushing = player->is_pushing;
13234   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13235   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13236   int jx = oldx, jy = oldy;
13237   int dx = x - jx, dy = y - jy;
13238   int nextx = x + dx, nexty = y + dy;
13239   int move_direction = (dx == -1 ? MV_LEFT  :
13240                         dx == +1 ? MV_RIGHT :
13241                         dy == -1 ? MV_UP    :
13242                         dy == +1 ? MV_DOWN  : MV_NONE);
13243   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13244   int dig_side = MV_DIR_OPPOSITE(move_direction);
13245   int old_element = Feld[jx][jy];
13246   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13247   int collect_count;
13248
13249   if (is_player)                /* function can also be called by EL_PENGUIN */
13250   {
13251     if (player->MovPos == 0)
13252     {
13253       player->is_digging = FALSE;
13254       player->is_collecting = FALSE;
13255     }
13256
13257     if (player->MovPos == 0)    /* last pushing move finished */
13258       player->is_pushing = FALSE;
13259
13260     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13261     {
13262       player->is_switching = FALSE;
13263       player->push_delay = -1;
13264
13265       return MP_NO_ACTION;
13266     }
13267   }
13268
13269   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13270     old_element = Back[jx][jy];
13271
13272   /* in case of element dropped at player position, check background */
13273   else if (Back[jx][jy] != EL_EMPTY &&
13274            game.engine_version >= VERSION_IDENT(2,2,0,0))
13275     old_element = Back[jx][jy];
13276
13277   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13278     return MP_NO_ACTION;        /* field has no opening in this direction */
13279
13280   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13281     return MP_NO_ACTION;        /* field has no opening in this direction */
13282
13283   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13284   {
13285     SplashAcid(x, y);
13286
13287     Feld[jx][jy] = player->artwork_element;
13288     InitMovingField(jx, jy, MV_DOWN);
13289     Store[jx][jy] = EL_ACID;
13290     ContinueMoving(jx, jy);
13291     BuryPlayer(player);
13292
13293     return MP_DONT_RUN_INTO;
13294   }
13295
13296   if (player_can_move && DONT_RUN_INTO(element))
13297   {
13298     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13299
13300     return MP_DONT_RUN_INTO;
13301   }
13302
13303   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13304     return MP_NO_ACTION;
13305
13306   collect_count = element_info[element].collect_count_initial;
13307
13308   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13309     return MP_NO_ACTION;
13310
13311   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13312     player_can_move = player_can_move_or_snap;
13313
13314   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13315       game.engine_version >= VERSION_IDENT(2,2,0,0))
13316   {
13317     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13318                                player->index_bit, dig_side);
13319     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13320                                         player->index_bit, dig_side);
13321
13322     if (element == EL_DC_LANDMINE)
13323       Bang(x, y);
13324
13325     if (Feld[x][y] != element)          /* field changed by snapping */
13326       return MP_ACTION;
13327
13328     return MP_NO_ACTION;
13329   }
13330
13331   if (player->gravity && is_player && !player->is_auto_moving &&
13332       canFallDown(player) && move_direction != MV_DOWN &&
13333       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13334     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13335
13336   if (player_can_move &&
13337       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13338   {
13339     int sound_element = SND_ELEMENT(element);
13340     int sound_action = ACTION_WALKING;
13341
13342     if (IS_RND_GATE(element))
13343     {
13344       if (!player->key[RND_GATE_NR(element)])
13345         return MP_NO_ACTION;
13346     }
13347     else if (IS_RND_GATE_GRAY(element))
13348     {
13349       if (!player->key[RND_GATE_GRAY_NR(element)])
13350         return MP_NO_ACTION;
13351     }
13352     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13353     {
13354       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13355         return MP_NO_ACTION;
13356     }
13357     else if (element == EL_EXIT_OPEN ||
13358              element == EL_EM_EXIT_OPEN ||
13359              element == EL_EM_EXIT_OPENING ||
13360              element == EL_STEEL_EXIT_OPEN ||
13361              element == EL_EM_STEEL_EXIT_OPEN ||
13362              element == EL_EM_STEEL_EXIT_OPENING ||
13363              element == EL_SP_EXIT_OPEN ||
13364              element == EL_SP_EXIT_OPENING)
13365     {
13366       sound_action = ACTION_PASSING;    /* player is passing exit */
13367     }
13368     else if (element == EL_EMPTY)
13369     {
13370       sound_action = ACTION_MOVING;             /* nothing to walk on */
13371     }
13372
13373     /* play sound from background or player, whatever is available */
13374     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13375       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13376     else
13377       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13378   }
13379   else if (player_can_move &&
13380            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13381   {
13382     if (!ACCESS_FROM(element, opposite_direction))
13383       return MP_NO_ACTION;      /* field not accessible from this direction */
13384
13385     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13386       return MP_NO_ACTION;
13387
13388     if (IS_EM_GATE(element))
13389     {
13390       if (!player->key[EM_GATE_NR(element)])
13391         return MP_NO_ACTION;
13392     }
13393     else if (IS_EM_GATE_GRAY(element))
13394     {
13395       if (!player->key[EM_GATE_GRAY_NR(element)])
13396         return MP_NO_ACTION;
13397     }
13398     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13399     {
13400       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13401         return MP_NO_ACTION;
13402     }
13403     else if (IS_EMC_GATE(element))
13404     {
13405       if (!player->key[EMC_GATE_NR(element)])
13406         return MP_NO_ACTION;
13407     }
13408     else if (IS_EMC_GATE_GRAY(element))
13409     {
13410       if (!player->key[EMC_GATE_GRAY_NR(element)])
13411         return MP_NO_ACTION;
13412     }
13413     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13414     {
13415       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13416         return MP_NO_ACTION;
13417     }
13418     else if (element == EL_DC_GATE_WHITE ||
13419              element == EL_DC_GATE_WHITE_GRAY ||
13420              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13421     {
13422       if (player->num_white_keys == 0)
13423         return MP_NO_ACTION;
13424
13425       player->num_white_keys--;
13426     }
13427     else if (IS_SP_PORT(element))
13428     {
13429       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13430           element == EL_SP_GRAVITY_PORT_RIGHT ||
13431           element == EL_SP_GRAVITY_PORT_UP ||
13432           element == EL_SP_GRAVITY_PORT_DOWN)
13433         player->gravity = !player->gravity;
13434       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13435                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13436                element == EL_SP_GRAVITY_ON_PORT_UP ||
13437                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13438         player->gravity = TRUE;
13439       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13440                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13441                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13442                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13443         player->gravity = FALSE;
13444     }
13445
13446     /* automatically move to the next field with double speed */
13447     player->programmed_action = move_direction;
13448
13449     if (player->move_delay_reset_counter == 0)
13450     {
13451       player->move_delay_reset_counter = 2;     /* two double speed steps */
13452
13453       DOUBLE_PLAYER_SPEED(player);
13454     }
13455
13456     PlayLevelSoundAction(x, y, ACTION_PASSING);
13457   }
13458   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13459   {
13460     RemoveField(x, y);
13461
13462     if (mode != DF_SNAP)
13463     {
13464       GfxElement[x][y] = GFX_ELEMENT(element);
13465       player->is_digging = TRUE;
13466     }
13467
13468     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13469
13470     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13471                                         player->index_bit, dig_side);
13472
13473     if (mode == DF_SNAP)
13474     {
13475       if (level.block_snap_field)
13476         setFieldForSnapping(x, y, element, move_direction);
13477       else
13478         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13479
13480       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13481                                           player->index_bit, dig_side);
13482     }
13483   }
13484   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13485   {
13486     RemoveField(x, y);
13487
13488     if (is_player && mode != DF_SNAP)
13489     {
13490       GfxElement[x][y] = element;
13491       player->is_collecting = TRUE;
13492     }
13493
13494     if (element == EL_SPEED_PILL)
13495     {
13496       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13497     }
13498     else if (element == EL_EXTRA_TIME && level.time > 0)
13499     {
13500       TimeLeft += level.extra_time;
13501
13502       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13503
13504       DisplayGameControlValues();
13505     }
13506     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13507     {
13508       player->shield_normal_time_left += level.shield_normal_time;
13509       if (element == EL_SHIELD_DEADLY)
13510         player->shield_deadly_time_left += level.shield_deadly_time;
13511     }
13512     else if (element == EL_DYNAMITE ||
13513              element == EL_EM_DYNAMITE ||
13514              element == EL_SP_DISK_RED)
13515     {
13516       if (player->inventory_size < MAX_INVENTORY_SIZE)
13517         player->inventory_element[player->inventory_size++] = element;
13518
13519       DrawGameDoorValues();
13520     }
13521     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13522     {
13523       player->dynabomb_count++;
13524       player->dynabombs_left++;
13525     }
13526     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13527     {
13528       player->dynabomb_size++;
13529     }
13530     else if (element == EL_DYNABOMB_INCREASE_POWER)
13531     {
13532       player->dynabomb_xl = TRUE;
13533     }
13534     else if (IS_KEY(element))
13535     {
13536       player->key[KEY_NR(element)] = TRUE;
13537
13538       DrawGameDoorValues();
13539     }
13540     else if (element == EL_DC_KEY_WHITE)
13541     {
13542       player->num_white_keys++;
13543
13544       /* display white keys? */
13545       /* DrawGameDoorValues(); */
13546     }
13547     else if (IS_ENVELOPE(element))
13548     {
13549       player->show_envelope = element;
13550     }
13551     else if (element == EL_EMC_LENSES)
13552     {
13553       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13554
13555       RedrawAllInvisibleElementsForLenses();
13556     }
13557     else if (element == EL_EMC_MAGNIFIER)
13558     {
13559       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13560
13561       RedrawAllInvisibleElementsForMagnifier();
13562     }
13563     else if (IS_DROPPABLE(element) ||
13564              IS_THROWABLE(element))     /* can be collected and dropped */
13565     {
13566       int i;
13567
13568       if (collect_count == 0)
13569         player->inventory_infinite_element = element;
13570       else
13571         for (i = 0; i < collect_count; i++)
13572           if (player->inventory_size < MAX_INVENTORY_SIZE)
13573             player->inventory_element[player->inventory_size++] = element;
13574
13575       DrawGameDoorValues();
13576     }
13577     else if (collect_count > 0)
13578     {
13579       local_player->gems_still_needed -= collect_count;
13580       if (local_player->gems_still_needed < 0)
13581         local_player->gems_still_needed = 0;
13582
13583       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13584
13585       DisplayGameControlValues();
13586     }
13587
13588     RaiseScoreElement(element);
13589     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13590
13591     if (is_player)
13592       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13593                                           player->index_bit, dig_side);
13594
13595     if (mode == DF_SNAP)
13596     {
13597       if (level.block_snap_field)
13598         setFieldForSnapping(x, y, element, move_direction);
13599       else
13600         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13601
13602       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13603                                           player->index_bit, dig_side);
13604     }
13605   }
13606   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13607   {
13608     if (mode == DF_SNAP && element != EL_BD_ROCK)
13609       return MP_NO_ACTION;
13610
13611     if (CAN_FALL(element) && dy)
13612       return MP_NO_ACTION;
13613
13614     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13615         !(element == EL_SPRING && level.use_spring_bug))
13616       return MP_NO_ACTION;
13617
13618     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13619         ((move_direction & MV_VERTICAL &&
13620           ((element_info[element].move_pattern & MV_LEFT &&
13621             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13622            (element_info[element].move_pattern & MV_RIGHT &&
13623             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13624          (move_direction & MV_HORIZONTAL &&
13625           ((element_info[element].move_pattern & MV_UP &&
13626             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13627            (element_info[element].move_pattern & MV_DOWN &&
13628             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13629       return MP_NO_ACTION;
13630
13631     /* do not push elements already moving away faster than player */
13632     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13633         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13634       return MP_NO_ACTION;
13635
13636     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13637     {
13638       if (player->push_delay_value == -1 || !player_was_pushing)
13639         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13640     }
13641     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13642     {
13643       if (player->push_delay_value == -1)
13644         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13645     }
13646     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13647     {
13648       if (!player->is_pushing)
13649         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13650     }
13651
13652     player->is_pushing = TRUE;
13653     player->is_active = TRUE;
13654
13655     if (!(IN_LEV_FIELD(nextx, nexty) &&
13656           (IS_FREE(nextx, nexty) ||
13657            (IS_SB_ELEMENT(element) &&
13658             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13659            (IS_CUSTOM_ELEMENT(element) &&
13660             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13661       return MP_NO_ACTION;
13662
13663     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13664       return MP_NO_ACTION;
13665
13666     if (player->push_delay == -1)       /* new pushing; restart delay */
13667       player->push_delay = 0;
13668
13669     if (player->push_delay < player->push_delay_value &&
13670         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13671         element != EL_SPRING && element != EL_BALLOON)
13672     {
13673       /* make sure that there is no move delay before next try to push */
13674       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13675         player->move_delay = 0;
13676
13677       return MP_NO_ACTION;
13678     }
13679
13680     if (IS_CUSTOM_ELEMENT(element) &&
13681         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13682     {
13683       if (!DigFieldByCE(nextx, nexty, element))
13684         return MP_NO_ACTION;
13685     }
13686
13687     if (IS_SB_ELEMENT(element))
13688     {
13689       if (element == EL_SOKOBAN_FIELD_FULL)
13690       {
13691         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13692         local_player->sokobanfields_still_needed++;
13693       }
13694
13695       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13696       {
13697         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13698         local_player->sokobanfields_still_needed--;
13699       }
13700
13701       Feld[x][y] = EL_SOKOBAN_OBJECT;
13702
13703       if (Back[x][y] == Back[nextx][nexty])
13704         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13705       else if (Back[x][y] != 0)
13706         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13707                                     ACTION_EMPTYING);
13708       else
13709         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13710                                     ACTION_FILLING);
13711
13712       if (local_player->sokobanfields_still_needed == 0 &&
13713           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13714       {
13715         PlayerWins(player);
13716
13717         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13718       }
13719     }
13720     else
13721       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13722
13723     InitMovingField(x, y, move_direction);
13724     GfxAction[x][y] = ACTION_PUSHING;
13725
13726     if (mode == DF_SNAP)
13727       ContinueMoving(x, y);
13728     else
13729       MovPos[x][y] = (dx != 0 ? dx : dy);
13730
13731     Pushed[x][y] = TRUE;
13732     Pushed[nextx][nexty] = TRUE;
13733
13734     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13735       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13736     else
13737       player->push_delay_value = -1;    /* get new value later */
13738
13739     /* check for element change _after_ element has been pushed */
13740     if (game.use_change_when_pushing_bug)
13741     {
13742       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13743                                  player->index_bit, dig_side);
13744       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13745                                           player->index_bit, dig_side);
13746     }
13747   }
13748   else if (IS_SWITCHABLE(element))
13749   {
13750     if (PLAYER_SWITCHING(player, x, y))
13751     {
13752       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13753                                           player->index_bit, dig_side);
13754
13755       return MP_ACTION;
13756     }
13757
13758     player->is_switching = TRUE;
13759     player->switch_x = x;
13760     player->switch_y = y;
13761
13762     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13763
13764     if (element == EL_ROBOT_WHEEL)
13765     {
13766       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13767       ZX = x;
13768       ZY = y;
13769
13770       game.robot_wheel_active = TRUE;
13771
13772       TEST_DrawLevelField(x, y);
13773     }
13774     else if (element == EL_SP_TERMINAL)
13775     {
13776       int xx, yy;
13777
13778       SCAN_PLAYFIELD(xx, yy)
13779       {
13780         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13781           Bang(xx, yy);
13782         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13783           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13784       }
13785     }
13786     else if (IS_BELT_SWITCH(element))
13787     {
13788       ToggleBeltSwitch(x, y);
13789     }
13790     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13791              element == EL_SWITCHGATE_SWITCH_DOWN ||
13792              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13793              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13794     {
13795       ToggleSwitchgateSwitch(x, y);
13796     }
13797     else if (element == EL_LIGHT_SWITCH ||
13798              element == EL_LIGHT_SWITCH_ACTIVE)
13799     {
13800       ToggleLightSwitch(x, y);
13801     }
13802     else if (element == EL_TIMEGATE_SWITCH ||
13803              element == EL_DC_TIMEGATE_SWITCH)
13804     {
13805       ActivateTimegateSwitch(x, y);
13806     }
13807     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13808              element == EL_BALLOON_SWITCH_RIGHT ||
13809              element == EL_BALLOON_SWITCH_UP    ||
13810              element == EL_BALLOON_SWITCH_DOWN  ||
13811              element == EL_BALLOON_SWITCH_NONE  ||
13812              element == EL_BALLOON_SWITCH_ANY)
13813     {
13814       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13815                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13816                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13817                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13818                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13819                              move_direction);
13820     }
13821     else if (element == EL_LAMP)
13822     {
13823       Feld[x][y] = EL_LAMP_ACTIVE;
13824       local_player->lights_still_needed--;
13825
13826       ResetGfxAnimation(x, y);
13827       TEST_DrawLevelField(x, y);
13828     }
13829     else if (element == EL_TIME_ORB_FULL)
13830     {
13831       Feld[x][y] = EL_TIME_ORB_EMPTY;
13832
13833       if (level.time > 0 || level.use_time_orb_bug)
13834       {
13835         TimeLeft += level.time_orb_time;
13836         game.no_time_limit = FALSE;
13837
13838         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13839
13840         DisplayGameControlValues();
13841       }
13842
13843       ResetGfxAnimation(x, y);
13844       TEST_DrawLevelField(x, y);
13845     }
13846     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13847              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13848     {
13849       int xx, yy;
13850
13851       game.ball_state = !game.ball_state;
13852
13853       SCAN_PLAYFIELD(xx, yy)
13854       {
13855         int e = Feld[xx][yy];
13856
13857         if (game.ball_state)
13858         {
13859           if (e == EL_EMC_MAGIC_BALL)
13860             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13861           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13862             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13863         }
13864         else
13865         {
13866           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13867             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13868           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13869             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13870         }
13871       }
13872     }
13873
13874     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13875                                         player->index_bit, dig_side);
13876
13877     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13878                                         player->index_bit, dig_side);
13879
13880     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13881                                         player->index_bit, dig_side);
13882
13883     return MP_ACTION;
13884   }
13885   else
13886   {
13887     if (!PLAYER_SWITCHING(player, x, y))
13888     {
13889       player->is_switching = TRUE;
13890       player->switch_x = x;
13891       player->switch_y = y;
13892
13893       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13894                                  player->index_bit, dig_side);
13895       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13896                                           player->index_bit, dig_side);
13897
13898       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13899                                  player->index_bit, dig_side);
13900       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13901                                           player->index_bit, dig_side);
13902     }
13903
13904     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13905                                player->index_bit, dig_side);
13906     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13907                                         player->index_bit, dig_side);
13908
13909     return MP_NO_ACTION;
13910   }
13911
13912   player->push_delay = -1;
13913
13914   if (is_player)                /* function can also be called by EL_PENGUIN */
13915   {
13916     if (Feld[x][y] != element)          /* really digged/collected something */
13917     {
13918       player->is_collecting = !player->is_digging;
13919       player->is_active = TRUE;
13920     }
13921   }
13922
13923   return MP_MOVING;
13924 }
13925
13926 static boolean DigFieldByCE(int x, int y, int digging_element)
13927 {
13928   int element = Feld[x][y];
13929
13930   if (!IS_FREE(x, y))
13931   {
13932     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13933                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13934                   ACTION_BREAKING);
13935
13936     /* no element can dig solid indestructible elements */
13937     if (IS_INDESTRUCTIBLE(element) &&
13938         !IS_DIGGABLE(element) &&
13939         !IS_COLLECTIBLE(element))
13940       return FALSE;
13941
13942     if (AmoebaNr[x][y] &&
13943         (element == EL_AMOEBA_FULL ||
13944          element == EL_BD_AMOEBA ||
13945          element == EL_AMOEBA_GROWING))
13946     {
13947       AmoebaCnt[AmoebaNr[x][y]]--;
13948       AmoebaCnt2[AmoebaNr[x][y]]--;
13949     }
13950
13951     if (IS_MOVING(x, y))
13952       RemoveMovingField(x, y);
13953     else
13954     {
13955       RemoveField(x, y);
13956       TEST_DrawLevelField(x, y);
13957     }
13958
13959     /* if digged element was about to explode, prevent the explosion */
13960     ExplodeField[x][y] = EX_TYPE_NONE;
13961
13962     PlayLevelSoundAction(x, y, action);
13963   }
13964
13965   Store[x][y] = EL_EMPTY;
13966
13967   /* this makes it possible to leave the removed element again */
13968   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13969     Store[x][y] = element;
13970
13971   return TRUE;
13972 }
13973
13974 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13975 {
13976   int jx = player->jx, jy = player->jy;
13977   int x = jx + dx, y = jy + dy;
13978   int snap_direction = (dx == -1 ? MV_LEFT  :
13979                         dx == +1 ? MV_RIGHT :
13980                         dy == -1 ? MV_UP    :
13981                         dy == +1 ? MV_DOWN  : MV_NONE);
13982   boolean can_continue_snapping = (level.continuous_snapping &&
13983                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13984
13985   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13986     return FALSE;
13987
13988   if (!player->active || !IN_LEV_FIELD(x, y))
13989     return FALSE;
13990
13991   if (dx && dy)
13992     return FALSE;
13993
13994   if (!dx && !dy)
13995   {
13996     if (player->MovPos == 0)
13997       player->is_pushing = FALSE;
13998
13999     player->is_snapping = FALSE;
14000
14001     if (player->MovPos == 0)
14002     {
14003       player->is_moving = FALSE;
14004       player->is_digging = FALSE;
14005       player->is_collecting = FALSE;
14006     }
14007
14008     return FALSE;
14009   }
14010
14011   /* prevent snapping with already pressed snap key when not allowed */
14012   if (player->is_snapping && !can_continue_snapping)
14013     return FALSE;
14014
14015   player->MovDir = snap_direction;
14016
14017   if (player->MovPos == 0)
14018   {
14019     player->is_moving = FALSE;
14020     player->is_digging = FALSE;
14021     player->is_collecting = FALSE;
14022   }
14023
14024   player->is_dropping = FALSE;
14025   player->is_dropping_pressed = FALSE;
14026   player->drop_pressed_delay = 0;
14027
14028   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14029     return FALSE;
14030
14031   player->is_snapping = TRUE;
14032   player->is_active = TRUE;
14033
14034   if (player->MovPos == 0)
14035   {
14036     player->is_moving = FALSE;
14037     player->is_digging = FALSE;
14038     player->is_collecting = FALSE;
14039   }
14040
14041   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14042     TEST_DrawLevelField(player->last_jx, player->last_jy);
14043
14044   TEST_DrawLevelField(x, y);
14045
14046   return TRUE;
14047 }
14048
14049 static boolean DropElement(struct PlayerInfo *player)
14050 {
14051   int old_element, new_element;
14052   int dropx = player->jx, dropy = player->jy;
14053   int drop_direction = player->MovDir;
14054   int drop_side = drop_direction;
14055   int drop_element = get_next_dropped_element(player);
14056
14057   player->is_dropping_pressed = TRUE;
14058
14059   /* do not drop an element on top of another element; when holding drop key
14060      pressed without moving, dropped element must move away before the next
14061      element can be dropped (this is especially important if the next element
14062      is dynamite, which can be placed on background for historical reasons) */
14063   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14064     return MP_ACTION;
14065
14066   if (IS_THROWABLE(drop_element))
14067   {
14068     dropx += GET_DX_FROM_DIR(drop_direction);
14069     dropy += GET_DY_FROM_DIR(drop_direction);
14070
14071     if (!IN_LEV_FIELD(dropx, dropy))
14072       return FALSE;
14073   }
14074
14075   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14076   new_element = drop_element;           /* default: no change when dropping */
14077
14078   /* check if player is active, not moving and ready to drop */
14079   if (!player->active || player->MovPos || player->drop_delay > 0)
14080     return FALSE;
14081
14082   /* check if player has anything that can be dropped */
14083   if (new_element == EL_UNDEFINED)
14084     return FALSE;
14085
14086   /* check if drop key was pressed long enough for EM style dynamite */
14087   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14088     return FALSE;
14089
14090   /* check if anything can be dropped at the current position */
14091   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14092     return FALSE;
14093
14094   /* collected custom elements can only be dropped on empty fields */
14095   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14096     return FALSE;
14097
14098   if (old_element != EL_EMPTY)
14099     Back[dropx][dropy] = old_element;   /* store old element on this field */
14100
14101   ResetGfxAnimation(dropx, dropy);
14102   ResetRandomAnimationValue(dropx, dropy);
14103
14104   if (player->inventory_size > 0 ||
14105       player->inventory_infinite_element != EL_UNDEFINED)
14106   {
14107     if (player->inventory_size > 0)
14108     {
14109       player->inventory_size--;
14110
14111       DrawGameDoorValues();
14112
14113       if (new_element == EL_DYNAMITE)
14114         new_element = EL_DYNAMITE_ACTIVE;
14115       else if (new_element == EL_EM_DYNAMITE)
14116         new_element = EL_EM_DYNAMITE_ACTIVE;
14117       else if (new_element == EL_SP_DISK_RED)
14118         new_element = EL_SP_DISK_RED_ACTIVE;
14119     }
14120
14121     Feld[dropx][dropy] = new_element;
14122
14123     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14124       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14125                           el2img(Feld[dropx][dropy]), 0);
14126
14127     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14128
14129     /* needed if previous element just changed to "empty" in the last frame */
14130     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14131
14132     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14133                                player->index_bit, drop_side);
14134     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14135                                         CE_PLAYER_DROPS_X,
14136                                         player->index_bit, drop_side);
14137
14138     TestIfElementTouchesCustomElement(dropx, dropy);
14139   }
14140   else          /* player is dropping a dyna bomb */
14141   {
14142     player->dynabombs_left--;
14143
14144     Feld[dropx][dropy] = new_element;
14145
14146     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14147       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14148                           el2img(Feld[dropx][dropy]), 0);
14149
14150     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14151   }
14152
14153   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14154     InitField_WithBug1(dropx, dropy, FALSE);
14155
14156   new_element = Feld[dropx][dropy];     /* element might have changed */
14157
14158   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14159       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14160   {
14161     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14162       MovDir[dropx][dropy] = drop_direction;
14163
14164     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14165
14166     /* do not cause impact style collision by dropping elements that can fall */
14167     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14168   }
14169
14170   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14171   player->is_dropping = TRUE;
14172
14173   player->drop_pressed_delay = 0;
14174   player->is_dropping_pressed = FALSE;
14175
14176   player->drop_x = dropx;
14177   player->drop_y = dropy;
14178
14179   return TRUE;
14180 }
14181
14182 /* ------------------------------------------------------------------------- */
14183 /* game sound playing functions                                              */
14184 /* ------------------------------------------------------------------------- */
14185
14186 static int *loop_sound_frame = NULL;
14187 static int *loop_sound_volume = NULL;
14188
14189 void InitPlayLevelSound()
14190 {
14191   int num_sounds = getSoundListSize();
14192
14193   checked_free(loop_sound_frame);
14194   checked_free(loop_sound_volume);
14195
14196   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14197   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14198 }
14199
14200 static void PlayLevelSound(int x, int y, int nr)
14201 {
14202   int sx = SCREENX(x), sy = SCREENY(y);
14203   int volume, stereo_position;
14204   int max_distance = 8;
14205   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14206
14207   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14208       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14209     return;
14210
14211   if (!IN_LEV_FIELD(x, y) ||
14212       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14213       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14214     return;
14215
14216   volume = SOUND_MAX_VOLUME;
14217
14218   if (!IN_SCR_FIELD(sx, sy))
14219   {
14220     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14221     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14222
14223     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14224   }
14225
14226   stereo_position = (SOUND_MAX_LEFT +
14227                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14228                      (SCR_FIELDX + 2 * max_distance));
14229
14230   if (IS_LOOP_SOUND(nr))
14231   {
14232     /* This assures that quieter loop sounds do not overwrite louder ones,
14233        while restarting sound volume comparison with each new game frame. */
14234
14235     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14236       return;
14237
14238     loop_sound_volume[nr] = volume;
14239     loop_sound_frame[nr] = FrameCounter;
14240   }
14241
14242   PlaySoundExt(nr, volume, stereo_position, type);
14243 }
14244
14245 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14246 {
14247   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14248                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14249                  y < LEVELY(BY1) ? LEVELY(BY1) :
14250                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14251                  sound_action);
14252 }
14253
14254 static void PlayLevelSoundAction(int x, int y, int action)
14255 {
14256   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14257 }
14258
14259 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14260 {
14261   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14262
14263   if (sound_effect != SND_UNDEFINED)
14264     PlayLevelSound(x, y, sound_effect);
14265 }
14266
14267 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14268                                               int action)
14269 {
14270   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14271
14272   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14273     PlayLevelSound(x, y, sound_effect);
14274 }
14275
14276 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14277 {
14278   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14279
14280   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14281     PlayLevelSound(x, y, sound_effect);
14282 }
14283
14284 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14285 {
14286   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14287
14288   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14289     StopSound(sound_effect);
14290 }
14291
14292 static void PlayLevelMusic()
14293 {
14294   if (levelset.music[level_nr] != MUS_UNDEFINED)
14295     PlayMusic(levelset.music[level_nr]);        /* from config file */
14296   else
14297     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14298 }
14299
14300 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14301 {
14302   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14303   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14304   int x = xx - 1 - offset;
14305   int y = yy - 1 - offset;
14306
14307   switch (sample)
14308   {
14309     case SAMPLE_blank:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14311       break;
14312
14313     case SAMPLE_roll:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14315       break;
14316
14317     case SAMPLE_stone:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14319       break;
14320
14321     case SAMPLE_nut:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14323       break;
14324
14325     case SAMPLE_crack:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14327       break;
14328
14329     case SAMPLE_bug:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14331       break;
14332
14333     case SAMPLE_tank:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14335       break;
14336
14337     case SAMPLE_android_clone:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14339       break;
14340
14341     case SAMPLE_android_move:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14343       break;
14344
14345     case SAMPLE_spring:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14347       break;
14348
14349     case SAMPLE_slurp:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14351       break;
14352
14353     case SAMPLE_eater:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14355       break;
14356
14357     case SAMPLE_eater_eat:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14359       break;
14360
14361     case SAMPLE_alien:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14363       break;
14364
14365     case SAMPLE_collect:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14367       break;
14368
14369     case SAMPLE_diamond:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14371       break;
14372
14373     case SAMPLE_squash:
14374       /* !!! CHECK THIS !!! */
14375 #if 1
14376       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14377 #else
14378       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14379 #endif
14380       break;
14381
14382     case SAMPLE_wonderfall:
14383       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14384       break;
14385
14386     case SAMPLE_drip:
14387       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14388       break;
14389
14390     case SAMPLE_push:
14391       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14392       break;
14393
14394     case SAMPLE_dirt:
14395       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14396       break;
14397
14398     case SAMPLE_acid:
14399       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14400       break;
14401
14402     case SAMPLE_ball:
14403       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14404       break;
14405
14406     case SAMPLE_grow:
14407       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14408       break;
14409
14410     case SAMPLE_wonder:
14411       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14412       break;
14413
14414     case SAMPLE_door:
14415       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14416       break;
14417
14418     case SAMPLE_exit_open:
14419       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14420       break;
14421
14422     case SAMPLE_exit_leave:
14423       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14424       break;
14425
14426     case SAMPLE_dynamite:
14427       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14428       break;
14429
14430     case SAMPLE_tick:
14431       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14432       break;
14433
14434     case SAMPLE_press:
14435       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14436       break;
14437
14438     case SAMPLE_wheel:
14439       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14440       break;
14441
14442     case SAMPLE_boom:
14443       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14444       break;
14445
14446     case SAMPLE_die:
14447       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14448       break;
14449
14450     case SAMPLE_time:
14451       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14452       break;
14453
14454     default:
14455       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14456       break;
14457   }
14458 }
14459
14460 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14461 {
14462   int element = map_element_SP_to_RND(element_sp);
14463   int action = map_action_SP_to_RND(action_sp);
14464   int offset = (setup.sp_show_border_elements ? 0 : 1);
14465   int x = xx - offset;
14466   int y = yy - offset;
14467
14468   PlayLevelSoundElementAction(x, y, element, action);
14469 }
14470
14471 void RaiseScore(int value)
14472 {
14473   local_player->score += value;
14474
14475   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14476
14477   DisplayGameControlValues();
14478 }
14479
14480 void RaiseScoreElement(int element)
14481 {
14482   switch (element)
14483   {
14484     case EL_EMERALD:
14485     case EL_BD_DIAMOND:
14486     case EL_EMERALD_YELLOW:
14487     case EL_EMERALD_RED:
14488     case EL_EMERALD_PURPLE:
14489     case EL_SP_INFOTRON:
14490       RaiseScore(level.score[SC_EMERALD]);
14491       break;
14492     case EL_DIAMOND:
14493       RaiseScore(level.score[SC_DIAMOND]);
14494       break;
14495     case EL_CRYSTAL:
14496       RaiseScore(level.score[SC_CRYSTAL]);
14497       break;
14498     case EL_PEARL:
14499       RaiseScore(level.score[SC_PEARL]);
14500       break;
14501     case EL_BUG:
14502     case EL_BD_BUTTERFLY:
14503     case EL_SP_ELECTRON:
14504       RaiseScore(level.score[SC_BUG]);
14505       break;
14506     case EL_SPACESHIP:
14507     case EL_BD_FIREFLY:
14508     case EL_SP_SNIKSNAK:
14509       RaiseScore(level.score[SC_SPACESHIP]);
14510       break;
14511     case EL_YAMYAM:
14512     case EL_DARK_YAMYAM:
14513       RaiseScore(level.score[SC_YAMYAM]);
14514       break;
14515     case EL_ROBOT:
14516       RaiseScore(level.score[SC_ROBOT]);
14517       break;
14518     case EL_PACMAN:
14519       RaiseScore(level.score[SC_PACMAN]);
14520       break;
14521     case EL_NUT:
14522       RaiseScore(level.score[SC_NUT]);
14523       break;
14524     case EL_DYNAMITE:
14525     case EL_EM_DYNAMITE:
14526     case EL_SP_DISK_RED:
14527     case EL_DYNABOMB_INCREASE_NUMBER:
14528     case EL_DYNABOMB_INCREASE_SIZE:
14529     case EL_DYNABOMB_INCREASE_POWER:
14530       RaiseScore(level.score[SC_DYNAMITE]);
14531       break;
14532     case EL_SHIELD_NORMAL:
14533     case EL_SHIELD_DEADLY:
14534       RaiseScore(level.score[SC_SHIELD]);
14535       break;
14536     case EL_EXTRA_TIME:
14537       RaiseScore(level.extra_time_score);
14538       break;
14539     case EL_KEY_1:
14540     case EL_KEY_2:
14541     case EL_KEY_3:
14542     case EL_KEY_4:
14543     case EL_EM_KEY_1:
14544     case EL_EM_KEY_2:
14545     case EL_EM_KEY_3:
14546     case EL_EM_KEY_4:
14547     case EL_EMC_KEY_5:
14548     case EL_EMC_KEY_6:
14549     case EL_EMC_KEY_7:
14550     case EL_EMC_KEY_8:
14551     case EL_DC_KEY_WHITE:
14552       RaiseScore(level.score[SC_KEY]);
14553       break;
14554     default:
14555       RaiseScore(element_info[element].collect_score);
14556       break;
14557   }
14558 }
14559
14560 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14561 {
14562   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14563   {
14564     /* closing door required in case of envelope style request dialogs */
14565     if (!skip_request)
14566       CloseDoor(DOOR_CLOSE_1);
14567
14568 #if defined(NETWORK_AVALIABLE)
14569     if (options.network)
14570       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14571     else
14572 #endif
14573     {
14574       if (quick_quit)
14575       {
14576         FadeSkipNextFadeIn();
14577
14578         game_status = GAME_MODE_MAIN;
14579
14580         DrawMainMenu();
14581       }
14582       else
14583       {
14584         game_status = GAME_MODE_MAIN;
14585
14586         DrawMainMenu();
14587       }
14588     }
14589   }
14590   else          /* continue playing the game */
14591   {
14592     if (tape.playing && tape.deactivate_display)
14593       TapeDeactivateDisplayOff(TRUE);
14594
14595     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14596
14597     if (tape.playing && tape.deactivate_display)
14598       TapeDeactivateDisplayOn();
14599   }
14600 }
14601
14602 void RequestQuitGame(boolean ask_if_really_quit)
14603 {
14604   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14605   boolean skip_request = AllPlayersGone || quick_quit;
14606
14607   RequestQuitGameExt(skip_request, quick_quit,
14608                      "Do you really want to quit the game?");
14609 }
14610
14611
14612 /* ------------------------------------------------------------------------- */
14613 /* random generator functions                                                */
14614 /* ------------------------------------------------------------------------- */
14615
14616 unsigned int InitEngineRandom_RND(int seed)
14617 {
14618   game.num_random_calls = 0;
14619
14620   return InitEngineRandom(seed);
14621 }
14622
14623 unsigned int RND(int max)
14624 {
14625   if (max > 0)
14626   {
14627     game.num_random_calls++;
14628
14629     return GetEngineRandom(max);
14630   }
14631
14632   return 0;
14633 }
14634
14635
14636 /* ------------------------------------------------------------------------- */
14637 /* game engine snapshot handling functions                                   */
14638 /* ------------------------------------------------------------------------- */
14639
14640 struct EngineSnapshotInfo
14641 {
14642   /* runtime values for custom element collect score */
14643   int collect_score[NUM_CUSTOM_ELEMENTS];
14644
14645   /* runtime values for group element choice position */
14646   int choice_pos[NUM_GROUP_ELEMENTS];
14647
14648   /* runtime values for belt position animations */
14649   int belt_graphic[4][NUM_BELT_PARTS];
14650   int belt_anim_mode[4][NUM_BELT_PARTS];
14651 };
14652
14653 static struct EngineSnapshotInfo engine_snapshot_rnd;
14654 static char *snapshot_level_identifier = NULL;
14655 static int snapshot_level_nr = -1;
14656
14657 static void SaveEngineSnapshotValues_RND()
14658 {
14659   static int belt_base_active_element[4] =
14660   {
14661     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14662     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14663     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14664     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14665   };
14666   int i, j;
14667
14668   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14669   {
14670     int element = EL_CUSTOM_START + i;
14671
14672     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14673   }
14674
14675   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14676   {
14677     int element = EL_GROUP_START + i;
14678
14679     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14680   }
14681
14682   for (i = 0; i < 4; i++)
14683   {
14684     for (j = 0; j < NUM_BELT_PARTS; j++)
14685     {
14686       int element = belt_base_active_element[i] + j;
14687       int graphic = el2img(element);
14688       int anim_mode = graphic_info[graphic].anim_mode;
14689
14690       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14691       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14692     }
14693   }
14694 }
14695
14696 static void LoadEngineSnapshotValues_RND()
14697 {
14698   unsigned int num_random_calls = game.num_random_calls;
14699   int i, j;
14700
14701   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14702   {
14703     int element = EL_CUSTOM_START + i;
14704
14705     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14706   }
14707
14708   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14709   {
14710     int element = EL_GROUP_START + i;
14711
14712     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14713   }
14714
14715   for (i = 0; i < 4; i++)
14716   {
14717     for (j = 0; j < NUM_BELT_PARTS; j++)
14718     {
14719       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14720       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14721
14722       graphic_info[graphic].anim_mode = anim_mode;
14723     }
14724   }
14725
14726   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14727   {
14728     InitRND(tape.random_seed);
14729     for (i = 0; i < num_random_calls; i++)
14730       RND(1);
14731   }
14732
14733   if (game.num_random_calls != num_random_calls)
14734   {
14735     Error(ERR_INFO, "number of random calls out of sync");
14736     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14737     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14738     Error(ERR_EXIT, "this should not happen -- please debug");
14739   }
14740 }
14741
14742 void FreeEngineSnapshotSingle()
14743 {
14744   FreeSnapshotSingle();
14745
14746   setString(&snapshot_level_identifier, NULL);
14747   snapshot_level_nr = -1;
14748 }
14749
14750 void FreeEngineSnapshotList()
14751 {
14752   FreeSnapshotList();
14753 }
14754
14755 ListNode *SaveEngineSnapshotBuffers()
14756 {
14757   ListNode *buffers = NULL;
14758
14759   /* copy some special values to a structure better suited for the snapshot */
14760
14761   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14762     SaveEngineSnapshotValues_RND();
14763   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14764     SaveEngineSnapshotValues_EM();
14765   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14766     SaveEngineSnapshotValues_SP(&buffers);
14767
14768   /* save values stored in special snapshot structure */
14769
14770   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14771     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14772   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14773     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14774   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14775     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14776
14777   /* save further RND engine values */
14778
14779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14781   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14782
14783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14787
14788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14790   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14791   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14792   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14793
14794   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14795   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14796   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14797
14798   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14799
14800   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14801
14802   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14803   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14804
14805   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14806   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14807   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14808   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14809   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14810   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14811   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14812   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14813   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14814   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14815   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14816   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14817   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14818   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14819   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14820   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14821   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14822   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14823
14824   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14825   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14826
14827   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14828   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14829   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14830
14831   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14832   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14833
14834   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14835   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14836   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14837   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14838   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14839
14840   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14841   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14842
14843 #if 0
14844   ListNode *node = engine_snapshot_list_rnd;
14845   int num_bytes = 0;
14846
14847   while (node != NULL)
14848   {
14849     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14850
14851     node = node->next;
14852   }
14853
14854   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14855 #endif
14856
14857   return buffers;
14858 }
14859
14860 void SaveEngineSnapshotSingle()
14861 {
14862   ListNode *buffers = SaveEngineSnapshotBuffers();
14863
14864   /* finally save all snapshot buffers to single snapshot */
14865   SaveSnapshotSingle(buffers);
14866
14867   /* save level identification information */
14868   setString(&snapshot_level_identifier, leveldir_current->identifier);
14869   snapshot_level_nr = level_nr;
14870 }
14871
14872 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14873 {
14874   boolean save_snapshot =
14875     (initial_snapshot ||
14876      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14877      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14878       game.snapshot.changed_action));
14879
14880   game.snapshot.changed_action = FALSE;
14881
14882   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14883       tape.quick_resume ||
14884       !save_snapshot)
14885     return FALSE;
14886
14887   ListNode *buffers = SaveEngineSnapshotBuffers();
14888
14889   /* finally save all snapshot buffers to snapshot list */
14890   SaveSnapshotToList(buffers);
14891
14892   return TRUE;
14893 }
14894
14895 boolean SaveEngineSnapshotToList()
14896 {
14897   return SaveEngineSnapshotToListExt(FALSE);
14898 }
14899
14900 void SaveEngineSnapshotToListInitial()
14901 {
14902   FreeEngineSnapshotList();
14903
14904   SaveEngineSnapshotToListExt(TRUE);
14905 }
14906
14907 void LoadEngineSnapshotValues()
14908 {
14909   /* restore special values from snapshot structure */
14910
14911   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14912     LoadEngineSnapshotValues_RND();
14913   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14914     LoadEngineSnapshotValues_EM();
14915   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14916     LoadEngineSnapshotValues_SP();
14917 }
14918
14919 void LoadEngineSnapshotSingle()
14920 {
14921   LoadSnapshotSingle();
14922
14923   LoadEngineSnapshotValues();
14924 }
14925
14926 void LoadEngineSnapshot_Undo(int steps)
14927 {
14928   LoadSnapshotFromList_Older(steps);
14929
14930   LoadEngineSnapshotValues();
14931 }
14932
14933 void LoadEngineSnapshot_Redo(int steps)
14934 {
14935   LoadSnapshotFromList_Newer(steps);
14936
14937   LoadEngineSnapshotValues();
14938 }
14939
14940 boolean CheckEngineSnapshotSingle()
14941 {
14942   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14943           snapshot_level_nr == level_nr);
14944 }
14945
14946 boolean CheckEngineSnapshotList()
14947 {
14948   return CheckSnapshotList();
14949 }
14950
14951
14952 /* ---------- new game button stuff ---------------------------------------- */
14953
14954 static struct
14955 {
14956   int graphic;
14957   struct XY *pos;
14958   int gadget_id;
14959   char *infotext;
14960 } gamebutton_info[NUM_GAME_BUTTONS] =
14961 {
14962   {
14963     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14964     GAME_CTRL_ID_STOP,                  "stop game"
14965   },
14966   {
14967     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14968     GAME_CTRL_ID_PAUSE,                 "pause game"
14969   },
14970   {
14971     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14972     GAME_CTRL_ID_PLAY,                  "play game"
14973   },
14974   {
14975     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14976     GAME_CTRL_ID_UNDO,                  "undo step"
14977   },
14978   {
14979     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14980     GAME_CTRL_ID_REDO,                  "redo step"
14981   },
14982   {
14983     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14984     GAME_CTRL_ID_SAVE,                  "save game"
14985   },
14986   {
14987     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14988     GAME_CTRL_ID_PAUSE2,                "pause game"
14989   },
14990   {
14991     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14992     GAME_CTRL_ID_LOAD,                  "load game"
14993   },
14994   {
14995     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14996     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14997   },
14998   {
14999     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
15000     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
15001   },
15002   {
15003     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
15004     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
15005   }
15006 };
15007
15008 void CreateGameButtons()
15009 {
15010   int i;
15011
15012   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15013   {
15014     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15015     struct XY *pos = gamebutton_info[i].pos;
15016     struct GadgetInfo *gi;
15017     int button_type;
15018     boolean checked;
15019     unsigned int event_mask;
15020     int base_x = (tape.show_game_buttons ? VX : DX);
15021     int base_y = (tape.show_game_buttons ? VY : DY);
15022     int gd_x   = gfx->src_x;
15023     int gd_y   = gfx->src_y;
15024     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15025     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15026     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15027     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15028     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15029     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15030     int id = i;
15031
15032     if (gfx->bitmap == NULL)
15033     {
15034       game_gadget[id] = NULL;
15035
15036       continue;
15037     }
15038
15039     if (id == GAME_CTRL_ID_STOP ||
15040         id == GAME_CTRL_ID_PLAY ||
15041         id == GAME_CTRL_ID_SAVE ||
15042         id == GAME_CTRL_ID_LOAD)
15043     {
15044       button_type = GD_TYPE_NORMAL_BUTTON;
15045       checked = FALSE;
15046       event_mask = GD_EVENT_RELEASED;
15047     }
15048     else if (id == GAME_CTRL_ID_UNDO ||
15049              id == GAME_CTRL_ID_REDO)
15050     {
15051       button_type = GD_TYPE_NORMAL_BUTTON;
15052       checked = FALSE;
15053       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15054     }
15055     else
15056     {
15057       button_type = GD_TYPE_CHECK_BUTTON;
15058       checked =
15059         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15060          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15061          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15062       event_mask = GD_EVENT_PRESSED;
15063     }
15064
15065     gi = CreateGadget(GDI_CUSTOM_ID, id,
15066                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15067                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15068                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15069                       GDI_WIDTH, gfx->width,
15070                       GDI_HEIGHT, gfx->height,
15071                       GDI_TYPE, button_type,
15072                       GDI_STATE, GD_BUTTON_UNPRESSED,
15073                       GDI_CHECKED, checked,
15074                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15075                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15076                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15077                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15078                       GDI_DIRECT_DRAW, FALSE,
15079                       GDI_EVENT_MASK, event_mask,
15080                       GDI_CALLBACK_ACTION, HandleGameButtons,
15081                       GDI_END);
15082
15083     if (gi == NULL)
15084       Error(ERR_EXIT, "cannot create gadget");
15085
15086     game_gadget[id] = gi;
15087   }
15088 }
15089
15090 void FreeGameButtons()
15091 {
15092   int i;
15093
15094   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15095     FreeGadget(game_gadget[i]);
15096 }
15097
15098 static void MapGameButtonsAtSamePosition(int id)
15099 {
15100   int i;
15101
15102   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15103     if (i != id &&
15104         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15105         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15106       MapGadget(game_gadget[i]);
15107 }
15108
15109 static void UnmapGameButtonsAtSamePosition(int id)
15110 {
15111   int i;
15112
15113   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15114     if (i != id &&
15115         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15116         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15117       UnmapGadget(game_gadget[i]);
15118 }
15119
15120 void MapUndoRedoButtons()
15121 {
15122   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15123   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15124
15125   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15126   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15127 }
15128
15129 void UnmapUndoRedoButtons()
15130 {
15131   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15132   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15133
15134   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15135   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15136 }
15137
15138 void MapGameButtons()
15139 {
15140   int i;
15141
15142   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15143     if (i != GAME_CTRL_ID_UNDO &&
15144         i != GAME_CTRL_ID_REDO)
15145       MapGadget(game_gadget[i]);
15146
15147   if (setup.show_snapshot_buttons)
15148   {
15149     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15150     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15151     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15152   }
15153   else
15154   {
15155     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15156     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15157     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15158   }
15159
15160   RedrawGameButtons();
15161 }
15162
15163 void UnmapGameButtons()
15164 {
15165   int i;
15166
15167   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15168     UnmapGadget(game_gadget[i]);
15169 }
15170
15171 void RedrawGameButtons()
15172 {
15173   int i;
15174
15175   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15176     RedrawGadget(game_gadget[i]);
15177
15178   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15179   redraw_mask &= ~REDRAW_ALL;
15180 }
15181
15182 void GameUndoRedoExt()
15183 {
15184   ClearPlayerAction();
15185
15186   tape.pausing = TRUE;
15187
15188   RedrawPlayfield();
15189   UpdateAndDisplayGameControlValues();
15190
15191   DrawCompleteVideoDisplay();
15192   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15193   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15194   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15195                     VIDEO_STATE_1STEP_OFF), 0);
15196
15197   BackToFront();
15198 }
15199
15200 void GameUndo(int steps)
15201 {
15202   if (!CheckEngineSnapshotList())
15203     return;
15204
15205   LoadEngineSnapshot_Undo(steps);
15206
15207   GameUndoRedoExt();
15208 }
15209
15210 void GameRedo(int steps)
15211 {
15212   if (!CheckEngineSnapshotList())
15213     return;
15214
15215   LoadEngineSnapshot_Redo(steps);
15216
15217   GameUndoRedoExt();
15218 }
15219
15220 static void HandleGameButtonsExt(int id, int button)
15221 {
15222   int steps = BUTTON_STEPSIZE(button);
15223   boolean handle_game_buttons =
15224     (game_status == GAME_MODE_PLAYING ||
15225      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15226
15227   if (!handle_game_buttons)
15228     return;
15229
15230   switch (id)
15231   {
15232     case GAME_CTRL_ID_STOP:
15233       if (game_status == GAME_MODE_MAIN)
15234         break;
15235
15236       if (tape.playing)
15237         TapeStop();
15238       else
15239         RequestQuitGame(TRUE);
15240
15241       break;
15242
15243     case GAME_CTRL_ID_PAUSE:
15244     case GAME_CTRL_ID_PAUSE2:
15245       if (options.network && game_status == GAME_MODE_PLAYING)
15246       {
15247 #if defined(NETWORK_AVALIABLE)
15248         if (tape.pausing)
15249           SendToServer_ContinuePlaying();
15250         else
15251           SendToServer_PausePlaying();
15252 #endif
15253       }
15254       else
15255         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15256       break;
15257
15258     case GAME_CTRL_ID_PLAY:
15259       if (game_status == GAME_MODE_MAIN)
15260       {
15261         StartGameActions(options.network, setup.autorecord, level.random_seed);
15262       }
15263       else if (tape.pausing)
15264       {
15265 #if defined(NETWORK_AVALIABLE)
15266         if (options.network)
15267           SendToServer_ContinuePlaying();
15268         else
15269 #endif
15270           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15271       }
15272       break;
15273
15274     case GAME_CTRL_ID_UNDO:
15275       GameUndo(steps);
15276       break;
15277
15278     case GAME_CTRL_ID_REDO:
15279       GameRedo(steps);
15280       break;
15281
15282     case GAME_CTRL_ID_SAVE:
15283       TapeQuickSave();
15284       break;
15285
15286     case GAME_CTRL_ID_LOAD:
15287       TapeQuickLoad();
15288       break;
15289
15290     case SOUND_CTRL_ID_MUSIC:
15291       if (setup.sound_music)
15292       { 
15293         setup.sound_music = FALSE;
15294
15295         FadeMusic();
15296       }
15297       else if (audio.music_available)
15298       { 
15299         setup.sound = setup.sound_music = TRUE;
15300
15301         SetAudioMode(setup.sound);
15302
15303         PlayLevelMusic();
15304       }
15305       break;
15306
15307     case SOUND_CTRL_ID_LOOPS:
15308       if (setup.sound_loops)
15309         setup.sound_loops = FALSE;
15310       else if (audio.loops_available)
15311       {
15312         setup.sound = setup.sound_loops = TRUE;
15313
15314         SetAudioMode(setup.sound);
15315       }
15316       break;
15317
15318     case SOUND_CTRL_ID_SIMPLE:
15319       if (setup.sound_simple)
15320         setup.sound_simple = FALSE;
15321       else if (audio.sound_available)
15322       {
15323         setup.sound = setup.sound_simple = TRUE;
15324
15325         SetAudioMode(setup.sound);
15326       }
15327       break;
15328
15329     default:
15330       break;
15331   }
15332 }
15333
15334 static void HandleGameButtons(struct GadgetInfo *gi)
15335 {
15336   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15337 }
15338
15339 void HandleSoundButtonKeys(Key key)
15340 {
15341
15342   if (key == setup.shortcut.sound_simple)
15343     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15344   else if (key == setup.shortcut.sound_loops)
15345     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15346   else if (key == setup.shortcut.sound_music)
15347     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15348 }