added step-based engine snapshots to undo/redo game steps (continued)
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   FreeEngineSnapshotList();
3039 }
3040
3041 int get_num_special_action(int element, int action_first, int action_last)
3042 {
3043   int num_special_action = 0;
3044   int i, j;
3045
3046   for (i = action_first; i <= action_last; i++)
3047   {
3048     boolean found = FALSE;
3049
3050     for (j = 0; j < NUM_DIRECTIONS; j++)
3051       if (el_act_dir2img(element, i, j) !=
3052           el_act_dir2img(element, ACTION_DEFAULT, j))
3053         found = TRUE;
3054
3055     if (found)
3056       num_special_action++;
3057     else
3058       break;
3059   }
3060
3061   return num_special_action;
3062 }
3063
3064
3065 /*
3066   =============================================================================
3067   InitGame()
3068   -----------------------------------------------------------------------------
3069   initialize and start new game
3070   =============================================================================
3071 */
3072
3073 void InitGame()
3074 {
3075   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3076   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3077
3078   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3079   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3080   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3081   int initial_move_dir = MV_DOWN;
3082   int i, j, x, y;
3083
3084   game_status = GAME_MODE_PLAYING;
3085
3086   StopAnimation();
3087
3088   if (!game.restart_level)
3089     CloseDoor(DOOR_CLOSE_1);
3090
3091   if (level_editor_test_game)
3092     FadeSkipNextFadeIn();
3093   else
3094     FadeSetEnterScreen();
3095
3096   FadeOut(REDRAW_FIELD);
3097
3098   /* needed if different viewport properties defined for playing */
3099   ChangeViewportPropertiesIfNeeded();
3100
3101   DrawCompleteVideoDisplay();
3102
3103   InitGameEngine();
3104   InitGameControlValues();
3105
3106   /* don't play tapes over network */
3107   network_playing = (options.network && !tape.playing);
3108
3109   for (i = 0; i < MAX_PLAYERS; i++)
3110   {
3111     struct PlayerInfo *player = &stored_player[i];
3112
3113     player->index_nr = i;
3114     player->index_bit = (1 << i);
3115     player->element_nr = EL_PLAYER_1 + i;
3116
3117     player->present = FALSE;
3118     player->active = FALSE;
3119     player->mapped = FALSE;
3120
3121     player->killed = FALSE;
3122     player->reanimated = FALSE;
3123
3124     player->action = 0;
3125     player->effective_action = 0;
3126     player->programmed_action = 0;
3127
3128     player->score = 0;
3129     player->score_final = 0;
3130
3131     player->gems_still_needed = level.gems_needed;
3132     player->sokobanfields_still_needed = 0;
3133     player->lights_still_needed = 0;
3134     player->friends_still_needed = 0;
3135
3136     for (j = 0; j < MAX_NUM_KEYS; j++)
3137       player->key[j] = FALSE;
3138
3139     player->num_white_keys = 0;
3140
3141     player->dynabomb_count = 0;
3142     player->dynabomb_size = 1;
3143     player->dynabombs_left = 0;
3144     player->dynabomb_xl = FALSE;
3145
3146     player->MovDir = initial_move_dir;
3147     player->MovPos = 0;
3148     player->GfxPos = 0;
3149     player->GfxDir = initial_move_dir;
3150     player->GfxAction = ACTION_DEFAULT;
3151     player->Frame = 0;
3152     player->StepFrame = 0;
3153
3154     player->initial_element = player->element_nr;
3155     player->artwork_element =
3156       (level.use_artwork_element[i] ? level.artwork_element[i] :
3157        player->element_nr);
3158     player->use_murphy = FALSE;
3159
3160     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3161     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3162
3163     player->gravity = level.initial_player_gravity[i];
3164
3165     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3166
3167     player->actual_frame_counter = 0;
3168
3169     player->step_counter = 0;
3170
3171     player->last_move_dir = initial_move_dir;
3172
3173     player->is_active = FALSE;
3174
3175     player->is_waiting = FALSE;
3176     player->is_moving = FALSE;
3177     player->is_auto_moving = FALSE;
3178     player->is_digging = FALSE;
3179     player->is_snapping = FALSE;
3180     player->is_collecting = FALSE;
3181     player->is_pushing = FALSE;
3182     player->is_switching = FALSE;
3183     player->is_dropping = FALSE;
3184     player->is_dropping_pressed = FALSE;
3185
3186     player->is_bored = FALSE;
3187     player->is_sleeping = FALSE;
3188
3189     player->frame_counter_bored = -1;
3190     player->frame_counter_sleeping = -1;
3191
3192     player->anim_delay_counter = 0;
3193     player->post_delay_counter = 0;
3194
3195     player->dir_waiting = initial_move_dir;
3196     player->action_waiting = ACTION_DEFAULT;
3197     player->last_action_waiting = ACTION_DEFAULT;
3198     player->special_action_bored = ACTION_DEFAULT;
3199     player->special_action_sleeping = ACTION_DEFAULT;
3200
3201     player->switch_x = -1;
3202     player->switch_y = -1;
3203
3204     player->drop_x = -1;
3205     player->drop_y = -1;
3206
3207     player->show_envelope = 0;
3208
3209     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3210
3211     player->push_delay       = -1;      /* initialized when pushing starts */
3212     player->push_delay_value = game.initial_push_delay_value;
3213
3214     player->drop_delay = 0;
3215     player->drop_pressed_delay = 0;
3216
3217     player->last_jx = -1;
3218     player->last_jy = -1;
3219     player->jx = -1;
3220     player->jy = -1;
3221
3222     player->shield_normal_time_left = 0;
3223     player->shield_deadly_time_left = 0;
3224
3225     player->inventory_infinite_element = EL_UNDEFINED;
3226     player->inventory_size = 0;
3227
3228     if (level.use_initial_inventory[i])
3229     {
3230       for (j = 0; j < level.initial_inventory_size[i]; j++)
3231       {
3232         int element = level.initial_inventory_content[i][j];
3233         int collect_count = element_info[element].collect_count_initial;
3234         int k;
3235
3236         if (!IS_CUSTOM_ELEMENT(element))
3237           collect_count = 1;
3238
3239         if (collect_count == 0)
3240           player->inventory_infinite_element = element;
3241         else
3242           for (k = 0; k < collect_count; k++)
3243             if (player->inventory_size < MAX_INVENTORY_SIZE)
3244               player->inventory_element[player->inventory_size++] = element;
3245       }
3246     }
3247
3248     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3249     SnapField(player, 0, 0);
3250
3251     player->LevelSolved = FALSE;
3252     player->GameOver = FALSE;
3253
3254     player->LevelSolved_GameWon = FALSE;
3255     player->LevelSolved_GameEnd = FALSE;
3256     player->LevelSolved_PanelOff = FALSE;
3257     player->LevelSolved_SaveTape = FALSE;
3258     player->LevelSolved_SaveScore = FALSE;
3259     player->LevelSolved_CountingTime = 0;
3260     player->LevelSolved_CountingScore = 0;
3261
3262     map_player_action[i] = i;
3263   }
3264
3265   network_player_action_received = FALSE;
3266
3267 #if defined(NETWORK_AVALIABLE)
3268   /* initial null action */
3269   if (network_playing)
3270     SendToServer_MovePlayer(MV_NONE);
3271 #endif
3272
3273   ZX = ZY = -1;
3274   ExitX = ExitY = -1;
3275
3276   FrameCounter = 0;
3277   TimeFrames = 0;
3278   TimePlayed = 0;
3279   TimeLeft = level.time;
3280   TapeTime = 0;
3281
3282   ScreenMovDir = MV_NONE;
3283   ScreenMovPos = 0;
3284   ScreenGfxPos = 0;
3285
3286   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3287
3288   AllPlayersGone = FALSE;
3289
3290   game.no_time_limit = (level.time == 0);
3291
3292   game.yamyam_content_nr = 0;
3293   game.robot_wheel_active = FALSE;
3294   game.magic_wall_active = FALSE;
3295   game.magic_wall_time_left = 0;
3296   game.light_time_left = 0;
3297   game.timegate_time_left = 0;
3298   game.switchgate_pos = 0;
3299   game.wind_direction = level.wind_direction_initial;
3300
3301   game.lenses_time_left = 0;
3302   game.magnify_time_left = 0;
3303
3304   game.ball_state = level.ball_state_initial;
3305   game.ball_content_nr = 0;
3306
3307   game.envelope_active = FALSE;
3308
3309   /* set focus to local player for network games, else to all players */
3310   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3311   game.centered_player_nr_next = game.centered_player_nr;
3312   game.set_centered_player = FALSE;
3313
3314   if (network_playing && tape.recording)
3315   {
3316     /* store client dependent player focus when recording network games */
3317     tape.centered_player_nr_next = game.centered_player_nr_next;
3318     tape.set_centered_player = TRUE;
3319   }
3320
3321   for (i = 0; i < NUM_BELTS; i++)
3322   {
3323     game.belt_dir[i] = MV_NONE;
3324     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3325   }
3326
3327   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3328     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3329
3330 #if DEBUG_INIT_PLAYER
3331   if (options.debug)
3332   {
3333     printf("Player status at level initialization:\n");
3334   }
3335 #endif
3336
3337   SCAN_PLAYFIELD(x, y)
3338   {
3339     Feld[x][y] = level.field[x][y];
3340     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3341     ChangeDelay[x][y] = 0;
3342     ChangePage[x][y] = -1;
3343     CustomValue[x][y] = 0;              /* initialized in InitField() */
3344     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3345     AmoebaNr[x][y] = 0;
3346     WasJustMoving[x][y] = 0;
3347     WasJustFalling[x][y] = 0;
3348     CheckCollision[x][y] = 0;
3349     CheckImpact[x][y] = 0;
3350     Stop[x][y] = FALSE;
3351     Pushed[x][y] = FALSE;
3352
3353     ChangeCount[x][y] = 0;
3354     ChangeEvent[x][y] = -1;
3355
3356     ExplodePhase[x][y] = 0;
3357     ExplodeDelay[x][y] = 0;
3358     ExplodeField[x][y] = EX_TYPE_NONE;
3359
3360     RunnerVisit[x][y] = 0;
3361     PlayerVisit[x][y] = 0;
3362
3363     GfxFrame[x][y] = 0;
3364     GfxRandom[x][y] = INIT_GFX_RANDOM();
3365     GfxElement[x][y] = EL_UNDEFINED;
3366     GfxAction[x][y] = ACTION_DEFAULT;
3367     GfxDir[x][y] = MV_NONE;
3368     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3369   }
3370
3371   SCAN_PLAYFIELD(x, y)
3372   {
3373     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3374       emulate_bd = FALSE;
3375     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3376       emulate_sb = FALSE;
3377     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3378       emulate_sp = FALSE;
3379
3380     InitField(x, y, TRUE);
3381
3382     ResetGfxAnimation(x, y);
3383   }
3384
3385   InitBeltMovement();
3386
3387   for (i = 0; i < MAX_PLAYERS; i++)
3388   {
3389     struct PlayerInfo *player = &stored_player[i];
3390
3391     /* set number of special actions for bored and sleeping animation */
3392     player->num_special_action_bored =
3393       get_num_special_action(player->artwork_element,
3394                              ACTION_BORING_1, ACTION_BORING_LAST);
3395     player->num_special_action_sleeping =
3396       get_num_special_action(player->artwork_element,
3397                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3398   }
3399
3400   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3401                     emulate_sb ? EMU_SOKOBAN :
3402                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3403
3404   /* initialize type of slippery elements */
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406   {
3407     if (!IS_CUSTOM_ELEMENT(i))
3408     {
3409       /* default: elements slip down either to the left or right randomly */
3410       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3411
3412       /* SP style elements prefer to slip down on the left side */
3413       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3414         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3415
3416       /* BD style elements prefer to slip down on the left side */
3417       if (game.emulation == EMU_BOULDERDASH)
3418         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3419     }
3420   }
3421
3422   /* initialize explosion and ignition delay */
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (!IS_CUSTOM_ELEMENT(i))
3426     {
3427       int num_phase = 8;
3428       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3429                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3430                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3431       int last_phase = (num_phase + 1) * delay;
3432       int half_phase = (num_phase / 2) * delay;
3433
3434       element_info[i].explosion_delay = last_phase - 1;
3435       element_info[i].ignition_delay = half_phase;
3436
3437       if (i == EL_BLACK_ORB)
3438         element_info[i].ignition_delay = 1;
3439     }
3440   }
3441
3442   /* correct non-moving belts to start moving left */
3443   for (i = 0; i < NUM_BELTS; i++)
3444     if (game.belt_dir[i] == MV_NONE)
3445       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3446
3447 #if USE_NEW_PLAYER_ASSIGNMENTS
3448   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3449   /* choose default local player */
3450   local_player = &stored_player[0];
3451
3452   for (i = 0; i < MAX_PLAYERS; i++)
3453     stored_player[i].connected = FALSE;
3454
3455   local_player->connected = TRUE;
3456   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3457
3458   if (tape.playing)
3459   {
3460     for (i = 0; i < MAX_PLAYERS; i++)
3461       stored_player[i].connected = tape.player_participates[i];
3462   }
3463   else if (game.team_mode && !options.network)
3464   {
3465     /* try to guess locally connected team mode players (needed for correct
3466        assignment of player figures from level to locally playing players) */
3467
3468     for (i = 0; i < MAX_PLAYERS; i++)
3469       if (setup.input[i].use_joystick ||
3470           setup.input[i].key.left != KSYM_UNDEFINED)
3471         stored_player[i].connected = TRUE;
3472   }
3473
3474 #if DEBUG_INIT_PLAYER
3475   if (options.debug)
3476   {
3477     printf("Player status after level initialization:\n");
3478
3479     for (i = 0; i < MAX_PLAYERS; i++)
3480     {
3481       struct PlayerInfo *player = &stored_player[i];
3482
3483       printf("- player %d: present == %d, connected == %d, active == %d",
3484              i + 1,
3485              player->present,
3486              player->connected,
3487              player->active);
3488
3489       if (local_player == player)
3490         printf(" (local player)");
3491
3492       printf("\n");
3493     }
3494   }
3495 #endif
3496
3497 #if DEBUG_INIT_PLAYER
3498   if (options.debug)
3499     printf("Reassigning players ...\n");
3500 #endif
3501
3502   /* check if any connected player was not found in playfield */
3503   for (i = 0; i < MAX_PLAYERS; i++)
3504   {
3505     struct PlayerInfo *player = &stored_player[i];
3506
3507     if (player->connected && !player->present)
3508     {
3509       struct PlayerInfo *field_player = NULL;
3510
3511 #if DEBUG_INIT_PLAYER
3512       if (options.debug)
3513         printf("- looking for field player for player %d ...\n", i + 1);
3514 #endif
3515
3516       /* assign first free player found that is present in the playfield */
3517
3518       /* first try: look for unmapped playfield player that is not connected */
3519       for (j = 0; j < MAX_PLAYERS; j++)
3520         if (field_player == NULL &&
3521             stored_player[j].present &&
3522             !stored_player[j].mapped &&
3523             !stored_player[j].connected)
3524           field_player = &stored_player[j];
3525
3526       /* second try: look for *any* unmapped playfield player */
3527       for (j = 0; j < MAX_PLAYERS; j++)
3528         if (field_player == NULL &&
3529             stored_player[j].present &&
3530             !stored_player[j].mapped)
3531           field_player = &stored_player[j];
3532
3533       if (field_player != NULL)
3534       {
3535         int jx = field_player->jx, jy = field_player->jy;
3536
3537 #if DEBUG_INIT_PLAYER
3538         if (options.debug)
3539           printf("- found player %d\n", field_player->index_nr + 1);
3540 #endif
3541
3542         player->present = FALSE;
3543         player->active = FALSE;
3544
3545         field_player->present = TRUE;
3546         field_player->active = TRUE;
3547
3548         /*
3549         player->initial_element = field_player->initial_element;
3550         player->artwork_element = field_player->artwork_element;
3551
3552         player->block_last_field       = field_player->block_last_field;
3553         player->block_delay_adjustment = field_player->block_delay_adjustment;
3554         */
3555
3556         StorePlayer[jx][jy] = field_player->element_nr;
3557
3558         field_player->jx = field_player->last_jx = jx;
3559         field_player->jy = field_player->last_jy = jy;
3560
3561         if (local_player == player)
3562           local_player = field_player;
3563
3564         map_player_action[field_player->index_nr] = i;
3565
3566         field_player->mapped = TRUE;
3567
3568 #if DEBUG_INIT_PLAYER
3569         if (options.debug)
3570           printf("- map_player_action[%d] == %d\n",
3571                  field_player->index_nr + 1, i + 1);
3572 #endif
3573       }
3574     }
3575
3576     if (player->connected && player->present)
3577       player->mapped = TRUE;
3578   }
3579
3580 #if DEBUG_INIT_PLAYER
3581   if (options.debug)
3582   {
3583     printf("Player status after player assignment (first stage):\n");
3584
3585     for (i = 0; i < MAX_PLAYERS; i++)
3586     {
3587       struct PlayerInfo *player = &stored_player[i];
3588
3589       printf("- player %d: present == %d, connected == %d, active == %d",
3590              i + 1,
3591              player->present,
3592              player->connected,
3593              player->active);
3594
3595       if (local_player == player)
3596         printf(" (local player)");
3597
3598       printf("\n");
3599     }
3600   }
3601 #endif
3602
3603 #else
3604
3605   /* check if any connected player was not found in playfield */
3606   for (i = 0; i < MAX_PLAYERS; i++)
3607   {
3608     struct PlayerInfo *player = &stored_player[i];
3609
3610     if (player->connected && !player->present)
3611     {
3612       for (j = 0; j < MAX_PLAYERS; j++)
3613       {
3614         struct PlayerInfo *field_player = &stored_player[j];
3615         int jx = field_player->jx, jy = field_player->jy;
3616
3617         /* assign first free player found that is present in the playfield */
3618         if (field_player->present && !field_player->connected)
3619         {
3620           player->present = TRUE;
3621           player->active = TRUE;
3622
3623           field_player->present = FALSE;
3624           field_player->active = FALSE;
3625
3626           player->initial_element = field_player->initial_element;
3627           player->artwork_element = field_player->artwork_element;
3628
3629           player->block_last_field       = field_player->block_last_field;
3630           player->block_delay_adjustment = field_player->block_delay_adjustment;
3631
3632           StorePlayer[jx][jy] = player->element_nr;
3633
3634           player->jx = player->last_jx = jx;
3635           player->jy = player->last_jy = jy;
3636
3637           break;
3638         }
3639       }
3640     }
3641   }
3642 #endif
3643
3644 #if 0
3645   printf("::: local_player->present == %d\n", local_player->present);
3646 #endif
3647
3648   if (tape.playing)
3649   {
3650     /* when playing a tape, eliminate all players who do not participate */
3651
3652 #if USE_NEW_PLAYER_ASSIGNMENTS
3653
3654     if (!game.team_mode)
3655     {
3656       for (i = 0; i < MAX_PLAYERS; i++)
3657       {
3658         if (stored_player[i].active &&
3659             !tape.player_participates[map_player_action[i]])
3660         {
3661           struct PlayerInfo *player = &stored_player[i];
3662           int jx = player->jx, jy = player->jy;
3663
3664 #if DEBUG_INIT_PLAYER
3665           if (options.debug)
3666             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3667 #endif
3668
3669           player->active = FALSE;
3670           StorePlayer[jx][jy] = 0;
3671           Feld[jx][jy] = EL_EMPTY;
3672         }
3673       }
3674     }
3675
3676 #else
3677
3678     for (i = 0; i < MAX_PLAYERS; i++)
3679     {
3680       if (stored_player[i].active &&
3681           !tape.player_participates[i])
3682       {
3683         struct PlayerInfo *player = &stored_player[i];
3684         int jx = player->jx, jy = player->jy;
3685
3686         player->active = FALSE;
3687         StorePlayer[jx][jy] = 0;
3688         Feld[jx][jy] = EL_EMPTY;
3689       }
3690     }
3691 #endif
3692   }
3693   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3694   {
3695     /* when in single player mode, eliminate all but the first active player */
3696
3697     for (i = 0; i < MAX_PLAYERS; i++)
3698     {
3699       if (stored_player[i].active)
3700       {
3701         for (j = i + 1; j < MAX_PLAYERS; j++)
3702         {
3703           if (stored_player[j].active)
3704           {
3705             struct PlayerInfo *player = &stored_player[j];
3706             int jx = player->jx, jy = player->jy;
3707
3708             player->active = FALSE;
3709             player->present = FALSE;
3710
3711             StorePlayer[jx][jy] = 0;
3712             Feld[jx][jy] = EL_EMPTY;
3713           }
3714         }
3715       }
3716     }
3717   }
3718
3719   /* when recording the game, store which players take part in the game */
3720   if (tape.recording)
3721   {
3722 #if USE_NEW_PLAYER_ASSIGNMENTS
3723     for (i = 0; i < MAX_PLAYERS; i++)
3724       if (stored_player[i].connected)
3725         tape.player_participates[i] = TRUE;
3726 #else
3727     for (i = 0; i < MAX_PLAYERS; i++)
3728       if (stored_player[i].active)
3729         tape.player_participates[i] = TRUE;
3730 #endif
3731   }
3732
3733 #if DEBUG_INIT_PLAYER
3734   if (options.debug)
3735   {
3736     printf("Player status after player assignment (final stage):\n");
3737
3738     for (i = 0; i < MAX_PLAYERS; i++)
3739     {
3740       struct PlayerInfo *player = &stored_player[i];
3741
3742       printf("- player %d: present == %d, connected == %d, active == %d",
3743              i + 1,
3744              player->present,
3745              player->connected,
3746              player->active);
3747
3748       if (local_player == player)
3749         printf(" (local player)");
3750
3751       printf("\n");
3752     }
3753   }
3754 #endif
3755
3756   if (BorderElement == EL_EMPTY)
3757   {
3758     SBX_Left = 0;
3759     SBX_Right = lev_fieldx - SCR_FIELDX;
3760     SBY_Upper = 0;
3761     SBY_Lower = lev_fieldy - SCR_FIELDY;
3762   }
3763   else
3764   {
3765     SBX_Left = -1;
3766     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3767     SBY_Upper = -1;
3768     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3769   }
3770
3771   if (full_lev_fieldx <= SCR_FIELDX)
3772     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3773   if (full_lev_fieldy <= SCR_FIELDY)
3774     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3775
3776   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3777     SBX_Left--;
3778   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3779     SBY_Upper--;
3780
3781   /* if local player not found, look for custom element that might create
3782      the player (make some assumptions about the right custom element) */
3783   if (!local_player->present)
3784   {
3785     int start_x = 0, start_y = 0;
3786     int found_rating = 0;
3787     int found_element = EL_UNDEFINED;
3788     int player_nr = local_player->index_nr;
3789
3790     SCAN_PLAYFIELD(x, y)
3791     {
3792       int element = Feld[x][y];
3793       int content;
3794       int xx, yy;
3795       boolean is_player;
3796
3797       if (level.use_start_element[player_nr] &&
3798           level.start_element[player_nr] == element &&
3799           found_rating < 4)
3800       {
3801         start_x = x;
3802         start_y = y;
3803
3804         found_rating = 4;
3805         found_element = element;
3806       }
3807
3808       if (!IS_CUSTOM_ELEMENT(element))
3809         continue;
3810
3811       if (CAN_CHANGE(element))
3812       {
3813         for (i = 0; i < element_info[element].num_change_pages; i++)
3814         {
3815           /* check for player created from custom element as single target */
3816           content = element_info[element].change_page[i].target_element;
3817           is_player = ELEM_IS_PLAYER(content);
3818
3819           if (is_player && (found_rating < 3 ||
3820                             (found_rating == 3 && element < found_element)))
3821           {
3822             start_x = x;
3823             start_y = y;
3824
3825             found_rating = 3;
3826             found_element = element;
3827           }
3828         }
3829       }
3830
3831       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3832       {
3833         /* check for player created from custom element as explosion content */
3834         content = element_info[element].content.e[xx][yy];
3835         is_player = ELEM_IS_PLAYER(content);
3836
3837         if (is_player && (found_rating < 2 ||
3838                           (found_rating == 2 && element < found_element)))
3839         {
3840           start_x = x + xx - 1;
3841           start_y = y + yy - 1;
3842
3843           found_rating = 2;
3844           found_element = element;
3845         }
3846
3847         if (!CAN_CHANGE(element))
3848           continue;
3849
3850         for (i = 0; i < element_info[element].num_change_pages; i++)
3851         {
3852           /* check for player created from custom element as extended target */
3853           content =
3854             element_info[element].change_page[i].target_content.e[xx][yy];
3855
3856           is_player = ELEM_IS_PLAYER(content);
3857
3858           if (is_player && (found_rating < 1 ||
3859                             (found_rating == 1 && element < found_element)))
3860           {
3861             start_x = x + xx - 1;
3862             start_y = y + yy - 1;
3863
3864             found_rating = 1;
3865             found_element = element;
3866           }
3867         }
3868       }
3869     }
3870
3871     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3872                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3873                 start_x - MIDPOSX);
3874
3875     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3876                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3877                 start_y - MIDPOSY);
3878   }
3879   else
3880   {
3881     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3882                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3883                 local_player->jx - MIDPOSX);
3884
3885     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3886                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3887                 local_player->jy - MIDPOSY);
3888   }
3889
3890   /* !!! FIX THIS (START) !!! */
3891   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3892   {
3893     InitGameEngine_EM();
3894   }
3895   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3896   {
3897     InitGameEngine_SP();
3898   }
3899   else
3900   {
3901     DrawLevel(REDRAW_FIELD);
3902     DrawAllPlayers();
3903
3904     /* after drawing the level, correct some elements */
3905     if (game.timegate_time_left == 0)
3906       CloseAllOpenTimegates();
3907   }
3908
3909   /* blit playfield from scroll buffer to normal back buffer for fading in */
3910   BlitScreenToBitmap(backbuffer);
3911
3912   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3913   /* !!! FIX THIS (END) !!! */
3914
3915   FadeIn(REDRAW_FIELD);
3916
3917 #if 1
3918   // full screen redraw is required at this point in the following cases:
3919   // - special editor door undrawn when game was started from level editor
3920   // - drawing area (playfield) was changed and has to be removed completely
3921   redraw_mask = REDRAW_ALL;
3922   BackToFront();
3923 #endif
3924
3925   if (!game.restart_level)
3926   {
3927     /* copy default game door content to main double buffer */
3928
3929     /* !!! CHECK AGAIN !!! */
3930     SetPanelBackground();
3931     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3932     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3933   }
3934
3935   SetPanelBackground();
3936   SetDrawBackgroundMask(REDRAW_DOOR_1);
3937
3938   UpdateAndDisplayGameControlValues();
3939
3940   if (!game.restart_level)
3941   {
3942     UnmapGameButtons();
3943     UnmapTapeButtons();
3944     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3945     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3946     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3947     MapGameButtons();
3948     MapTapeButtons();
3949
3950     /* copy actual game door content to door double buffer for OpenDoor() */
3951     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3952
3953     OpenDoor(DOOR_OPEN_ALL);
3954
3955     PlaySound(SND_GAME_STARTING);
3956
3957     if (setup.sound_music)
3958       PlayLevelMusic();
3959
3960     KeyboardAutoRepeatOffUnlessAutoplay();
3961
3962 #if DEBUG_INIT_PLAYER
3963     if (options.debug)
3964     {
3965       printf("Player status (final):\n");
3966
3967       for (i = 0; i < MAX_PLAYERS; i++)
3968       {
3969         struct PlayerInfo *player = &stored_player[i];
3970
3971         printf("- player %d: present == %d, connected == %d, active == %d",
3972                i + 1,
3973                player->present,
3974                player->connected,
3975                player->active);
3976
3977         if (local_player == player)
3978           printf(" (local player)");
3979
3980         printf("\n");
3981       }
3982     }
3983 #endif
3984   }
3985
3986   UnmapAllGadgets();
3987
3988   MapGameButtons();
3989   MapTapeButtons();
3990
3991   if (!game.restart_level && !tape.playing)
3992   {
3993     LevelStats_incPlayed(level_nr);
3994
3995     SaveLevelSetup_SeriesInfo();
3996   }
3997
3998   game.restart_level = FALSE;
3999
4000   SaveEngineSnapshotToList();
4001 }
4002
4003 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4004 {
4005   /* this is used for non-R'n'D game engines to update certain engine values */
4006
4007   /* needed to determine if sounds are played within the visible screen area */
4008   scroll_x = actual_scroll_x;
4009   scroll_y = actual_scroll_y;
4010 }
4011
4012 void InitMovDir(int x, int y)
4013 {
4014   int i, element = Feld[x][y];
4015   static int xy[4][2] =
4016   {
4017     {  0, +1 },
4018     { +1,  0 },
4019     {  0, -1 },
4020     { -1,  0 }
4021   };
4022   static int direction[3][4] =
4023   {
4024     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4025     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4026     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4027   };
4028
4029   switch (element)
4030   {
4031     case EL_BUG_RIGHT:
4032     case EL_BUG_UP:
4033     case EL_BUG_LEFT:
4034     case EL_BUG_DOWN:
4035       Feld[x][y] = EL_BUG;
4036       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4037       break;
4038
4039     case EL_SPACESHIP_RIGHT:
4040     case EL_SPACESHIP_UP:
4041     case EL_SPACESHIP_LEFT:
4042     case EL_SPACESHIP_DOWN:
4043       Feld[x][y] = EL_SPACESHIP;
4044       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4045       break;
4046
4047     case EL_BD_BUTTERFLY_RIGHT:
4048     case EL_BD_BUTTERFLY_UP:
4049     case EL_BD_BUTTERFLY_LEFT:
4050     case EL_BD_BUTTERFLY_DOWN:
4051       Feld[x][y] = EL_BD_BUTTERFLY;
4052       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4053       break;
4054
4055     case EL_BD_FIREFLY_RIGHT:
4056     case EL_BD_FIREFLY_UP:
4057     case EL_BD_FIREFLY_LEFT:
4058     case EL_BD_FIREFLY_DOWN:
4059       Feld[x][y] = EL_BD_FIREFLY;
4060       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4061       break;
4062
4063     case EL_PACMAN_RIGHT:
4064     case EL_PACMAN_UP:
4065     case EL_PACMAN_LEFT:
4066     case EL_PACMAN_DOWN:
4067       Feld[x][y] = EL_PACMAN;
4068       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4069       break;
4070
4071     case EL_YAMYAM_LEFT:
4072     case EL_YAMYAM_RIGHT:
4073     case EL_YAMYAM_UP:
4074     case EL_YAMYAM_DOWN:
4075       Feld[x][y] = EL_YAMYAM;
4076       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4077       break;
4078
4079     case EL_SP_SNIKSNAK:
4080       MovDir[x][y] = MV_UP;
4081       break;
4082
4083     case EL_SP_ELECTRON:
4084       MovDir[x][y] = MV_LEFT;
4085       break;
4086
4087     case EL_MOLE_LEFT:
4088     case EL_MOLE_RIGHT:
4089     case EL_MOLE_UP:
4090     case EL_MOLE_DOWN:
4091       Feld[x][y] = EL_MOLE;
4092       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4093       break;
4094
4095     default:
4096       if (IS_CUSTOM_ELEMENT(element))
4097       {
4098         struct ElementInfo *ei = &element_info[element];
4099         int move_direction_initial = ei->move_direction_initial;
4100         int move_pattern = ei->move_pattern;
4101
4102         if (move_direction_initial == MV_START_PREVIOUS)
4103         {
4104           if (MovDir[x][y] != MV_NONE)
4105             return;
4106
4107           move_direction_initial = MV_START_AUTOMATIC;
4108         }
4109
4110         if (move_direction_initial == MV_START_RANDOM)
4111           MovDir[x][y] = 1 << RND(4);
4112         else if (move_direction_initial & MV_ANY_DIRECTION)
4113           MovDir[x][y] = move_direction_initial;
4114         else if (move_pattern == MV_ALL_DIRECTIONS ||
4115                  move_pattern == MV_TURNING_LEFT ||
4116                  move_pattern == MV_TURNING_RIGHT ||
4117                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4118                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4119                  move_pattern == MV_TURNING_RANDOM)
4120           MovDir[x][y] = 1 << RND(4);
4121         else if (move_pattern == MV_HORIZONTAL)
4122           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4123         else if (move_pattern == MV_VERTICAL)
4124           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4125         else if (move_pattern & MV_ANY_DIRECTION)
4126           MovDir[x][y] = element_info[element].move_pattern;
4127         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4128                  move_pattern == MV_ALONG_RIGHT_SIDE)
4129         {
4130           /* use random direction as default start direction */
4131           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4132             MovDir[x][y] = 1 << RND(4);
4133
4134           for (i = 0; i < NUM_DIRECTIONS; i++)
4135           {
4136             int x1 = x + xy[i][0];
4137             int y1 = y + xy[i][1];
4138
4139             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4140             {
4141               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4142                 MovDir[x][y] = direction[0][i];
4143               else
4144                 MovDir[x][y] = direction[1][i];
4145
4146               break;
4147             }
4148           }
4149         }                
4150       }
4151       else
4152       {
4153         MovDir[x][y] = 1 << RND(4);
4154
4155         if (element != EL_BUG &&
4156             element != EL_SPACESHIP &&
4157             element != EL_BD_BUTTERFLY &&
4158             element != EL_BD_FIREFLY)
4159           break;
4160
4161         for (i = 0; i < NUM_DIRECTIONS; i++)
4162         {
4163           int x1 = x + xy[i][0];
4164           int y1 = y + xy[i][1];
4165
4166           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4167           {
4168             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4169             {
4170               MovDir[x][y] = direction[0][i];
4171               break;
4172             }
4173             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4174                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4175             {
4176               MovDir[x][y] = direction[1][i];
4177               break;
4178             }
4179           }
4180         }
4181       }
4182       break;
4183   }
4184
4185   GfxDir[x][y] = MovDir[x][y];
4186 }
4187
4188 void InitAmoebaNr(int x, int y)
4189 {
4190   int i;
4191   int group_nr = AmoebeNachbarNr(x, y);
4192
4193   if (group_nr == 0)
4194   {
4195     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4196     {
4197       if (AmoebaCnt[i] == 0)
4198       {
4199         group_nr = i;
4200         break;
4201       }
4202     }
4203   }
4204
4205   AmoebaNr[x][y] = group_nr;
4206   AmoebaCnt[group_nr]++;
4207   AmoebaCnt2[group_nr]++;
4208 }
4209
4210 static void PlayerWins(struct PlayerInfo *player)
4211 {
4212   player->LevelSolved = TRUE;
4213   player->GameOver = TRUE;
4214
4215   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4216                          level.native_em_level->lev->score : player->score);
4217
4218   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4219                                       TimeLeft);
4220   player->LevelSolved_CountingScore = player->score_final;
4221 }
4222
4223 void GameWon()
4224 {
4225   static int time, time_final;
4226   static int score, score_final;
4227   static int game_over_delay_1 = 0;
4228   static int game_over_delay_2 = 0;
4229   int game_over_delay_value_1 = 50;
4230   int game_over_delay_value_2 = 50;
4231
4232   if (!local_player->LevelSolved_GameWon)
4233   {
4234     int i;
4235
4236     /* do not start end game actions before the player stops moving (to exit) */
4237     if (local_player->MovPos)
4238       return;
4239
4240     local_player->LevelSolved_GameWon = TRUE;
4241     local_player->LevelSolved_SaveTape = tape.recording;
4242     local_player->LevelSolved_SaveScore = !tape.playing;
4243
4244     if (!tape.playing)
4245     {
4246       LevelStats_incSolved(level_nr);
4247
4248       SaveLevelSetup_SeriesInfo();
4249     }
4250
4251     if (tape.auto_play)         /* tape might already be stopped here */
4252       tape.auto_play_level_solved = TRUE;
4253
4254     TapeStop();
4255
4256     game_over_delay_1 = game_over_delay_value_1;
4257     game_over_delay_2 = game_over_delay_value_2;
4258
4259     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4260     score = score_final = local_player->score_final;
4261
4262     if (TimeLeft > 0)
4263     {
4264       time_final = 0;
4265       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4266     }
4267     else if (game.no_time_limit && TimePlayed < 999)
4268     {
4269       time_final = 999;
4270       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4271     }
4272
4273     local_player->score_final = score_final;
4274
4275     if (level_editor_test_game)
4276     {
4277       time = time_final;
4278       score = score_final;
4279
4280       local_player->LevelSolved_CountingTime = time;
4281       local_player->LevelSolved_CountingScore = score;
4282
4283       game_panel_controls[GAME_PANEL_TIME].value = time;
4284       game_panel_controls[GAME_PANEL_SCORE].value = score;
4285
4286       DisplayGameControlValues();
4287     }
4288
4289     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4290     {
4291       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4292       {
4293         /* close exit door after last player */
4294         if ((AllPlayersGone &&
4295              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4296               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4297               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4298             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4299             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4300         {
4301           int element = Feld[ExitX][ExitY];
4302
4303           Feld[ExitX][ExitY] =
4304             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4305              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4306              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4307              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4308              EL_EM_STEEL_EXIT_CLOSING);
4309
4310           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4311         }
4312
4313         /* player disappears */
4314         DrawLevelField(ExitX, ExitY);
4315       }
4316
4317       for (i = 0; i < MAX_PLAYERS; i++)
4318       {
4319         struct PlayerInfo *player = &stored_player[i];
4320
4321         if (player->present)
4322         {
4323           RemovePlayer(player);
4324
4325           /* player disappears */
4326           DrawLevelField(player->jx, player->jy);
4327         }
4328       }
4329     }
4330
4331     PlaySound(SND_GAME_WINNING);
4332   }
4333
4334   if (game_over_delay_1 > 0)
4335   {
4336     game_over_delay_1--;
4337
4338     return;
4339   }
4340
4341   if (time != time_final)
4342   {
4343     int time_to_go = ABS(time_final - time);
4344     int time_count_dir = (time < time_final ? +1 : -1);
4345     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4346
4347     time  += time_count_steps * time_count_dir;
4348     score += time_count_steps * level.score[SC_TIME_BONUS];
4349
4350     local_player->LevelSolved_CountingTime = time;
4351     local_player->LevelSolved_CountingScore = score;
4352
4353     game_panel_controls[GAME_PANEL_TIME].value = time;
4354     game_panel_controls[GAME_PANEL_SCORE].value = score;
4355
4356     DisplayGameControlValues();
4357
4358     if (time == time_final)
4359       StopSound(SND_GAME_LEVELTIME_BONUS);
4360     else if (setup.sound_loops)
4361       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4362     else
4363       PlaySound(SND_GAME_LEVELTIME_BONUS);
4364
4365     return;
4366   }
4367
4368   local_player->LevelSolved_PanelOff = TRUE;
4369
4370   if (game_over_delay_2 > 0)
4371   {
4372     game_over_delay_2--;
4373
4374     return;
4375   }
4376
4377   GameEnd();
4378 }
4379
4380 void GameEnd()
4381 {
4382   int hi_pos;
4383   boolean raise_level = FALSE;
4384
4385   local_player->LevelSolved_GameEnd = TRUE;
4386
4387   CloseDoor(DOOR_CLOSE_1);
4388
4389   if (local_player->LevelSolved_SaveTape)
4390   {
4391     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4392   }
4393
4394   if (level_editor_test_game)
4395   {
4396     game_status = GAME_MODE_MAIN;
4397
4398     DrawAndFadeInMainMenu(REDRAW_FIELD);
4399
4400     return;
4401   }
4402
4403   if (!local_player->LevelSolved_SaveScore)
4404   {
4405     FadeOut(REDRAW_FIELD);
4406
4407     game_status = GAME_MODE_MAIN;
4408
4409     DrawAndFadeInMainMenu(REDRAW_FIELD);
4410
4411     return;
4412   }
4413
4414   if (level_nr == leveldir_current->handicap_level)
4415   {
4416     leveldir_current->handicap_level++;
4417
4418     SaveLevelSetup_SeriesInfo();
4419   }
4420
4421   if (level_nr < leveldir_current->last_level)
4422     raise_level = TRUE;                 /* advance to next level */
4423
4424   if ((hi_pos = NewHiScore()) >= 0) 
4425   {
4426     game_status = GAME_MODE_SCORES;
4427
4428     DrawHallOfFame(hi_pos);
4429
4430     if (raise_level)
4431     {
4432       level_nr++;
4433       TapeErase();
4434     }
4435   }
4436   else
4437   {
4438     FadeOut(REDRAW_FIELD);
4439
4440     game_status = GAME_MODE_MAIN;
4441
4442     if (raise_level)
4443     {
4444       level_nr++;
4445       TapeErase();
4446     }
4447
4448     DrawAndFadeInMainMenu(REDRAW_FIELD);
4449   }
4450 }
4451
4452 int NewHiScore()
4453 {
4454   int k, l;
4455   int position = -1;
4456
4457   LoadScore(level_nr);
4458
4459   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4460       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4461     return -1;
4462
4463   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4464   {
4465     if (local_player->score_final > highscore[k].Score)
4466     {
4467       /* player has made it to the hall of fame */
4468
4469       if (k < MAX_SCORE_ENTRIES - 1)
4470       {
4471         int m = MAX_SCORE_ENTRIES - 1;
4472
4473 #ifdef ONE_PER_NAME
4474         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4475           if (strEqual(setup.player_name, highscore[l].Name))
4476             m = l;
4477         if (m == k)     /* player's new highscore overwrites his old one */
4478           goto put_into_list;
4479 #endif
4480
4481         for (l = m; l > k; l--)
4482         {
4483           strcpy(highscore[l].Name, highscore[l - 1].Name);
4484           highscore[l].Score = highscore[l - 1].Score;
4485         }
4486       }
4487
4488 #ifdef ONE_PER_NAME
4489       put_into_list:
4490 #endif
4491       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4492       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4493       highscore[k].Score = local_player->score_final; 
4494       position = k;
4495       break;
4496     }
4497
4498 #ifdef ONE_PER_NAME
4499     else if (!strncmp(setup.player_name, highscore[k].Name,
4500                       MAX_PLAYER_NAME_LEN))
4501       break;    /* player already there with a higher score */
4502 #endif
4503
4504   }
4505
4506   if (position >= 0) 
4507     SaveScore(level_nr);
4508
4509   return position;
4510 }
4511
4512 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4513 {
4514   int element = Feld[x][y];
4515   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4516   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4517   int horiz_move = (dx != 0);
4518   int sign = (horiz_move ? dx : dy);
4519   int step = sign * element_info[element].move_stepsize;
4520
4521   /* special values for move stepsize for spring and things on conveyor belt */
4522   if (horiz_move)
4523   {
4524     if (CAN_FALL(element) &&
4525         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4526       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4527     else if (element == EL_SPRING)
4528       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4529   }
4530
4531   return step;
4532 }
4533
4534 inline static int getElementMoveStepsize(int x, int y)
4535 {
4536   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4537 }
4538
4539 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4540 {
4541   if (player->GfxAction != action || player->GfxDir != dir)
4542   {
4543     player->GfxAction = action;
4544     player->GfxDir = dir;
4545     player->Frame = 0;
4546     player->StepFrame = 0;
4547   }
4548 }
4549
4550 static void ResetGfxFrame(int x, int y, boolean redraw)
4551 {
4552   int element = Feld[x][y];
4553   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4554   int last_gfx_frame = GfxFrame[x][y];
4555
4556   if (graphic_info[graphic].anim_global_sync)
4557     GfxFrame[x][y] = FrameCounter;
4558   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4559     GfxFrame[x][y] = CustomValue[x][y];
4560   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4561     GfxFrame[x][y] = element_info[element].collect_score;
4562   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4563     GfxFrame[x][y] = ChangeDelay[x][y];
4564
4565   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4566     DrawLevelGraphicAnimation(x, y, graphic);
4567 }
4568
4569 static void ResetGfxAnimation(int x, int y)
4570 {
4571   GfxAction[x][y] = ACTION_DEFAULT;
4572   GfxDir[x][y] = MovDir[x][y];
4573   GfxFrame[x][y] = 0;
4574
4575   ResetGfxFrame(x, y, FALSE);
4576 }
4577
4578 static void ResetRandomAnimationValue(int x, int y)
4579 {
4580   GfxRandom[x][y] = INIT_GFX_RANDOM();
4581 }
4582
4583 void InitMovingField(int x, int y, int direction)
4584 {
4585   int element = Feld[x][y];
4586   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4587   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4588   int newx = x + dx;
4589   int newy = y + dy;
4590   boolean is_moving_before, is_moving_after;
4591
4592   /* check if element was/is moving or being moved before/after mode change */
4593   is_moving_before = (WasJustMoving[x][y] != 0);
4594   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4595
4596   /* reset animation only for moving elements which change direction of moving
4597      or which just started or stopped moving
4598      (else CEs with property "can move" / "not moving" are reset each frame) */
4599   if (is_moving_before != is_moving_after ||
4600       direction != MovDir[x][y])
4601     ResetGfxAnimation(x, y);
4602
4603   MovDir[x][y] = direction;
4604   GfxDir[x][y] = direction;
4605
4606   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4607                      direction == MV_DOWN && CAN_FALL(element) ?
4608                      ACTION_FALLING : ACTION_MOVING);
4609
4610   /* this is needed for CEs with property "can move" / "not moving" */
4611
4612   if (is_moving_after)
4613   {
4614     if (Feld[newx][newy] == EL_EMPTY)
4615       Feld[newx][newy] = EL_BLOCKED;
4616
4617     MovDir[newx][newy] = MovDir[x][y];
4618
4619     CustomValue[newx][newy] = CustomValue[x][y];
4620
4621     GfxFrame[newx][newy] = GfxFrame[x][y];
4622     GfxRandom[newx][newy] = GfxRandom[x][y];
4623     GfxAction[newx][newy] = GfxAction[x][y];
4624     GfxDir[newx][newy] = GfxDir[x][y];
4625   }
4626 }
4627
4628 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4629 {
4630   int direction = MovDir[x][y];
4631   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4632   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4633
4634   *goes_to_x = newx;
4635   *goes_to_y = newy;
4636 }
4637
4638 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4639 {
4640   int oldx = x, oldy = y;
4641   int direction = MovDir[x][y];
4642
4643   if (direction == MV_LEFT)
4644     oldx++;
4645   else if (direction == MV_RIGHT)
4646     oldx--;
4647   else if (direction == MV_UP)
4648     oldy++;
4649   else if (direction == MV_DOWN)
4650     oldy--;
4651
4652   *comes_from_x = oldx;
4653   *comes_from_y = oldy;
4654 }
4655
4656 int MovingOrBlocked2Element(int x, int y)
4657 {
4658   int element = Feld[x][y];
4659
4660   if (element == EL_BLOCKED)
4661   {
4662     int oldx, oldy;
4663
4664     Blocked2Moving(x, y, &oldx, &oldy);
4665     return Feld[oldx][oldy];
4666   }
4667   else
4668     return element;
4669 }
4670
4671 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4672 {
4673   /* like MovingOrBlocked2Element(), but if element is moving
4674      and (x,y) is the field the moving element is just leaving,
4675      return EL_BLOCKED instead of the element value */
4676   int element = Feld[x][y];
4677
4678   if (IS_MOVING(x, y))
4679   {
4680     if (element == EL_BLOCKED)
4681     {
4682       int oldx, oldy;
4683
4684       Blocked2Moving(x, y, &oldx, &oldy);
4685       return Feld[oldx][oldy];
4686     }
4687     else
4688       return EL_BLOCKED;
4689   }
4690   else
4691     return element;
4692 }
4693
4694 static void RemoveField(int x, int y)
4695 {
4696   Feld[x][y] = EL_EMPTY;
4697
4698   MovPos[x][y] = 0;
4699   MovDir[x][y] = 0;
4700   MovDelay[x][y] = 0;
4701
4702   CustomValue[x][y] = 0;
4703
4704   AmoebaNr[x][y] = 0;
4705   ChangeDelay[x][y] = 0;
4706   ChangePage[x][y] = -1;
4707   Pushed[x][y] = FALSE;
4708
4709   GfxElement[x][y] = EL_UNDEFINED;
4710   GfxAction[x][y] = ACTION_DEFAULT;
4711   GfxDir[x][y] = MV_NONE;
4712 }
4713
4714 void RemoveMovingField(int x, int y)
4715 {
4716   int oldx = x, oldy = y, newx = x, newy = y;
4717   int element = Feld[x][y];
4718   int next_element = EL_UNDEFINED;
4719
4720   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4721     return;
4722
4723   if (IS_MOVING(x, y))
4724   {
4725     Moving2Blocked(x, y, &newx, &newy);
4726
4727     if (Feld[newx][newy] != EL_BLOCKED)
4728     {
4729       /* element is moving, but target field is not free (blocked), but
4730          already occupied by something different (example: acid pool);
4731          in this case, only remove the moving field, but not the target */
4732
4733       RemoveField(oldx, oldy);
4734
4735       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4736
4737       TEST_DrawLevelField(oldx, oldy);
4738
4739       return;
4740     }
4741   }
4742   else if (element == EL_BLOCKED)
4743   {
4744     Blocked2Moving(x, y, &oldx, &oldy);
4745     if (!IS_MOVING(oldx, oldy))
4746       return;
4747   }
4748
4749   if (element == EL_BLOCKED &&
4750       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4751        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4752        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4753        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4754        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4755        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4756     next_element = get_next_element(Feld[oldx][oldy]);
4757
4758   RemoveField(oldx, oldy);
4759   RemoveField(newx, newy);
4760
4761   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4762
4763   if (next_element != EL_UNDEFINED)
4764     Feld[oldx][oldy] = next_element;
4765
4766   TEST_DrawLevelField(oldx, oldy);
4767   TEST_DrawLevelField(newx, newy);
4768 }
4769
4770 void DrawDynamite(int x, int y)
4771 {
4772   int sx = SCREENX(x), sy = SCREENY(y);
4773   int graphic = el2img(Feld[x][y]);
4774   int frame;
4775
4776   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4777     return;
4778
4779   if (IS_WALKABLE_INSIDE(Back[x][y]))
4780     return;
4781
4782   if (Back[x][y])
4783     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4784   else if (Store[x][y])
4785     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4786
4787   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4788
4789   if (Back[x][y] || Store[x][y])
4790     DrawGraphicThruMask(sx, sy, graphic, frame);
4791   else
4792     DrawGraphic(sx, sy, graphic, frame);
4793 }
4794
4795 void CheckDynamite(int x, int y)
4796 {
4797   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4798   {
4799     MovDelay[x][y]--;
4800
4801     if (MovDelay[x][y] != 0)
4802     {
4803       DrawDynamite(x, y);
4804       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4805
4806       return;
4807     }
4808   }
4809
4810   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4811
4812   Bang(x, y);
4813 }
4814
4815 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4816 {
4817   boolean num_checked_players = 0;
4818   int i;
4819
4820   for (i = 0; i < MAX_PLAYERS; i++)
4821   {
4822     if (stored_player[i].active)
4823     {
4824       int sx = stored_player[i].jx;
4825       int sy = stored_player[i].jy;
4826
4827       if (num_checked_players == 0)
4828       {
4829         *sx1 = *sx2 = sx;
4830         *sy1 = *sy2 = sy;
4831       }
4832       else
4833       {
4834         *sx1 = MIN(*sx1, sx);
4835         *sy1 = MIN(*sy1, sy);
4836         *sx2 = MAX(*sx2, sx);
4837         *sy2 = MAX(*sy2, sy);
4838       }
4839
4840       num_checked_players++;
4841     }
4842   }
4843 }
4844
4845 static boolean checkIfAllPlayersFitToScreen_RND()
4846 {
4847   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4848
4849   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4850
4851   return (sx2 - sx1 < SCR_FIELDX &&
4852           sy2 - sy1 < SCR_FIELDY);
4853 }
4854
4855 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4856 {
4857   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4858
4859   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4860
4861   *sx = (sx1 + sx2) / 2;
4862   *sy = (sy1 + sy2) / 2;
4863 }
4864
4865 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4866                         boolean center_screen, boolean quick_relocation)
4867 {
4868   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4869   boolean no_delay = (tape.warp_forward);
4870   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4871   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4872
4873   if (quick_relocation)
4874   {
4875     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4876     {
4877       if (!level.shifted_relocation || center_screen)
4878       {
4879         /* quick relocation (without scrolling), with centering of screen */
4880
4881         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4882                     x > SBX_Right + MIDPOSX ? SBX_Right :
4883                     x - MIDPOSX);
4884
4885         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4886                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4887                     y - MIDPOSY);
4888       }
4889       else
4890       {
4891         /* quick relocation (without scrolling), but do not center screen */
4892
4893         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4894                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4895                                old_x - MIDPOSX);
4896
4897         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4898                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4899                                old_y - MIDPOSY);
4900
4901         int offset_x = x + (scroll_x - center_scroll_x);
4902         int offset_y = y + (scroll_y - center_scroll_y);
4903
4904         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4905                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4906                     offset_x - MIDPOSX);
4907
4908         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4909                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4910                     offset_y - MIDPOSY);
4911       }
4912     }
4913     else
4914     {
4915       if (!level.shifted_relocation || center_screen)
4916       {
4917         /* quick relocation (without scrolling), with centering of screen */
4918
4919         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4920                     x > SBX_Right + MIDPOSX ? SBX_Right :
4921                     x - MIDPOSX);
4922
4923         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4924                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4925                     y - MIDPOSY);
4926       }
4927       else
4928       {
4929         /* quick relocation (without scrolling), but do not center screen */
4930
4931         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4932                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4933                                old_x - MIDPOSX);
4934
4935         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4936                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4937                                old_y - MIDPOSY);
4938
4939         int offset_x = x + (scroll_x - center_scroll_x);
4940         int offset_y = y + (scroll_y - center_scroll_y);
4941
4942         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4943                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4944                     offset_x - MIDPOSX);
4945
4946         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4947                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4948                     offset_y - MIDPOSY);
4949       }
4950     }
4951
4952     RedrawPlayfield(TRUE, 0,0,0,0);
4953   }
4954   else
4955   {
4956     int scroll_xx, scroll_yy;
4957
4958     if (!level.shifted_relocation || center_screen)
4959     {
4960       /* visible relocation (with scrolling), with centering of screen */
4961
4962       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4963                    x > SBX_Right + MIDPOSX ? SBX_Right :
4964                    x - MIDPOSX);
4965
4966       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4967                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4968                    y - MIDPOSY);
4969     }
4970     else
4971     {
4972       /* visible relocation (with scrolling), but do not center screen */
4973
4974       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4975                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4976                              old_x - MIDPOSX);
4977
4978       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4979                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4980                              old_y - MIDPOSY);
4981
4982       int offset_x = x + (scroll_x - center_scroll_x);
4983       int offset_y = y + (scroll_y - center_scroll_y);
4984
4985       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4986                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4987                    offset_x - MIDPOSX);
4988
4989       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4990                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4991                    offset_y - MIDPOSY);
4992     }
4993
4994
4995     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4996
4997     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4998     {
4999       int dx = 0, dy = 0;
5000       int fx = FX, fy = FY;
5001
5002       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5003       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5004
5005       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5006         break;
5007
5008       scroll_x -= dx;
5009       scroll_y -= dy;
5010
5011       fx += dx * TILEX / 2;
5012       fy += dy * TILEY / 2;
5013
5014       ScrollLevel(dx, dy);
5015       DrawAllPlayers();
5016
5017       /* scroll in two steps of half tile size to make things smoother */
5018       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5019       Delay(wait_delay_value);
5020
5021       /* scroll second step to align at full tile size */
5022       BackToFront();
5023       Delay(wait_delay_value);
5024     }
5025
5026     DrawAllPlayers();
5027     BackToFront();
5028     Delay(wait_delay_value);
5029   }
5030 }
5031
5032 void RelocatePlayer(int jx, int jy, int el_player_raw)
5033 {
5034   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5035   int player_nr = GET_PLAYER_NR(el_player);
5036   struct PlayerInfo *player = &stored_player[player_nr];
5037   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5038   boolean no_delay = (tape.warp_forward);
5039   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5040   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5041   int old_jx = player->jx;
5042   int old_jy = player->jy;
5043   int old_element = Feld[old_jx][old_jy];
5044   int element = Feld[jx][jy];
5045   boolean player_relocated = (old_jx != jx || old_jy != jy);
5046
5047   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5048   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5049   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5050   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5051   int leave_side_horiz = move_dir_horiz;
5052   int leave_side_vert  = move_dir_vert;
5053   int enter_side = enter_side_horiz | enter_side_vert;
5054   int leave_side = leave_side_horiz | leave_side_vert;
5055
5056   if (player->GameOver)         /* do not reanimate dead player */
5057     return;
5058
5059   if (!player_relocated)        /* no need to relocate the player */
5060     return;
5061
5062   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5063   {
5064     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5065     DrawLevelField(jx, jy);
5066   }
5067
5068   if (player->present)
5069   {
5070     while (player->MovPos)
5071     {
5072       ScrollPlayer(player, SCROLL_GO_ON);
5073       ScrollScreen(NULL, SCROLL_GO_ON);
5074
5075       AdvanceFrameAndPlayerCounters(player->index_nr);
5076
5077       DrawPlayer(player);
5078
5079       BackToFront();
5080       Delay(wait_delay_value);
5081     }
5082
5083     DrawPlayer(player);         /* needed here only to cleanup last field */
5084     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5085
5086     player->is_moving = FALSE;
5087   }
5088
5089   if (IS_CUSTOM_ELEMENT(old_element))
5090     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5091                                CE_LEFT_BY_PLAYER,
5092                                player->index_bit, leave_side);
5093
5094   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5095                                       CE_PLAYER_LEAVES_X,
5096                                       player->index_bit, leave_side);
5097
5098   Feld[jx][jy] = el_player;
5099   InitPlayerField(jx, jy, el_player, TRUE);
5100
5101   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5102      possible that the relocation target field did not contain a player element,
5103      but a walkable element, to which the new player was relocated -- in this
5104      case, restore that (already initialized!) element on the player field */
5105   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5106   {
5107     Feld[jx][jy] = element;     /* restore previously existing element */
5108   }
5109
5110   /* only visually relocate centered player */
5111   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5112                      FALSE, level.instant_relocation);
5113
5114   TestIfPlayerTouchesBadThing(jx, jy);
5115   TestIfPlayerTouchesCustomElement(jx, jy);
5116
5117   if (IS_CUSTOM_ELEMENT(element))
5118     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5119                                player->index_bit, enter_side);
5120
5121   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5122                                       player->index_bit, enter_side);
5123
5124   if (player->is_switching)
5125   {
5126     /* ensure that relocation while still switching an element does not cause
5127        a new element to be treated as also switched directly after relocation
5128        (this is important for teleporter switches that teleport the player to
5129        a place where another teleporter switch is in the same direction, which
5130        would then incorrectly be treated as immediately switched before the
5131        direction key that caused the switch was released) */
5132
5133     player->switch_x += jx - old_jx;
5134     player->switch_y += jy - old_jy;
5135   }
5136 }
5137
5138 void Explode(int ex, int ey, int phase, int mode)
5139 {
5140   int x, y;
5141   int last_phase;
5142   int border_element;
5143
5144   /* !!! eliminate this variable !!! */
5145   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5146
5147   if (game.explosions_delayed)
5148   {
5149     ExplodeField[ex][ey] = mode;
5150     return;
5151   }
5152
5153   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5154   {
5155     int center_element = Feld[ex][ey];
5156     int artwork_element, explosion_element;     /* set these values later */
5157
5158     /* remove things displayed in background while burning dynamite */
5159     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5160       Back[ex][ey] = 0;
5161
5162     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5163     {
5164       /* put moving element to center field (and let it explode there) */
5165       center_element = MovingOrBlocked2Element(ex, ey);
5166       RemoveMovingField(ex, ey);
5167       Feld[ex][ey] = center_element;
5168     }
5169
5170     /* now "center_element" is finally determined -- set related values now */
5171     artwork_element = center_element;           /* for custom player artwork */
5172     explosion_element = center_element;         /* for custom player artwork */
5173
5174     if (IS_PLAYER(ex, ey))
5175     {
5176       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5177
5178       artwork_element = stored_player[player_nr].artwork_element;
5179
5180       if (level.use_explosion_element[player_nr])
5181       {
5182         explosion_element = level.explosion_element[player_nr];
5183         artwork_element = explosion_element;
5184       }
5185     }
5186
5187     if (mode == EX_TYPE_NORMAL ||
5188         mode == EX_TYPE_CENTER ||
5189         mode == EX_TYPE_CROSS)
5190       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5191
5192     last_phase = element_info[explosion_element].explosion_delay + 1;
5193
5194     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5195     {
5196       int xx = x - ex + 1;
5197       int yy = y - ey + 1;
5198       int element;
5199
5200       if (!IN_LEV_FIELD(x, y) ||
5201           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5202           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5203         continue;
5204
5205       element = Feld[x][y];
5206
5207       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5208       {
5209         element = MovingOrBlocked2Element(x, y);
5210
5211         if (!IS_EXPLOSION_PROOF(element))
5212           RemoveMovingField(x, y);
5213       }
5214
5215       /* indestructible elements can only explode in center (but not flames) */
5216       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5217                                            mode == EX_TYPE_BORDER)) ||
5218           element == EL_FLAMES)
5219         continue;
5220
5221       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5222          behaviour, for example when touching a yamyam that explodes to rocks
5223          with active deadly shield, a rock is created under the player !!! */
5224       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5225 #if 0
5226       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5227           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5228            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5229 #else
5230       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5231 #endif
5232       {
5233         if (IS_ACTIVE_BOMB(element))
5234         {
5235           /* re-activate things under the bomb like gate or penguin */
5236           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5237           Back[x][y] = 0;
5238         }
5239
5240         continue;
5241       }
5242
5243       /* save walkable background elements while explosion on same tile */
5244       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5245           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5246         Back[x][y] = element;
5247
5248       /* ignite explodable elements reached by other explosion */
5249       if (element == EL_EXPLOSION)
5250         element = Store2[x][y];
5251
5252       if (AmoebaNr[x][y] &&
5253           (element == EL_AMOEBA_FULL ||
5254            element == EL_BD_AMOEBA ||
5255            element == EL_AMOEBA_GROWING))
5256       {
5257         AmoebaCnt[AmoebaNr[x][y]]--;
5258         AmoebaCnt2[AmoebaNr[x][y]]--;
5259       }
5260
5261       RemoveField(x, y);
5262
5263       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5264       {
5265         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5266
5267         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5268
5269         if (PLAYERINFO(ex, ey)->use_murphy)
5270           Store[x][y] = EL_EMPTY;
5271       }
5272
5273       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5274          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5275       else if (ELEM_IS_PLAYER(center_element))
5276         Store[x][y] = EL_EMPTY;
5277       else if (center_element == EL_YAMYAM)
5278         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5279       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5280         Store[x][y] = element_info[center_element].content.e[xx][yy];
5281 #if 1
5282       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5283          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5284          otherwise) -- FIX THIS !!! */
5285       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5286         Store[x][y] = element_info[element].content.e[1][1];
5287 #else
5288       else if (!CAN_EXPLODE(element))
5289         Store[x][y] = element_info[element].content.e[1][1];
5290 #endif
5291       else
5292         Store[x][y] = EL_EMPTY;
5293
5294       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5295           center_element == EL_AMOEBA_TO_DIAMOND)
5296         Store2[x][y] = element;
5297
5298       Feld[x][y] = EL_EXPLOSION;
5299       GfxElement[x][y] = artwork_element;
5300
5301       ExplodePhase[x][y] = 1;
5302       ExplodeDelay[x][y] = last_phase;
5303
5304       Stop[x][y] = TRUE;
5305     }
5306
5307     if (center_element == EL_YAMYAM)
5308       game.yamyam_content_nr =
5309         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5310
5311     return;
5312   }
5313
5314   if (Stop[ex][ey])
5315     return;
5316
5317   x = ex;
5318   y = ey;
5319
5320   if (phase == 1)
5321     GfxFrame[x][y] = 0;         /* restart explosion animation */
5322
5323   last_phase = ExplodeDelay[x][y];
5324
5325   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5326
5327   /* this can happen if the player leaves an explosion just in time */
5328   if (GfxElement[x][y] == EL_UNDEFINED)
5329     GfxElement[x][y] = EL_EMPTY;
5330
5331   border_element = Store2[x][y];
5332   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5333     border_element = StorePlayer[x][y];
5334
5335   if (phase == element_info[border_element].ignition_delay ||
5336       phase == last_phase)
5337   {
5338     boolean border_explosion = FALSE;
5339
5340     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5341         !PLAYER_EXPLOSION_PROTECTED(x, y))
5342     {
5343       KillPlayerUnlessExplosionProtected(x, y);
5344       border_explosion = TRUE;
5345     }
5346     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5347     {
5348       Feld[x][y] = Store2[x][y];
5349       Store2[x][y] = 0;
5350       Bang(x, y);
5351       border_explosion = TRUE;
5352     }
5353     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5354     {
5355       AmoebeUmwandeln(x, y);
5356       Store2[x][y] = 0;
5357       border_explosion = TRUE;
5358     }
5359
5360     /* if an element just explodes due to another explosion (chain-reaction),
5361        do not immediately end the new explosion when it was the last frame of
5362        the explosion (as it would be done in the following "if"-statement!) */
5363     if (border_explosion && phase == last_phase)
5364       return;
5365   }
5366
5367   if (phase == last_phase)
5368   {
5369     int element;
5370
5371     element = Feld[x][y] = Store[x][y];
5372     Store[x][y] = Store2[x][y] = 0;
5373     GfxElement[x][y] = EL_UNDEFINED;
5374
5375     /* player can escape from explosions and might therefore be still alive */
5376     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5377         element <= EL_PLAYER_IS_EXPLODING_4)
5378     {
5379       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5380       int explosion_element = EL_PLAYER_1 + player_nr;
5381       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5382       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5383
5384       if (level.use_explosion_element[player_nr])
5385         explosion_element = level.explosion_element[player_nr];
5386
5387       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5388                     element_info[explosion_element].content.e[xx][yy]);
5389     }
5390
5391     /* restore probably existing indestructible background element */
5392     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5393       element = Feld[x][y] = Back[x][y];
5394     Back[x][y] = 0;
5395
5396     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5397     GfxDir[x][y] = MV_NONE;
5398     ChangeDelay[x][y] = 0;
5399     ChangePage[x][y] = -1;
5400
5401     CustomValue[x][y] = 0;
5402
5403     InitField_WithBug2(x, y, FALSE);
5404
5405     TEST_DrawLevelField(x, y);
5406
5407     TestIfElementTouchesCustomElement(x, y);
5408
5409     if (GFX_CRUMBLED(element))
5410       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5411
5412     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5413       StorePlayer[x][y] = 0;
5414
5415     if (ELEM_IS_PLAYER(element))
5416       RelocatePlayer(x, y, element);
5417   }
5418   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5419   {
5420     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5421     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5422
5423     if (phase == delay)
5424       TEST_DrawLevelFieldCrumbled(x, y);
5425
5426     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5427     {
5428       DrawLevelElement(x, y, Back[x][y]);
5429       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5430     }
5431     else if (IS_WALKABLE_UNDER(Back[x][y]))
5432     {
5433       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5434       DrawLevelElementThruMask(x, y, Back[x][y]);
5435     }
5436     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5437       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5438   }
5439 }
5440
5441 void DynaExplode(int ex, int ey)
5442 {
5443   int i, j;
5444   int dynabomb_element = Feld[ex][ey];
5445   int dynabomb_size = 1;
5446   boolean dynabomb_xl = FALSE;
5447   struct PlayerInfo *player;
5448   static int xy[4][2] =
5449   {
5450     { 0, -1 },
5451     { -1, 0 },
5452     { +1, 0 },
5453     { 0, +1 }
5454   };
5455
5456   if (IS_ACTIVE_BOMB(dynabomb_element))
5457   {
5458     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5459     dynabomb_size = player->dynabomb_size;
5460     dynabomb_xl = player->dynabomb_xl;
5461     player->dynabombs_left++;
5462   }
5463
5464   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5465
5466   for (i = 0; i < NUM_DIRECTIONS; i++)
5467   {
5468     for (j = 1; j <= dynabomb_size; j++)
5469     {
5470       int x = ex + j * xy[i][0];
5471       int y = ey + j * xy[i][1];
5472       int element;
5473
5474       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5475         break;
5476
5477       element = Feld[x][y];
5478
5479       /* do not restart explosions of fields with active bombs */
5480       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5481         continue;
5482
5483       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5484
5485       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5486           !IS_DIGGABLE(element) && !dynabomb_xl)
5487         break;
5488     }
5489   }
5490 }
5491
5492 void Bang(int x, int y)
5493 {
5494   int element = MovingOrBlocked2Element(x, y);
5495   int explosion_type = EX_TYPE_NORMAL;
5496
5497   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5498   {
5499     struct PlayerInfo *player = PLAYERINFO(x, y);
5500
5501     element = Feld[x][y] = player->initial_element;
5502
5503     if (level.use_explosion_element[player->index_nr])
5504     {
5505       int explosion_element = level.explosion_element[player->index_nr];
5506
5507       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5508         explosion_type = EX_TYPE_CROSS;
5509       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5510         explosion_type = EX_TYPE_CENTER;
5511     }
5512   }
5513
5514   switch (element)
5515   {
5516     case EL_BUG:
5517     case EL_SPACESHIP:
5518     case EL_BD_BUTTERFLY:
5519     case EL_BD_FIREFLY:
5520     case EL_YAMYAM:
5521     case EL_DARK_YAMYAM:
5522     case EL_ROBOT:
5523     case EL_PACMAN:
5524     case EL_MOLE:
5525       RaiseScoreElement(element);
5526       break;
5527
5528     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5529     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5530     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5531     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5532     case EL_DYNABOMB_INCREASE_NUMBER:
5533     case EL_DYNABOMB_INCREASE_SIZE:
5534     case EL_DYNABOMB_INCREASE_POWER:
5535       explosion_type = EX_TYPE_DYNA;
5536       break;
5537
5538     case EL_DC_LANDMINE:
5539       explosion_type = EX_TYPE_CENTER;
5540       break;
5541
5542     case EL_PENGUIN:
5543     case EL_LAMP:
5544     case EL_LAMP_ACTIVE:
5545     case EL_AMOEBA_TO_DIAMOND:
5546       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5547         explosion_type = EX_TYPE_CENTER;
5548       break;
5549
5550     default:
5551       if (element_info[element].explosion_type == EXPLODES_CROSS)
5552         explosion_type = EX_TYPE_CROSS;
5553       else if (element_info[element].explosion_type == EXPLODES_1X1)
5554         explosion_type = EX_TYPE_CENTER;
5555       break;
5556   }
5557
5558   if (explosion_type == EX_TYPE_DYNA)
5559     DynaExplode(x, y);
5560   else
5561     Explode(x, y, EX_PHASE_START, explosion_type);
5562
5563   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5564 }
5565
5566 void SplashAcid(int x, int y)
5567 {
5568   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5569       (!IN_LEV_FIELD(x - 1, y - 2) ||
5570        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5571     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5572
5573   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5574       (!IN_LEV_FIELD(x + 1, y - 2) ||
5575        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5576     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5577
5578   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5579 }
5580
5581 static void InitBeltMovement()
5582 {
5583   static int belt_base_element[4] =
5584   {
5585     EL_CONVEYOR_BELT_1_LEFT,
5586     EL_CONVEYOR_BELT_2_LEFT,
5587     EL_CONVEYOR_BELT_3_LEFT,
5588     EL_CONVEYOR_BELT_4_LEFT
5589   };
5590   static int belt_base_active_element[4] =
5591   {
5592     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5593     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5594     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5595     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5596   };
5597
5598   int x, y, i, j;
5599
5600   /* set frame order for belt animation graphic according to belt direction */
5601   for (i = 0; i < NUM_BELTS; i++)
5602   {
5603     int belt_nr = i;
5604
5605     for (j = 0; j < NUM_BELT_PARTS; j++)
5606     {
5607       int element = belt_base_active_element[belt_nr] + j;
5608       int graphic_1 = el2img(element);
5609       int graphic_2 = el2panelimg(element);
5610
5611       if (game.belt_dir[i] == MV_LEFT)
5612       {
5613         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5614         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5615       }
5616       else
5617       {
5618         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5619         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5620       }
5621     }
5622   }
5623
5624   SCAN_PLAYFIELD(x, y)
5625   {
5626     int element = Feld[x][y];
5627
5628     for (i = 0; i < NUM_BELTS; i++)
5629     {
5630       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5631       {
5632         int e_belt_nr = getBeltNrFromBeltElement(element);
5633         int belt_nr = i;
5634
5635         if (e_belt_nr == belt_nr)
5636         {
5637           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5638
5639           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5640         }
5641       }
5642     }
5643   }
5644 }
5645
5646 static void ToggleBeltSwitch(int x, int y)
5647 {
5648   static int belt_base_element[4] =
5649   {
5650     EL_CONVEYOR_BELT_1_LEFT,
5651     EL_CONVEYOR_BELT_2_LEFT,
5652     EL_CONVEYOR_BELT_3_LEFT,
5653     EL_CONVEYOR_BELT_4_LEFT
5654   };
5655   static int belt_base_active_element[4] =
5656   {
5657     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5658     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5659     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5660     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5661   };
5662   static int belt_base_switch_element[4] =
5663   {
5664     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5665     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5666     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5667     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5668   };
5669   static int belt_move_dir[4] =
5670   {
5671     MV_LEFT,
5672     MV_NONE,
5673     MV_RIGHT,
5674     MV_NONE,
5675   };
5676
5677   int element = Feld[x][y];
5678   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5679   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5680   int belt_dir = belt_move_dir[belt_dir_nr];
5681   int xx, yy, i;
5682
5683   if (!IS_BELT_SWITCH(element))
5684     return;
5685
5686   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5687   game.belt_dir[belt_nr] = belt_dir;
5688
5689   if (belt_dir_nr == 3)
5690     belt_dir_nr = 1;
5691
5692   /* set frame order for belt animation graphic according to belt direction */
5693   for (i = 0; i < NUM_BELT_PARTS; i++)
5694   {
5695     int element = belt_base_active_element[belt_nr] + i;
5696     int graphic_1 = el2img(element);
5697     int graphic_2 = el2panelimg(element);
5698
5699     if (belt_dir == MV_LEFT)
5700     {
5701       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5702       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5703     }
5704     else
5705     {
5706       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5707       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5708     }
5709   }
5710
5711   SCAN_PLAYFIELD(xx, yy)
5712   {
5713     int element = Feld[xx][yy];
5714
5715     if (IS_BELT_SWITCH(element))
5716     {
5717       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5718
5719       if (e_belt_nr == belt_nr)
5720       {
5721         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5722         TEST_DrawLevelField(xx, yy);
5723       }
5724     }
5725     else if (IS_BELT(element) && belt_dir != MV_NONE)
5726     {
5727       int e_belt_nr = getBeltNrFromBeltElement(element);
5728
5729       if (e_belt_nr == belt_nr)
5730       {
5731         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5732
5733         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5734         TEST_DrawLevelField(xx, yy);
5735       }
5736     }
5737     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5738     {
5739       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5740
5741       if (e_belt_nr == belt_nr)
5742       {
5743         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5744
5745         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5746         TEST_DrawLevelField(xx, yy);
5747       }
5748     }
5749   }
5750 }
5751
5752 static void ToggleSwitchgateSwitch(int x, int y)
5753 {
5754   int xx, yy;
5755
5756   game.switchgate_pos = !game.switchgate_pos;
5757
5758   SCAN_PLAYFIELD(xx, yy)
5759   {
5760     int element = Feld[xx][yy];
5761
5762     if (element == EL_SWITCHGATE_SWITCH_UP)
5763     {
5764       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5765       TEST_DrawLevelField(xx, yy);
5766     }
5767     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5768     {
5769       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5770       TEST_DrawLevelField(xx, yy);
5771     }
5772     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5773     {
5774       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5775       TEST_DrawLevelField(xx, yy);
5776     }
5777     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5778     {
5779       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5780       TEST_DrawLevelField(xx, yy);
5781     }
5782     else if (element == EL_SWITCHGATE_OPEN ||
5783              element == EL_SWITCHGATE_OPENING)
5784     {
5785       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5786
5787       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5788     }
5789     else if (element == EL_SWITCHGATE_CLOSED ||
5790              element == EL_SWITCHGATE_CLOSING)
5791     {
5792       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5793
5794       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5795     }
5796   }
5797 }
5798
5799 static int getInvisibleActiveFromInvisibleElement(int element)
5800 {
5801   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5802           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5803           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5804           element);
5805 }
5806
5807 static int getInvisibleFromInvisibleActiveElement(int element)
5808 {
5809   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5810           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5811           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5812           element);
5813 }
5814
5815 static void RedrawAllLightSwitchesAndInvisibleElements()
5816 {
5817   int x, y;
5818
5819   SCAN_PLAYFIELD(x, y)
5820   {
5821     int element = Feld[x][y];
5822
5823     if (element == EL_LIGHT_SWITCH &&
5824         game.light_time_left > 0)
5825     {
5826       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5827       TEST_DrawLevelField(x, y);
5828     }
5829     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5830              game.light_time_left == 0)
5831     {
5832       Feld[x][y] = EL_LIGHT_SWITCH;
5833       TEST_DrawLevelField(x, y);
5834     }
5835     else if (element == EL_EMC_DRIPPER &&
5836              game.light_time_left > 0)
5837     {
5838       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5839       TEST_DrawLevelField(x, y);
5840     }
5841     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5842              game.light_time_left == 0)
5843     {
5844       Feld[x][y] = EL_EMC_DRIPPER;
5845       TEST_DrawLevelField(x, y);
5846     }
5847     else if (element == EL_INVISIBLE_STEELWALL ||
5848              element == EL_INVISIBLE_WALL ||
5849              element == EL_INVISIBLE_SAND)
5850     {
5851       if (game.light_time_left > 0)
5852         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5853
5854       TEST_DrawLevelField(x, y);
5855
5856       /* uncrumble neighbour fields, if needed */
5857       if (element == EL_INVISIBLE_SAND)
5858         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5859     }
5860     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5861              element == EL_INVISIBLE_WALL_ACTIVE ||
5862              element == EL_INVISIBLE_SAND_ACTIVE)
5863     {
5864       if (game.light_time_left == 0)
5865         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5866
5867       TEST_DrawLevelField(x, y);
5868
5869       /* re-crumble neighbour fields, if needed */
5870       if (element == EL_INVISIBLE_SAND)
5871         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5872     }
5873   }
5874 }
5875
5876 static void RedrawAllInvisibleElementsForLenses()
5877 {
5878   int x, y;
5879
5880   SCAN_PLAYFIELD(x, y)
5881   {
5882     int element = Feld[x][y];
5883
5884     if (element == EL_EMC_DRIPPER &&
5885         game.lenses_time_left > 0)
5886     {
5887       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5888       TEST_DrawLevelField(x, y);
5889     }
5890     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5891              game.lenses_time_left == 0)
5892     {
5893       Feld[x][y] = EL_EMC_DRIPPER;
5894       TEST_DrawLevelField(x, y);
5895     }
5896     else if (element == EL_INVISIBLE_STEELWALL ||
5897              element == EL_INVISIBLE_WALL ||
5898              element == EL_INVISIBLE_SAND)
5899     {
5900       if (game.lenses_time_left > 0)
5901         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5902
5903       TEST_DrawLevelField(x, y);
5904
5905       /* uncrumble neighbour fields, if needed */
5906       if (element == EL_INVISIBLE_SAND)
5907         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5908     }
5909     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5910              element == EL_INVISIBLE_WALL_ACTIVE ||
5911              element == EL_INVISIBLE_SAND_ACTIVE)
5912     {
5913       if (game.lenses_time_left == 0)
5914         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5915
5916       TEST_DrawLevelField(x, y);
5917
5918       /* re-crumble neighbour fields, if needed */
5919       if (element == EL_INVISIBLE_SAND)
5920         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5921     }
5922   }
5923 }
5924
5925 static void RedrawAllInvisibleElementsForMagnifier()
5926 {
5927   int x, y;
5928
5929   SCAN_PLAYFIELD(x, y)
5930   {
5931     int element = Feld[x][y];
5932
5933     if (element == EL_EMC_FAKE_GRASS &&
5934         game.magnify_time_left > 0)
5935     {
5936       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5937       TEST_DrawLevelField(x, y);
5938     }
5939     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5940              game.magnify_time_left == 0)
5941     {
5942       Feld[x][y] = EL_EMC_FAKE_GRASS;
5943       TEST_DrawLevelField(x, y);
5944     }
5945     else if (IS_GATE_GRAY(element) &&
5946              game.magnify_time_left > 0)
5947     {
5948       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5949                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5950                     IS_EM_GATE_GRAY(element) ?
5951                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5952                     IS_EMC_GATE_GRAY(element) ?
5953                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5954                     IS_DC_GATE_GRAY(element) ?
5955                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5956                     element);
5957       TEST_DrawLevelField(x, y);
5958     }
5959     else if (IS_GATE_GRAY_ACTIVE(element) &&
5960              game.magnify_time_left == 0)
5961     {
5962       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5963                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5964                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5965                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5966                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5967                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5968                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5969                     EL_DC_GATE_WHITE_GRAY :
5970                     element);
5971       TEST_DrawLevelField(x, y);
5972     }
5973   }
5974 }
5975
5976 static void ToggleLightSwitch(int x, int y)
5977 {
5978   int element = Feld[x][y];
5979
5980   game.light_time_left =
5981     (element == EL_LIGHT_SWITCH ?
5982      level.time_light * FRAMES_PER_SECOND : 0);
5983
5984   RedrawAllLightSwitchesAndInvisibleElements();
5985 }
5986
5987 static void ActivateTimegateSwitch(int x, int y)
5988 {
5989   int xx, yy;
5990
5991   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5992
5993   SCAN_PLAYFIELD(xx, yy)
5994   {
5995     int element = Feld[xx][yy];
5996
5997     if (element == EL_TIMEGATE_CLOSED ||
5998         element == EL_TIMEGATE_CLOSING)
5999     {
6000       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6001       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6002     }
6003
6004     /*
6005     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6006     {
6007       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6008       TEST_DrawLevelField(xx, yy);
6009     }
6010     */
6011
6012   }
6013
6014   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6015                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6016 }
6017
6018 void Impact(int x, int y)
6019 {
6020   boolean last_line = (y == lev_fieldy - 1);
6021   boolean object_hit = FALSE;
6022   boolean impact = (last_line || object_hit);
6023   int element = Feld[x][y];
6024   int smashed = EL_STEELWALL;
6025
6026   if (!last_line)       /* check if element below was hit */
6027   {
6028     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6029       return;
6030
6031     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6032                                          MovDir[x][y + 1] != MV_DOWN ||
6033                                          MovPos[x][y + 1] <= TILEY / 2));
6034
6035     /* do not smash moving elements that left the smashed field in time */
6036     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6037         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6038       object_hit = FALSE;
6039
6040 #if USE_QUICKSAND_IMPACT_BUGFIX
6041     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6042     {
6043       RemoveMovingField(x, y + 1);
6044       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6045       Feld[x][y + 2] = EL_ROCK;
6046       TEST_DrawLevelField(x, y + 2);
6047
6048       object_hit = TRUE;
6049     }
6050
6051     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6052     {
6053       RemoveMovingField(x, y + 1);
6054       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6055       Feld[x][y + 2] = EL_ROCK;
6056       TEST_DrawLevelField(x, y + 2);
6057
6058       object_hit = TRUE;
6059     }
6060 #endif
6061
6062     if (object_hit)
6063       smashed = MovingOrBlocked2Element(x, y + 1);
6064
6065     impact = (last_line || object_hit);
6066   }
6067
6068   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6069   {
6070     SplashAcid(x, y + 1);
6071     return;
6072   }
6073
6074   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6075   /* only reset graphic animation if graphic really changes after impact */
6076   if (impact &&
6077       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6078   {
6079     ResetGfxAnimation(x, y);
6080     TEST_DrawLevelField(x, y);
6081   }
6082
6083   if (impact && CAN_EXPLODE_IMPACT(element))
6084   {
6085     Bang(x, y);
6086     return;
6087   }
6088   else if (impact && element == EL_PEARL &&
6089            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6090   {
6091     ResetGfxAnimation(x, y);
6092
6093     Feld[x][y] = EL_PEARL_BREAKING;
6094     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6095     return;
6096   }
6097   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6098   {
6099     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6100
6101     return;
6102   }
6103
6104   if (impact && element == EL_AMOEBA_DROP)
6105   {
6106     if (object_hit && IS_PLAYER(x, y + 1))
6107       KillPlayerUnlessEnemyProtected(x, y + 1);
6108     else if (object_hit && smashed == EL_PENGUIN)
6109       Bang(x, y + 1);
6110     else
6111     {
6112       Feld[x][y] = EL_AMOEBA_GROWING;
6113       Store[x][y] = EL_AMOEBA_WET;
6114
6115       ResetRandomAnimationValue(x, y);
6116     }
6117     return;
6118   }
6119
6120   if (object_hit)               /* check which object was hit */
6121   {
6122     if ((CAN_PASS_MAGIC_WALL(element) && 
6123          (smashed == EL_MAGIC_WALL ||
6124           smashed == EL_BD_MAGIC_WALL)) ||
6125         (CAN_PASS_DC_MAGIC_WALL(element) &&
6126          smashed == EL_DC_MAGIC_WALL))
6127     {
6128       int xx, yy;
6129       int activated_magic_wall =
6130         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6131          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6132          EL_DC_MAGIC_WALL_ACTIVE);
6133
6134       /* activate magic wall / mill */
6135       SCAN_PLAYFIELD(xx, yy)
6136       {
6137         if (Feld[xx][yy] == smashed)
6138           Feld[xx][yy] = activated_magic_wall;
6139       }
6140
6141       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6142       game.magic_wall_active = TRUE;
6143
6144       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6145                             SND_MAGIC_WALL_ACTIVATING :
6146                             smashed == EL_BD_MAGIC_WALL ?
6147                             SND_BD_MAGIC_WALL_ACTIVATING :
6148                             SND_DC_MAGIC_WALL_ACTIVATING));
6149     }
6150
6151     if (IS_PLAYER(x, y + 1))
6152     {
6153       if (CAN_SMASH_PLAYER(element))
6154       {
6155         KillPlayerUnlessEnemyProtected(x, y + 1);
6156         return;
6157       }
6158     }
6159     else if (smashed == EL_PENGUIN)
6160     {
6161       if (CAN_SMASH_PLAYER(element))
6162       {
6163         Bang(x, y + 1);
6164         return;
6165       }
6166     }
6167     else if (element == EL_BD_DIAMOND)
6168     {
6169       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6170       {
6171         Bang(x, y + 1);
6172         return;
6173       }
6174     }
6175     else if (((element == EL_SP_INFOTRON ||
6176                element == EL_SP_ZONK) &&
6177               (smashed == EL_SP_SNIKSNAK ||
6178                smashed == EL_SP_ELECTRON ||
6179                smashed == EL_SP_DISK_ORANGE)) ||
6180              (element == EL_SP_INFOTRON &&
6181               smashed == EL_SP_DISK_YELLOW))
6182     {
6183       Bang(x, y + 1);
6184       return;
6185     }
6186     else if (CAN_SMASH_EVERYTHING(element))
6187     {
6188       if (IS_CLASSIC_ENEMY(smashed) ||
6189           CAN_EXPLODE_SMASHED(smashed))
6190       {
6191         Bang(x, y + 1);
6192         return;
6193       }
6194       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6195       {
6196         if (smashed == EL_LAMP ||
6197             smashed == EL_LAMP_ACTIVE)
6198         {
6199           Bang(x, y + 1);
6200           return;
6201         }
6202         else if (smashed == EL_NUT)
6203         {
6204           Feld[x][y + 1] = EL_NUT_BREAKING;
6205           PlayLevelSound(x, y, SND_NUT_BREAKING);
6206           RaiseScoreElement(EL_NUT);
6207           return;
6208         }
6209         else if (smashed == EL_PEARL)
6210         {
6211           ResetGfxAnimation(x, y);
6212
6213           Feld[x][y + 1] = EL_PEARL_BREAKING;
6214           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6215           return;
6216         }
6217         else if (smashed == EL_DIAMOND)
6218         {
6219           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6220           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6221           return;
6222         }
6223         else if (IS_BELT_SWITCH(smashed))
6224         {
6225           ToggleBeltSwitch(x, y + 1);
6226         }
6227         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6228                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6229                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6230                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6231         {
6232           ToggleSwitchgateSwitch(x, y + 1);
6233         }
6234         else if (smashed == EL_LIGHT_SWITCH ||
6235                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6236         {
6237           ToggleLightSwitch(x, y + 1);
6238         }
6239         else
6240         {
6241           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6242
6243           CheckElementChangeBySide(x, y + 1, smashed, element,
6244                                    CE_SWITCHED, CH_SIDE_TOP);
6245           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6246                                             CH_SIDE_TOP);
6247         }
6248       }
6249       else
6250       {
6251         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6252       }
6253     }
6254   }
6255
6256   /* play sound of magic wall / mill */
6257   if (!last_line &&
6258       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6259        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6260        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6261   {
6262     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6263       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6264     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6265       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6266     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6267       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6268
6269     return;
6270   }
6271
6272   /* play sound of object that hits the ground */
6273   if (last_line || object_hit)
6274     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6275 }
6276
6277 inline static void TurnRoundExt(int x, int y)
6278 {
6279   static struct
6280   {
6281     int dx, dy;
6282   } move_xy[] =
6283   {
6284     {  0,  0 },
6285     { -1,  0 },
6286     { +1,  0 },
6287     {  0,  0 },
6288     {  0, -1 },
6289     {  0,  0 }, { 0, 0 }, { 0, 0 },
6290     {  0, +1 }
6291   };
6292   static struct
6293   {
6294     int left, right, back;
6295   } turn[] =
6296   {
6297     { 0,        0,              0        },
6298     { MV_DOWN,  MV_UP,          MV_RIGHT },
6299     { MV_UP,    MV_DOWN,        MV_LEFT  },
6300     { 0,        0,              0        },
6301     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6302     { 0,        0,              0        },
6303     { 0,        0,              0        },
6304     { 0,        0,              0        },
6305     { MV_RIGHT, MV_LEFT,        MV_UP    }
6306   };
6307
6308   int element = Feld[x][y];
6309   int move_pattern = element_info[element].move_pattern;
6310
6311   int old_move_dir = MovDir[x][y];
6312   int left_dir  = turn[old_move_dir].left;
6313   int right_dir = turn[old_move_dir].right;
6314   int back_dir  = turn[old_move_dir].back;
6315
6316   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6317   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6318   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6319   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6320
6321   int left_x  = x + left_dx,  left_y  = y + left_dy;
6322   int right_x = x + right_dx, right_y = y + right_dy;
6323   int move_x  = x + move_dx,  move_y  = y + move_dy;
6324
6325   int xx, yy;
6326
6327   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6328   {
6329     TestIfBadThingTouchesOtherBadThing(x, y);
6330
6331     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6332       MovDir[x][y] = right_dir;
6333     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6334       MovDir[x][y] = left_dir;
6335
6336     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6337       MovDelay[x][y] = 9;
6338     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6339       MovDelay[x][y] = 1;
6340   }
6341   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6342   {
6343     TestIfBadThingTouchesOtherBadThing(x, y);
6344
6345     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6346       MovDir[x][y] = left_dir;
6347     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6348       MovDir[x][y] = right_dir;
6349
6350     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6351       MovDelay[x][y] = 9;
6352     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6353       MovDelay[x][y] = 1;
6354   }
6355   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6356   {
6357     TestIfBadThingTouchesOtherBadThing(x, y);
6358
6359     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6360       MovDir[x][y] = left_dir;
6361     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6362       MovDir[x][y] = right_dir;
6363
6364     if (MovDir[x][y] != old_move_dir)
6365       MovDelay[x][y] = 9;
6366   }
6367   else if (element == EL_YAMYAM)
6368   {
6369     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6370     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6371
6372     if (can_turn_left && can_turn_right)
6373       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6374     else if (can_turn_left)
6375       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6376     else if (can_turn_right)
6377       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6378     else
6379       MovDir[x][y] = back_dir;
6380
6381     MovDelay[x][y] = 16 + 16 * RND(3);
6382   }
6383   else if (element == EL_DARK_YAMYAM)
6384   {
6385     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6386                                                          left_x, left_y);
6387     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6388                                                          right_x, right_y);
6389
6390     if (can_turn_left && can_turn_right)
6391       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6392     else if (can_turn_left)
6393       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6394     else if (can_turn_right)
6395       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6396     else
6397       MovDir[x][y] = back_dir;
6398
6399     MovDelay[x][y] = 16 + 16 * RND(3);
6400   }
6401   else if (element == EL_PACMAN)
6402   {
6403     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6404     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6405
6406     if (can_turn_left && can_turn_right)
6407       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6408     else if (can_turn_left)
6409       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6410     else if (can_turn_right)
6411       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6412     else
6413       MovDir[x][y] = back_dir;
6414
6415     MovDelay[x][y] = 6 + RND(40);
6416   }
6417   else if (element == EL_PIG)
6418   {
6419     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6420     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6421     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6422     boolean should_turn_left, should_turn_right, should_move_on;
6423     int rnd_value = 24;
6424     int rnd = RND(rnd_value);
6425
6426     should_turn_left = (can_turn_left &&
6427                         (!can_move_on ||
6428                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6429                                                    y + back_dy + left_dy)));
6430     should_turn_right = (can_turn_right &&
6431                          (!can_move_on ||
6432                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6433                                                     y + back_dy + right_dy)));
6434     should_move_on = (can_move_on &&
6435                       (!can_turn_left ||
6436                        !can_turn_right ||
6437                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6438                                                  y + move_dy + left_dy) ||
6439                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6440                                                  y + move_dy + right_dy)));
6441
6442     if (should_turn_left || should_turn_right || should_move_on)
6443     {
6444       if (should_turn_left && should_turn_right && should_move_on)
6445         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6446                         rnd < 2 * rnd_value / 3 ? right_dir :
6447                         old_move_dir);
6448       else if (should_turn_left && should_turn_right)
6449         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6450       else if (should_turn_left && should_move_on)
6451         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6452       else if (should_turn_right && should_move_on)
6453         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6454       else if (should_turn_left)
6455         MovDir[x][y] = left_dir;
6456       else if (should_turn_right)
6457         MovDir[x][y] = right_dir;
6458       else if (should_move_on)
6459         MovDir[x][y] = old_move_dir;
6460     }
6461     else if (can_move_on && rnd > rnd_value / 8)
6462       MovDir[x][y] = old_move_dir;
6463     else if (can_turn_left && can_turn_right)
6464       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6465     else if (can_turn_left && rnd > rnd_value / 8)
6466       MovDir[x][y] = left_dir;
6467     else if (can_turn_right && rnd > rnd_value/8)
6468       MovDir[x][y] = right_dir;
6469     else
6470       MovDir[x][y] = back_dir;
6471
6472     xx = x + move_xy[MovDir[x][y]].dx;
6473     yy = y + move_xy[MovDir[x][y]].dy;
6474
6475     if (!IN_LEV_FIELD(xx, yy) ||
6476         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6477       MovDir[x][y] = old_move_dir;
6478
6479     MovDelay[x][y] = 0;
6480   }
6481   else if (element == EL_DRAGON)
6482   {
6483     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6484     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6485     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6486     int rnd_value = 24;
6487     int rnd = RND(rnd_value);
6488
6489     if (can_move_on && rnd > rnd_value / 8)
6490       MovDir[x][y] = old_move_dir;
6491     else if (can_turn_left && can_turn_right)
6492       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6493     else if (can_turn_left && rnd > rnd_value / 8)
6494       MovDir[x][y] = left_dir;
6495     else if (can_turn_right && rnd > rnd_value / 8)
6496       MovDir[x][y] = right_dir;
6497     else
6498       MovDir[x][y] = back_dir;
6499
6500     xx = x + move_xy[MovDir[x][y]].dx;
6501     yy = y + move_xy[MovDir[x][y]].dy;
6502
6503     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6504       MovDir[x][y] = old_move_dir;
6505
6506     MovDelay[x][y] = 0;
6507   }
6508   else if (element == EL_MOLE)
6509   {
6510     boolean can_move_on =
6511       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6512                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6513                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6514     if (!can_move_on)
6515     {
6516       boolean can_turn_left =
6517         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6518                               IS_AMOEBOID(Feld[left_x][left_y])));
6519
6520       boolean can_turn_right =
6521         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6522                               IS_AMOEBOID(Feld[right_x][right_y])));
6523
6524       if (can_turn_left && can_turn_right)
6525         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6526       else if (can_turn_left)
6527         MovDir[x][y] = left_dir;
6528       else
6529         MovDir[x][y] = right_dir;
6530     }
6531
6532     if (MovDir[x][y] != old_move_dir)
6533       MovDelay[x][y] = 9;
6534   }
6535   else if (element == EL_BALLOON)
6536   {
6537     MovDir[x][y] = game.wind_direction;
6538     MovDelay[x][y] = 0;
6539   }
6540   else if (element == EL_SPRING)
6541   {
6542     if (MovDir[x][y] & MV_HORIZONTAL)
6543     {
6544       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6545           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6546       {
6547         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6548         ResetGfxAnimation(move_x, move_y);
6549         TEST_DrawLevelField(move_x, move_y);
6550
6551         MovDir[x][y] = back_dir;
6552       }
6553       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6554                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6555         MovDir[x][y] = MV_NONE;
6556     }
6557
6558     MovDelay[x][y] = 0;
6559   }
6560   else if (element == EL_ROBOT ||
6561            element == EL_SATELLITE ||
6562            element == EL_PENGUIN ||
6563            element == EL_EMC_ANDROID)
6564   {
6565     int attr_x = -1, attr_y = -1;
6566
6567     if (AllPlayersGone)
6568     {
6569       attr_x = ExitX;
6570       attr_y = ExitY;
6571     }
6572     else
6573     {
6574       int i;
6575
6576       for (i = 0; i < MAX_PLAYERS; i++)
6577       {
6578         struct PlayerInfo *player = &stored_player[i];
6579         int jx = player->jx, jy = player->jy;
6580
6581         if (!player->active)
6582           continue;
6583
6584         if (attr_x == -1 ||
6585             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6586         {
6587           attr_x = jx;
6588           attr_y = jy;
6589         }
6590       }
6591     }
6592
6593     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6594         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6595          game.engine_version < VERSION_IDENT(3,1,0,0)))
6596     {
6597       attr_x = ZX;
6598       attr_y = ZY;
6599     }
6600
6601     if (element == EL_PENGUIN)
6602     {
6603       int i;
6604       static int xy[4][2] =
6605       {
6606         { 0, -1 },
6607         { -1, 0 },
6608         { +1, 0 },
6609         { 0, +1 }
6610       };
6611
6612       for (i = 0; i < NUM_DIRECTIONS; i++)
6613       {
6614         int ex = x + xy[i][0];
6615         int ey = y + xy[i][1];
6616
6617         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6618                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6619                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6620                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6621         {
6622           attr_x = ex;
6623           attr_y = ey;
6624           break;
6625         }
6626       }
6627     }
6628
6629     MovDir[x][y] = MV_NONE;
6630     if (attr_x < x)
6631       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6632     else if (attr_x > x)
6633       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6634     if (attr_y < y)
6635       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6636     else if (attr_y > y)
6637       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6638
6639     if (element == EL_ROBOT)
6640     {
6641       int newx, newy;
6642
6643       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6644         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6645       Moving2Blocked(x, y, &newx, &newy);
6646
6647       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6648         MovDelay[x][y] = 8 + 8 * !RND(3);
6649       else
6650         MovDelay[x][y] = 16;
6651     }
6652     else if (element == EL_PENGUIN)
6653     {
6654       int newx, newy;
6655
6656       MovDelay[x][y] = 1;
6657
6658       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6659       {
6660         boolean first_horiz = RND(2);
6661         int new_move_dir = MovDir[x][y];
6662
6663         MovDir[x][y] =
6664           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6665         Moving2Blocked(x, y, &newx, &newy);
6666
6667         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6668           return;
6669
6670         MovDir[x][y] =
6671           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6672         Moving2Blocked(x, y, &newx, &newy);
6673
6674         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6675           return;
6676
6677         MovDir[x][y] = old_move_dir;
6678         return;
6679       }
6680     }
6681     else if (element == EL_SATELLITE)
6682     {
6683       int newx, newy;
6684
6685       MovDelay[x][y] = 1;
6686
6687       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6688       {
6689         boolean first_horiz = RND(2);
6690         int new_move_dir = MovDir[x][y];
6691
6692         MovDir[x][y] =
6693           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6694         Moving2Blocked(x, y, &newx, &newy);
6695
6696         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6697           return;
6698
6699         MovDir[x][y] =
6700           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6701         Moving2Blocked(x, y, &newx, &newy);
6702
6703         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6704           return;
6705
6706         MovDir[x][y] = old_move_dir;
6707         return;
6708       }
6709     }
6710     else if (element == EL_EMC_ANDROID)
6711     {
6712       static int check_pos[16] =
6713       {
6714         -1,             /*  0 => (invalid)          */
6715         7,              /*  1 => MV_LEFT            */
6716         3,              /*  2 => MV_RIGHT           */
6717         -1,             /*  3 => (invalid)          */
6718         1,              /*  4 =>            MV_UP   */
6719         0,              /*  5 => MV_LEFT  | MV_UP   */
6720         2,              /*  6 => MV_RIGHT | MV_UP   */
6721         -1,             /*  7 => (invalid)          */
6722         5,              /*  8 =>            MV_DOWN */
6723         6,              /*  9 => MV_LEFT  | MV_DOWN */
6724         4,              /* 10 => MV_RIGHT | MV_DOWN */
6725         -1,             /* 11 => (invalid)          */
6726         -1,             /* 12 => (invalid)          */
6727         -1,             /* 13 => (invalid)          */
6728         -1,             /* 14 => (invalid)          */
6729         -1,             /* 15 => (invalid)          */
6730       };
6731       static struct
6732       {
6733         int dx, dy;
6734         int dir;
6735       } check_xy[8] =
6736       {
6737         { -1, -1,       MV_LEFT  | MV_UP   },
6738         {  0, -1,                  MV_UP   },
6739         { +1, -1,       MV_RIGHT | MV_UP   },
6740         { +1,  0,       MV_RIGHT           },
6741         { +1, +1,       MV_RIGHT | MV_DOWN },
6742         {  0, +1,                  MV_DOWN },
6743         { -1, +1,       MV_LEFT  | MV_DOWN },
6744         { -1,  0,       MV_LEFT            },
6745       };
6746       int start_pos, check_order;
6747       boolean can_clone = FALSE;
6748       int i;
6749
6750       /* check if there is any free field around current position */
6751       for (i = 0; i < 8; i++)
6752       {
6753         int newx = x + check_xy[i].dx;
6754         int newy = y + check_xy[i].dy;
6755
6756         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6757         {
6758           can_clone = TRUE;
6759
6760           break;
6761         }
6762       }
6763
6764       if (can_clone)            /* randomly find an element to clone */
6765       {
6766         can_clone = FALSE;
6767
6768         start_pos = check_pos[RND(8)];
6769         check_order = (RND(2) ? -1 : +1);
6770
6771         for (i = 0; i < 8; i++)
6772         {
6773           int pos_raw = start_pos + i * check_order;
6774           int pos = (pos_raw + 8) % 8;
6775           int newx = x + check_xy[pos].dx;
6776           int newy = y + check_xy[pos].dy;
6777
6778           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6779           {
6780             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6781             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6782
6783             Store[x][y] = Feld[newx][newy];
6784
6785             can_clone = TRUE;
6786
6787             break;
6788           }
6789         }
6790       }
6791
6792       if (can_clone)            /* randomly find a direction to move */
6793       {
6794         can_clone = FALSE;
6795
6796         start_pos = check_pos[RND(8)];
6797         check_order = (RND(2) ? -1 : +1);
6798
6799         for (i = 0; i < 8; i++)
6800         {
6801           int pos_raw = start_pos + i * check_order;
6802           int pos = (pos_raw + 8) % 8;
6803           int newx = x + check_xy[pos].dx;
6804           int newy = y + check_xy[pos].dy;
6805           int new_move_dir = check_xy[pos].dir;
6806
6807           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6808           {
6809             MovDir[x][y] = new_move_dir;
6810             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6811
6812             can_clone = TRUE;
6813
6814             break;
6815           }
6816         }
6817       }
6818
6819       if (can_clone)            /* cloning and moving successful */
6820         return;
6821
6822       /* cannot clone -- try to move towards player */
6823
6824       start_pos = check_pos[MovDir[x][y] & 0x0f];
6825       check_order = (RND(2) ? -1 : +1);
6826
6827       for (i = 0; i < 3; i++)
6828       {
6829         /* first check start_pos, then previous/next or (next/previous) pos */
6830         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6831         int pos = (pos_raw + 8) % 8;
6832         int newx = x + check_xy[pos].dx;
6833         int newy = y + check_xy[pos].dy;
6834         int new_move_dir = check_xy[pos].dir;
6835
6836         if (IS_PLAYER(newx, newy))
6837           break;
6838
6839         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6840         {
6841           MovDir[x][y] = new_move_dir;
6842           MovDelay[x][y] = level.android_move_time * 8 + 1;
6843
6844           break;
6845         }
6846       }
6847     }
6848   }
6849   else if (move_pattern == MV_TURNING_LEFT ||
6850            move_pattern == MV_TURNING_RIGHT ||
6851            move_pattern == MV_TURNING_LEFT_RIGHT ||
6852            move_pattern == MV_TURNING_RIGHT_LEFT ||
6853            move_pattern == MV_TURNING_RANDOM ||
6854            move_pattern == MV_ALL_DIRECTIONS)
6855   {
6856     boolean can_turn_left =
6857       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6858     boolean can_turn_right =
6859       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6860
6861     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6862       return;
6863
6864     if (move_pattern == MV_TURNING_LEFT)
6865       MovDir[x][y] = left_dir;
6866     else if (move_pattern == MV_TURNING_RIGHT)
6867       MovDir[x][y] = right_dir;
6868     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6869       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6870     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6871       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6872     else if (move_pattern == MV_TURNING_RANDOM)
6873       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6874                       can_turn_right && !can_turn_left ? right_dir :
6875                       RND(2) ? left_dir : right_dir);
6876     else if (can_turn_left && can_turn_right)
6877       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6878     else if (can_turn_left)
6879       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6880     else if (can_turn_right)
6881       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6882     else
6883       MovDir[x][y] = back_dir;
6884
6885     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6886   }
6887   else if (move_pattern == MV_HORIZONTAL ||
6888            move_pattern == MV_VERTICAL)
6889   {
6890     if (move_pattern & old_move_dir)
6891       MovDir[x][y] = back_dir;
6892     else if (move_pattern == MV_HORIZONTAL)
6893       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6894     else if (move_pattern == MV_VERTICAL)
6895       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6896
6897     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6898   }
6899   else if (move_pattern & MV_ANY_DIRECTION)
6900   {
6901     MovDir[x][y] = move_pattern;
6902     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6903   }
6904   else if (move_pattern & MV_WIND_DIRECTION)
6905   {
6906     MovDir[x][y] = game.wind_direction;
6907     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6908   }
6909   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6910   {
6911     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6912       MovDir[x][y] = left_dir;
6913     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6914       MovDir[x][y] = right_dir;
6915
6916     if (MovDir[x][y] != old_move_dir)
6917       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6918   }
6919   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6920   {
6921     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6922       MovDir[x][y] = right_dir;
6923     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6924       MovDir[x][y] = left_dir;
6925
6926     if (MovDir[x][y] != old_move_dir)
6927       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6928   }
6929   else if (move_pattern == MV_TOWARDS_PLAYER ||
6930            move_pattern == MV_AWAY_FROM_PLAYER)
6931   {
6932     int attr_x = -1, attr_y = -1;
6933     int newx, newy;
6934     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6935
6936     if (AllPlayersGone)
6937     {
6938       attr_x = ExitX;
6939       attr_y = ExitY;
6940     }
6941     else
6942     {
6943       int i;
6944
6945       for (i = 0; i < MAX_PLAYERS; i++)
6946       {
6947         struct PlayerInfo *player = &stored_player[i];
6948         int jx = player->jx, jy = player->jy;
6949
6950         if (!player->active)
6951           continue;
6952
6953         if (attr_x == -1 ||
6954             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6955         {
6956           attr_x = jx;
6957           attr_y = jy;
6958         }
6959       }
6960     }
6961
6962     MovDir[x][y] = MV_NONE;
6963     if (attr_x < x)
6964       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6965     else if (attr_x > x)
6966       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6967     if (attr_y < y)
6968       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6969     else if (attr_y > y)
6970       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6971
6972     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6973
6974     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6975     {
6976       boolean first_horiz = RND(2);
6977       int new_move_dir = MovDir[x][y];
6978
6979       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6980       {
6981         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6982         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6983
6984         return;
6985       }
6986
6987       MovDir[x][y] =
6988         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6989       Moving2Blocked(x, y, &newx, &newy);
6990
6991       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6992         return;
6993
6994       MovDir[x][y] =
6995         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6996       Moving2Blocked(x, y, &newx, &newy);
6997
6998       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6999         return;
7000
7001       MovDir[x][y] = old_move_dir;
7002     }
7003   }
7004   else if (move_pattern == MV_WHEN_PUSHED ||
7005            move_pattern == MV_WHEN_DROPPED)
7006   {
7007     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7008       MovDir[x][y] = MV_NONE;
7009
7010     MovDelay[x][y] = 0;
7011   }
7012   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7013   {
7014     static int test_xy[7][2] =
7015     {
7016       { 0, -1 },
7017       { -1, 0 },
7018       { +1, 0 },
7019       { 0, +1 },
7020       { 0, -1 },
7021       { -1, 0 },
7022       { +1, 0 },
7023     };
7024     static int test_dir[7] =
7025     {
7026       MV_UP,
7027       MV_LEFT,
7028       MV_RIGHT,
7029       MV_DOWN,
7030       MV_UP,
7031       MV_LEFT,
7032       MV_RIGHT,
7033     };
7034     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7035     int move_preference = -1000000;     /* start with very low preference */
7036     int new_move_dir = MV_NONE;
7037     int start_test = RND(4);
7038     int i;
7039
7040     for (i = 0; i < NUM_DIRECTIONS; i++)
7041     {
7042       int move_dir = test_dir[start_test + i];
7043       int move_dir_preference;
7044
7045       xx = x + test_xy[start_test + i][0];
7046       yy = y + test_xy[start_test + i][1];
7047
7048       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7049           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7050       {
7051         new_move_dir = move_dir;
7052
7053         break;
7054       }
7055
7056       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7057         continue;
7058
7059       move_dir_preference = -1 * RunnerVisit[xx][yy];
7060       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7061         move_dir_preference = PlayerVisit[xx][yy];
7062
7063       if (move_dir_preference > move_preference)
7064       {
7065         /* prefer field that has not been visited for the longest time */
7066         move_preference = move_dir_preference;
7067         new_move_dir = move_dir;
7068       }
7069       else if (move_dir_preference == move_preference &&
7070                move_dir == old_move_dir)
7071       {
7072         /* prefer last direction when all directions are preferred equally */
7073         move_preference = move_dir_preference;
7074         new_move_dir = move_dir;
7075       }
7076     }
7077
7078     MovDir[x][y] = new_move_dir;
7079     if (old_move_dir != new_move_dir)
7080       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7081   }
7082 }
7083
7084 static void TurnRound(int x, int y)
7085 {
7086   int direction = MovDir[x][y];
7087
7088   TurnRoundExt(x, y);
7089
7090   GfxDir[x][y] = MovDir[x][y];
7091
7092   if (direction != MovDir[x][y])
7093     GfxFrame[x][y] = 0;
7094
7095   if (MovDelay[x][y])
7096     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7097
7098   ResetGfxFrame(x, y, FALSE);
7099 }
7100
7101 static boolean JustBeingPushed(int x, int y)
7102 {
7103   int i;
7104
7105   for (i = 0; i < MAX_PLAYERS; i++)
7106   {
7107     struct PlayerInfo *player = &stored_player[i];
7108
7109     if (player->active && player->is_pushing && player->MovPos)
7110     {
7111       int next_jx = player->jx + (player->jx - player->last_jx);
7112       int next_jy = player->jy + (player->jy - player->last_jy);
7113
7114       if (x == next_jx && y == next_jy)
7115         return TRUE;
7116     }
7117   }
7118
7119   return FALSE;
7120 }
7121
7122 void StartMoving(int x, int y)
7123 {
7124   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7125   int element = Feld[x][y];
7126
7127   if (Stop[x][y])
7128     return;
7129
7130   if (MovDelay[x][y] == 0)
7131     GfxAction[x][y] = ACTION_DEFAULT;
7132
7133   if (CAN_FALL(element) && y < lev_fieldy - 1)
7134   {
7135     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7136         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7137       if (JustBeingPushed(x, y))
7138         return;
7139
7140     if (element == EL_QUICKSAND_FULL)
7141     {
7142       if (IS_FREE(x, y + 1))
7143       {
7144         InitMovingField(x, y, MV_DOWN);
7145         started_moving = TRUE;
7146
7147         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7148 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7149         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7150           Store[x][y] = EL_ROCK;
7151 #else
7152         Store[x][y] = EL_ROCK;
7153 #endif
7154
7155         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7156       }
7157       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7158       {
7159         if (!MovDelay[x][y])
7160         {
7161           MovDelay[x][y] = TILEY + 1;
7162
7163           ResetGfxAnimation(x, y);
7164           ResetGfxAnimation(x, y + 1);
7165         }
7166
7167         if (MovDelay[x][y])
7168         {
7169           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7170           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7171
7172           MovDelay[x][y]--;
7173           if (MovDelay[x][y])
7174             return;
7175         }
7176
7177         Feld[x][y] = EL_QUICKSAND_EMPTY;
7178         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7179         Store[x][y + 1] = Store[x][y];
7180         Store[x][y] = 0;
7181
7182         PlayLevelSoundAction(x, y, ACTION_FILLING);
7183       }
7184       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7185       {
7186         if (!MovDelay[x][y])
7187         {
7188           MovDelay[x][y] = TILEY + 1;
7189
7190           ResetGfxAnimation(x, y);
7191           ResetGfxAnimation(x, y + 1);
7192         }
7193
7194         if (MovDelay[x][y])
7195         {
7196           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7197           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7198
7199           MovDelay[x][y]--;
7200           if (MovDelay[x][y])
7201             return;
7202         }
7203
7204         Feld[x][y] = EL_QUICKSAND_EMPTY;
7205         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7206         Store[x][y + 1] = Store[x][y];
7207         Store[x][y] = 0;
7208
7209         PlayLevelSoundAction(x, y, ACTION_FILLING);
7210       }
7211     }
7212     else if (element == EL_QUICKSAND_FAST_FULL)
7213     {
7214       if (IS_FREE(x, y + 1))
7215       {
7216         InitMovingField(x, y, MV_DOWN);
7217         started_moving = TRUE;
7218
7219         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7220 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7221         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7222           Store[x][y] = EL_ROCK;
7223 #else
7224         Store[x][y] = EL_ROCK;
7225 #endif
7226
7227         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7228       }
7229       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7230       {
7231         if (!MovDelay[x][y])
7232         {
7233           MovDelay[x][y] = TILEY + 1;
7234
7235           ResetGfxAnimation(x, y);
7236           ResetGfxAnimation(x, y + 1);
7237         }
7238
7239         if (MovDelay[x][y])
7240         {
7241           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7242           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7243
7244           MovDelay[x][y]--;
7245           if (MovDelay[x][y])
7246             return;
7247         }
7248
7249         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7250         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7251         Store[x][y + 1] = Store[x][y];
7252         Store[x][y] = 0;
7253
7254         PlayLevelSoundAction(x, y, ACTION_FILLING);
7255       }
7256       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7257       {
7258         if (!MovDelay[x][y])
7259         {
7260           MovDelay[x][y] = TILEY + 1;
7261
7262           ResetGfxAnimation(x, y);
7263           ResetGfxAnimation(x, y + 1);
7264         }
7265
7266         if (MovDelay[x][y])
7267         {
7268           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7269           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7270
7271           MovDelay[x][y]--;
7272           if (MovDelay[x][y])
7273             return;
7274         }
7275
7276         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7277         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7278         Store[x][y + 1] = Store[x][y];
7279         Store[x][y] = 0;
7280
7281         PlayLevelSoundAction(x, y, ACTION_FILLING);
7282       }
7283     }
7284     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7285              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7286     {
7287       InitMovingField(x, y, MV_DOWN);
7288       started_moving = TRUE;
7289
7290       Feld[x][y] = EL_QUICKSAND_FILLING;
7291       Store[x][y] = element;
7292
7293       PlayLevelSoundAction(x, y, ACTION_FILLING);
7294     }
7295     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7296              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7297     {
7298       InitMovingField(x, y, MV_DOWN);
7299       started_moving = TRUE;
7300
7301       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7302       Store[x][y] = element;
7303
7304       PlayLevelSoundAction(x, y, ACTION_FILLING);
7305     }
7306     else if (element == EL_MAGIC_WALL_FULL)
7307     {
7308       if (IS_FREE(x, y + 1))
7309       {
7310         InitMovingField(x, y, MV_DOWN);
7311         started_moving = TRUE;
7312
7313         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7314         Store[x][y] = EL_CHANGED(Store[x][y]);
7315       }
7316       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7317       {
7318         if (!MovDelay[x][y])
7319           MovDelay[x][y] = TILEY / 4 + 1;
7320
7321         if (MovDelay[x][y])
7322         {
7323           MovDelay[x][y]--;
7324           if (MovDelay[x][y])
7325             return;
7326         }
7327
7328         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7329         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7330         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7331         Store[x][y] = 0;
7332       }
7333     }
7334     else if (element == EL_BD_MAGIC_WALL_FULL)
7335     {
7336       if (IS_FREE(x, y + 1))
7337       {
7338         InitMovingField(x, y, MV_DOWN);
7339         started_moving = TRUE;
7340
7341         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7342         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7343       }
7344       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7345       {
7346         if (!MovDelay[x][y])
7347           MovDelay[x][y] = TILEY / 4 + 1;
7348
7349         if (MovDelay[x][y])
7350         {
7351           MovDelay[x][y]--;
7352           if (MovDelay[x][y])
7353             return;
7354         }
7355
7356         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7357         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7358         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7359         Store[x][y] = 0;
7360       }
7361     }
7362     else if (element == EL_DC_MAGIC_WALL_FULL)
7363     {
7364       if (IS_FREE(x, y + 1))
7365       {
7366         InitMovingField(x, y, MV_DOWN);
7367         started_moving = TRUE;
7368
7369         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7370         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7371       }
7372       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7373       {
7374         if (!MovDelay[x][y])
7375           MovDelay[x][y] = TILEY / 4 + 1;
7376
7377         if (MovDelay[x][y])
7378         {
7379           MovDelay[x][y]--;
7380           if (MovDelay[x][y])
7381             return;
7382         }
7383
7384         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7385         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7386         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7387         Store[x][y] = 0;
7388       }
7389     }
7390     else if ((CAN_PASS_MAGIC_WALL(element) &&
7391               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7392                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7393              (CAN_PASS_DC_MAGIC_WALL(element) &&
7394               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7395
7396     {
7397       InitMovingField(x, y, MV_DOWN);
7398       started_moving = TRUE;
7399
7400       Feld[x][y] =
7401         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7402          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7403          EL_DC_MAGIC_WALL_FILLING);
7404       Store[x][y] = element;
7405     }
7406     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7407     {
7408       SplashAcid(x, y + 1);
7409
7410       InitMovingField(x, y, MV_DOWN);
7411       started_moving = TRUE;
7412
7413       Store[x][y] = EL_ACID;
7414     }
7415     else if (
7416              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7417               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7418              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7419               CAN_FALL(element) && WasJustFalling[x][y] &&
7420               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7421
7422              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7423               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7424               (Feld[x][y + 1] == EL_BLOCKED)))
7425     {
7426       /* this is needed for a special case not covered by calling "Impact()"
7427          from "ContinueMoving()": if an element moves to a tile directly below
7428          another element which was just falling on that tile (which was empty
7429          in the previous frame), the falling element above would just stop
7430          instead of smashing the element below (in previous version, the above
7431          element was just checked for "moving" instead of "falling", resulting
7432          in incorrect smashes caused by horizontal movement of the above
7433          element; also, the case of the player being the element to smash was
7434          simply not covered here... :-/ ) */
7435
7436       CheckCollision[x][y] = 0;
7437       CheckImpact[x][y] = 0;
7438
7439       Impact(x, y);
7440     }
7441     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7442     {
7443       if (MovDir[x][y] == MV_NONE)
7444       {
7445         InitMovingField(x, y, MV_DOWN);
7446         started_moving = TRUE;
7447       }
7448     }
7449     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7450     {
7451       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7452         MovDir[x][y] = MV_DOWN;
7453
7454       InitMovingField(x, y, MV_DOWN);
7455       started_moving = TRUE;
7456     }
7457     else if (element == EL_AMOEBA_DROP)
7458     {
7459       Feld[x][y] = EL_AMOEBA_GROWING;
7460       Store[x][y] = EL_AMOEBA_WET;
7461     }
7462     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7463               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7464              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7465              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7466     {
7467       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7468                                 (IS_FREE(x - 1, y + 1) ||
7469                                  Feld[x - 1][y + 1] == EL_ACID));
7470       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7471                                 (IS_FREE(x + 1, y + 1) ||
7472                                  Feld[x + 1][y + 1] == EL_ACID));
7473       boolean can_fall_any  = (can_fall_left || can_fall_right);
7474       boolean can_fall_both = (can_fall_left && can_fall_right);
7475       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7476
7477       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7478       {
7479         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7480           can_fall_right = FALSE;
7481         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7482           can_fall_left = FALSE;
7483         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7484           can_fall_right = FALSE;
7485         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7486           can_fall_left = FALSE;
7487
7488         can_fall_any  = (can_fall_left || can_fall_right);
7489         can_fall_both = FALSE;
7490       }
7491
7492       if (can_fall_both)
7493       {
7494         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7495           can_fall_right = FALSE;       /* slip down on left side */
7496         else
7497           can_fall_left = !(can_fall_right = RND(2));
7498
7499         can_fall_both = FALSE;
7500       }
7501
7502       if (can_fall_any)
7503       {
7504         /* if not determined otherwise, prefer left side for slipping down */
7505         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7506         started_moving = TRUE;
7507       }
7508     }
7509     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7510     {
7511       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7512       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7513       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7514       int belt_dir = game.belt_dir[belt_nr];
7515
7516       if ((belt_dir == MV_LEFT  && left_is_free) ||
7517           (belt_dir == MV_RIGHT && right_is_free))
7518       {
7519         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7520
7521         InitMovingField(x, y, belt_dir);
7522         started_moving = TRUE;
7523
7524         Pushed[x][y] = TRUE;
7525         Pushed[nextx][y] = TRUE;
7526
7527         GfxAction[x][y] = ACTION_DEFAULT;
7528       }
7529       else
7530       {
7531         MovDir[x][y] = 0;       /* if element was moving, stop it */
7532       }
7533     }
7534   }
7535
7536   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7537   if (CAN_MOVE(element) && !started_moving)
7538   {
7539     int move_pattern = element_info[element].move_pattern;
7540     int newx, newy;
7541
7542     Moving2Blocked(x, y, &newx, &newy);
7543
7544     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7545       return;
7546
7547     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7548         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7549     {
7550       WasJustMoving[x][y] = 0;
7551       CheckCollision[x][y] = 0;
7552
7553       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7554
7555       if (Feld[x][y] != element)        /* element has changed */
7556         return;
7557     }
7558
7559     if (!MovDelay[x][y])        /* start new movement phase */
7560     {
7561       /* all objects that can change their move direction after each step
7562          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7563
7564       if (element != EL_YAMYAM &&
7565           element != EL_DARK_YAMYAM &&
7566           element != EL_PACMAN &&
7567           !(move_pattern & MV_ANY_DIRECTION) &&
7568           move_pattern != MV_TURNING_LEFT &&
7569           move_pattern != MV_TURNING_RIGHT &&
7570           move_pattern != MV_TURNING_LEFT_RIGHT &&
7571           move_pattern != MV_TURNING_RIGHT_LEFT &&
7572           move_pattern != MV_TURNING_RANDOM)
7573       {
7574         TurnRound(x, y);
7575
7576         if (MovDelay[x][y] && (element == EL_BUG ||
7577                                element == EL_SPACESHIP ||
7578                                element == EL_SP_SNIKSNAK ||
7579                                element == EL_SP_ELECTRON ||
7580                                element == EL_MOLE))
7581           TEST_DrawLevelField(x, y);
7582       }
7583     }
7584
7585     if (MovDelay[x][y])         /* wait some time before next movement */
7586     {
7587       MovDelay[x][y]--;
7588
7589       if (element == EL_ROBOT ||
7590           element == EL_YAMYAM ||
7591           element == EL_DARK_YAMYAM)
7592       {
7593         DrawLevelElementAnimationIfNeeded(x, y, element);
7594         PlayLevelSoundAction(x, y, ACTION_WAITING);
7595       }
7596       else if (element == EL_SP_ELECTRON)
7597         DrawLevelElementAnimationIfNeeded(x, y, element);
7598       else if (element == EL_DRAGON)
7599       {
7600         int i;
7601         int dir = MovDir[x][y];
7602         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7603         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7604         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7605                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7606                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7607                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7608         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7609
7610         GfxAction[x][y] = ACTION_ATTACKING;
7611
7612         if (IS_PLAYER(x, y))
7613           DrawPlayerField(x, y);
7614         else
7615           TEST_DrawLevelField(x, y);
7616
7617         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7618
7619         for (i = 1; i <= 3; i++)
7620         {
7621           int xx = x + i * dx;
7622           int yy = y + i * dy;
7623           int sx = SCREENX(xx);
7624           int sy = SCREENY(yy);
7625           int flame_graphic = graphic + (i - 1);
7626
7627           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7628             break;
7629
7630           if (MovDelay[x][y])
7631           {
7632             int flamed = MovingOrBlocked2Element(xx, yy);
7633
7634             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7635               Bang(xx, yy);
7636             else
7637               RemoveMovingField(xx, yy);
7638
7639             ChangeDelay[xx][yy] = 0;
7640
7641             Feld[xx][yy] = EL_FLAMES;
7642
7643             if (IN_SCR_FIELD(sx, sy))
7644             {
7645               TEST_DrawLevelFieldCrumbled(xx, yy);
7646               DrawGraphic(sx, sy, flame_graphic, frame);
7647             }
7648           }
7649           else
7650           {
7651             if (Feld[xx][yy] == EL_FLAMES)
7652               Feld[xx][yy] = EL_EMPTY;
7653             TEST_DrawLevelField(xx, yy);
7654           }
7655         }
7656       }
7657
7658       if (MovDelay[x][y])       /* element still has to wait some time */
7659       {
7660         PlayLevelSoundAction(x, y, ACTION_WAITING);
7661
7662         return;
7663       }
7664     }
7665
7666     /* now make next step */
7667
7668     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7669
7670     if (DONT_COLLIDE_WITH(element) &&
7671         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7672         !PLAYER_ENEMY_PROTECTED(newx, newy))
7673     {
7674       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7675
7676       return;
7677     }
7678
7679     else if (CAN_MOVE_INTO_ACID(element) &&
7680              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7681              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7682              (MovDir[x][y] == MV_DOWN ||
7683               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7684     {
7685       SplashAcid(newx, newy);
7686       Store[x][y] = EL_ACID;
7687     }
7688     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7689     {
7690       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7691           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7692           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7693           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7694       {
7695         RemoveField(x, y);
7696         TEST_DrawLevelField(x, y);
7697
7698         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7699         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7700           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7701
7702         local_player->friends_still_needed--;
7703         if (!local_player->friends_still_needed &&
7704             !local_player->GameOver && AllPlayersGone)
7705           PlayerWins(local_player);
7706
7707         return;
7708       }
7709       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7710       {
7711         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7712           TEST_DrawLevelField(newx, newy);
7713         else
7714           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7715       }
7716       else if (!IS_FREE(newx, newy))
7717       {
7718         GfxAction[x][y] = ACTION_WAITING;
7719
7720         if (IS_PLAYER(x, y))
7721           DrawPlayerField(x, y);
7722         else
7723           TEST_DrawLevelField(x, y);
7724
7725         return;
7726       }
7727     }
7728     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7729     {
7730       if (IS_FOOD_PIG(Feld[newx][newy]))
7731       {
7732         if (IS_MOVING(newx, newy))
7733           RemoveMovingField(newx, newy);
7734         else
7735         {
7736           Feld[newx][newy] = EL_EMPTY;
7737           TEST_DrawLevelField(newx, newy);
7738         }
7739
7740         PlayLevelSound(x, y, SND_PIG_DIGGING);
7741       }
7742       else if (!IS_FREE(newx, newy))
7743       {
7744         if (IS_PLAYER(x, y))
7745           DrawPlayerField(x, y);
7746         else
7747           TEST_DrawLevelField(x, y);
7748
7749         return;
7750       }
7751     }
7752     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7753     {
7754       if (Store[x][y] != EL_EMPTY)
7755       {
7756         boolean can_clone = FALSE;
7757         int xx, yy;
7758
7759         /* check if element to clone is still there */
7760         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7761         {
7762           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7763           {
7764             can_clone = TRUE;
7765
7766             break;
7767           }
7768         }
7769
7770         /* cannot clone or target field not free anymore -- do not clone */
7771         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7772           Store[x][y] = EL_EMPTY;
7773       }
7774
7775       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7776       {
7777         if (IS_MV_DIAGONAL(MovDir[x][y]))
7778         {
7779           int diagonal_move_dir = MovDir[x][y];
7780           int stored = Store[x][y];
7781           int change_delay = 8;
7782           int graphic;
7783
7784           /* android is moving diagonally */
7785
7786           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7787
7788           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7789           GfxElement[x][y] = EL_EMC_ANDROID;
7790           GfxAction[x][y] = ACTION_SHRINKING;
7791           GfxDir[x][y] = diagonal_move_dir;
7792           ChangeDelay[x][y] = change_delay;
7793
7794           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7795                                    GfxDir[x][y]);
7796
7797           DrawLevelGraphicAnimation(x, y, graphic);
7798           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7799
7800           if (Feld[newx][newy] == EL_ACID)
7801           {
7802             SplashAcid(newx, newy);
7803
7804             return;
7805           }
7806
7807           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7808
7809           Store[newx][newy] = EL_EMC_ANDROID;
7810           GfxElement[newx][newy] = EL_EMC_ANDROID;
7811           GfxAction[newx][newy] = ACTION_GROWING;
7812           GfxDir[newx][newy] = diagonal_move_dir;
7813           ChangeDelay[newx][newy] = change_delay;
7814
7815           graphic = el_act_dir2img(GfxElement[newx][newy],
7816                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7817
7818           DrawLevelGraphicAnimation(newx, newy, graphic);
7819           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7820
7821           return;
7822         }
7823         else
7824         {
7825           Feld[newx][newy] = EL_EMPTY;
7826           TEST_DrawLevelField(newx, newy);
7827
7828           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7829         }
7830       }
7831       else if (!IS_FREE(newx, newy))
7832       {
7833         return;
7834       }
7835     }
7836     else if (IS_CUSTOM_ELEMENT(element) &&
7837              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7838     {
7839       if (!DigFieldByCE(newx, newy, element))
7840         return;
7841
7842       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7843       {
7844         RunnerVisit[x][y] = FrameCounter;
7845         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7846       }
7847     }
7848     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7849     {
7850       if (!IS_FREE(newx, newy))
7851       {
7852         if (IS_PLAYER(x, y))
7853           DrawPlayerField(x, y);
7854         else
7855           TEST_DrawLevelField(x, y);
7856
7857         return;
7858       }
7859       else
7860       {
7861         boolean wanna_flame = !RND(10);
7862         int dx = newx - x, dy = newy - y;
7863         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7864         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7865         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7866                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7867         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7868                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7869
7870         if ((wanna_flame ||
7871              IS_CLASSIC_ENEMY(element1) ||
7872              IS_CLASSIC_ENEMY(element2)) &&
7873             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7874             element1 != EL_FLAMES && element2 != EL_FLAMES)
7875         {
7876           ResetGfxAnimation(x, y);
7877           GfxAction[x][y] = ACTION_ATTACKING;
7878
7879           if (IS_PLAYER(x, y))
7880             DrawPlayerField(x, y);
7881           else
7882             TEST_DrawLevelField(x, y);
7883
7884           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7885
7886           MovDelay[x][y] = 50;
7887
7888           Feld[newx][newy] = EL_FLAMES;
7889           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7890             Feld[newx1][newy1] = EL_FLAMES;
7891           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7892             Feld[newx2][newy2] = EL_FLAMES;
7893
7894           return;
7895         }
7896       }
7897     }
7898     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7899              Feld[newx][newy] == EL_DIAMOND)
7900     {
7901       if (IS_MOVING(newx, newy))
7902         RemoveMovingField(newx, newy);
7903       else
7904       {
7905         Feld[newx][newy] = EL_EMPTY;
7906         TEST_DrawLevelField(newx, newy);
7907       }
7908
7909       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7910     }
7911     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7912              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7913     {
7914       if (AmoebaNr[newx][newy])
7915       {
7916         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7917         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7918             Feld[newx][newy] == EL_BD_AMOEBA)
7919           AmoebaCnt[AmoebaNr[newx][newy]]--;
7920       }
7921
7922       if (IS_MOVING(newx, newy))
7923       {
7924         RemoveMovingField(newx, newy);
7925       }
7926       else
7927       {
7928         Feld[newx][newy] = EL_EMPTY;
7929         TEST_DrawLevelField(newx, newy);
7930       }
7931
7932       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7933     }
7934     else if ((element == EL_PACMAN || element == EL_MOLE)
7935              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7936     {
7937       if (AmoebaNr[newx][newy])
7938       {
7939         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7940         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7941             Feld[newx][newy] == EL_BD_AMOEBA)
7942           AmoebaCnt[AmoebaNr[newx][newy]]--;
7943       }
7944
7945       if (element == EL_MOLE)
7946       {
7947         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7948         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7949
7950         ResetGfxAnimation(x, y);
7951         GfxAction[x][y] = ACTION_DIGGING;
7952         TEST_DrawLevelField(x, y);
7953
7954         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7955
7956         return;                         /* wait for shrinking amoeba */
7957       }
7958       else      /* element == EL_PACMAN */
7959       {
7960         Feld[newx][newy] = EL_EMPTY;
7961         TEST_DrawLevelField(newx, newy);
7962         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7963       }
7964     }
7965     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7966              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7967               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7968     {
7969       /* wait for shrinking amoeba to completely disappear */
7970       return;
7971     }
7972     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7973     {
7974       /* object was running against a wall */
7975
7976       TurnRound(x, y);
7977
7978       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7979         DrawLevelElementAnimation(x, y, element);
7980
7981       if (DONT_TOUCH(element))
7982         TestIfBadThingTouchesPlayer(x, y);
7983
7984       return;
7985     }
7986
7987     InitMovingField(x, y, MovDir[x][y]);
7988
7989     PlayLevelSoundAction(x, y, ACTION_MOVING);
7990   }
7991
7992   if (MovDir[x][y])
7993     ContinueMoving(x, y);
7994 }
7995
7996 void ContinueMoving(int x, int y)
7997 {
7998   int element = Feld[x][y];
7999   struct ElementInfo *ei = &element_info[element];
8000   int direction = MovDir[x][y];
8001   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8002   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8003   int newx = x + dx, newy = y + dy;
8004   int stored = Store[x][y];
8005   int stored_new = Store[newx][newy];
8006   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8007   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8008   boolean last_line = (newy == lev_fieldy - 1);
8009
8010   MovPos[x][y] += getElementMoveStepsize(x, y);
8011
8012   if (pushed_by_player) /* special case: moving object pushed by player */
8013     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8014
8015   if (ABS(MovPos[x][y]) < TILEX)
8016   {
8017     TEST_DrawLevelField(x, y);
8018
8019     return;     /* element is still moving */
8020   }
8021
8022   /* element reached destination field */
8023
8024   Feld[x][y] = EL_EMPTY;
8025   Feld[newx][newy] = element;
8026   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8027
8028   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8029   {
8030     element = Feld[newx][newy] = EL_ACID;
8031   }
8032   else if (element == EL_MOLE)
8033   {
8034     Feld[x][y] = EL_SAND;
8035
8036     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8037   }
8038   else if (element == EL_QUICKSAND_FILLING)
8039   {
8040     element = Feld[newx][newy] = get_next_element(element);
8041     Store[newx][newy] = Store[x][y];
8042   }
8043   else if (element == EL_QUICKSAND_EMPTYING)
8044   {
8045     Feld[x][y] = get_next_element(element);
8046     element = Feld[newx][newy] = Store[x][y];
8047   }
8048   else if (element == EL_QUICKSAND_FAST_FILLING)
8049   {
8050     element = Feld[newx][newy] = get_next_element(element);
8051     Store[newx][newy] = Store[x][y];
8052   }
8053   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8054   {
8055     Feld[x][y] = get_next_element(element);
8056     element = Feld[newx][newy] = Store[x][y];
8057   }
8058   else if (element == EL_MAGIC_WALL_FILLING)
8059   {
8060     element = Feld[newx][newy] = get_next_element(element);
8061     if (!game.magic_wall_active)
8062       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8063     Store[newx][newy] = Store[x][y];
8064   }
8065   else if (element == EL_MAGIC_WALL_EMPTYING)
8066   {
8067     Feld[x][y] = get_next_element(element);
8068     if (!game.magic_wall_active)
8069       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8070     element = Feld[newx][newy] = Store[x][y];
8071
8072     InitField(newx, newy, FALSE);
8073   }
8074   else if (element == EL_BD_MAGIC_WALL_FILLING)
8075   {
8076     element = Feld[newx][newy] = get_next_element(element);
8077     if (!game.magic_wall_active)
8078       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8079     Store[newx][newy] = Store[x][y];
8080   }
8081   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8082   {
8083     Feld[x][y] = get_next_element(element);
8084     if (!game.magic_wall_active)
8085       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8086     element = Feld[newx][newy] = Store[x][y];
8087
8088     InitField(newx, newy, FALSE);
8089   }
8090   else if (element == EL_DC_MAGIC_WALL_FILLING)
8091   {
8092     element = Feld[newx][newy] = get_next_element(element);
8093     if (!game.magic_wall_active)
8094       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8095     Store[newx][newy] = Store[x][y];
8096   }
8097   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8098   {
8099     Feld[x][y] = get_next_element(element);
8100     if (!game.magic_wall_active)
8101       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8102     element = Feld[newx][newy] = Store[x][y];
8103
8104     InitField(newx, newy, FALSE);
8105   }
8106   else if (element == EL_AMOEBA_DROPPING)
8107   {
8108     Feld[x][y] = get_next_element(element);
8109     element = Feld[newx][newy] = Store[x][y];
8110   }
8111   else if (element == EL_SOKOBAN_OBJECT)
8112   {
8113     if (Back[x][y])
8114       Feld[x][y] = Back[x][y];
8115
8116     if (Back[newx][newy])
8117       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8118
8119     Back[x][y] = Back[newx][newy] = 0;
8120   }
8121
8122   Store[x][y] = EL_EMPTY;
8123   MovPos[x][y] = 0;
8124   MovDir[x][y] = 0;
8125   MovDelay[x][y] = 0;
8126
8127   MovDelay[newx][newy] = 0;
8128
8129   if (CAN_CHANGE_OR_HAS_ACTION(element))
8130   {
8131     /* copy element change control values to new field */
8132     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8133     ChangePage[newx][newy]  = ChangePage[x][y];
8134     ChangeCount[newx][newy] = ChangeCount[x][y];
8135     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8136   }
8137
8138   CustomValue[newx][newy] = CustomValue[x][y];
8139
8140   ChangeDelay[x][y] = 0;
8141   ChangePage[x][y] = -1;
8142   ChangeCount[x][y] = 0;
8143   ChangeEvent[x][y] = -1;
8144
8145   CustomValue[x][y] = 0;
8146
8147   /* copy animation control values to new field */
8148   GfxFrame[newx][newy]  = GfxFrame[x][y];
8149   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8150   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8151   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8152
8153   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8154
8155   /* some elements can leave other elements behind after moving */
8156   if (ei->move_leave_element != EL_EMPTY &&
8157       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8158       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8159   {
8160     int move_leave_element = ei->move_leave_element;
8161
8162     /* this makes it possible to leave the removed element again */
8163     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8164       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8165
8166     Feld[x][y] = move_leave_element;
8167
8168     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8169       MovDir[x][y] = direction;
8170
8171     InitField(x, y, FALSE);
8172
8173     if (GFX_CRUMBLED(Feld[x][y]))
8174       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8175
8176     if (ELEM_IS_PLAYER(move_leave_element))
8177       RelocatePlayer(x, y, move_leave_element);
8178   }
8179
8180   /* do this after checking for left-behind element */
8181   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8182
8183   if (!CAN_MOVE(element) ||
8184       (CAN_FALL(element) && direction == MV_DOWN &&
8185        (element == EL_SPRING ||
8186         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8187         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8188     GfxDir[x][y] = MovDir[newx][newy] = 0;
8189
8190   TEST_DrawLevelField(x, y);
8191   TEST_DrawLevelField(newx, newy);
8192
8193   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8194
8195   /* prevent pushed element from moving on in pushed direction */
8196   if (pushed_by_player && CAN_MOVE(element) &&
8197       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8198       !(element_info[element].move_pattern & direction))
8199     TurnRound(newx, newy);
8200
8201   /* prevent elements on conveyor belt from moving on in last direction */
8202   if (pushed_by_conveyor && CAN_FALL(element) &&
8203       direction & MV_HORIZONTAL)
8204     MovDir[newx][newy] = 0;
8205
8206   if (!pushed_by_player)
8207   {
8208     int nextx = newx + dx, nexty = newy + dy;
8209     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8210
8211     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8212
8213     if (CAN_FALL(element) && direction == MV_DOWN)
8214       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8215
8216     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8217       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8218
8219     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8220       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8221   }
8222
8223   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8224   {
8225     TestIfBadThingTouchesPlayer(newx, newy);
8226     TestIfBadThingTouchesFriend(newx, newy);
8227
8228     if (!IS_CUSTOM_ELEMENT(element))
8229       TestIfBadThingTouchesOtherBadThing(newx, newy);
8230   }
8231   else if (element == EL_PENGUIN)
8232     TestIfFriendTouchesBadThing(newx, newy);
8233
8234   if (DONT_GET_HIT_BY(element))
8235   {
8236     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8237   }
8238
8239   /* give the player one last chance (one more frame) to move away */
8240   if (CAN_FALL(element) && direction == MV_DOWN &&
8241       (last_line || (!IS_FREE(x, newy + 1) &&
8242                      (!IS_PLAYER(x, newy + 1) ||
8243                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8244     Impact(x, newy);
8245
8246   if (pushed_by_player && !game.use_change_when_pushing_bug)
8247   {
8248     int push_side = MV_DIR_OPPOSITE(direction);
8249     struct PlayerInfo *player = PLAYERINFO(x, y);
8250
8251     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8252                                player->index_bit, push_side);
8253     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8254                                         player->index_bit, push_side);
8255   }
8256
8257   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8258     MovDelay[newx][newy] = 1;
8259
8260   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8261
8262   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8263   TestIfElementHitsCustomElement(newx, newy, direction);
8264   TestIfPlayerTouchesCustomElement(newx, newy);
8265   TestIfElementTouchesCustomElement(newx, newy);
8266
8267   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8268       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8269     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8270                              MV_DIR_OPPOSITE(direction));
8271 }
8272
8273 int AmoebeNachbarNr(int ax, int ay)
8274 {
8275   int i;
8276   int element = Feld[ax][ay];
8277   int group_nr = 0;
8278   static int xy[4][2] =
8279   {
8280     { 0, -1 },
8281     { -1, 0 },
8282     { +1, 0 },
8283     { 0, +1 }
8284   };
8285
8286   for (i = 0; i < NUM_DIRECTIONS; i++)
8287   {
8288     int x = ax + xy[i][0];
8289     int y = ay + xy[i][1];
8290
8291     if (!IN_LEV_FIELD(x, y))
8292       continue;
8293
8294     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8295       group_nr = AmoebaNr[x][y];
8296   }
8297
8298   return group_nr;
8299 }
8300
8301 void AmoebenVereinigen(int ax, int ay)
8302 {
8303   int i, x, y, xx, yy;
8304   int new_group_nr = AmoebaNr[ax][ay];
8305   static int xy[4][2] =
8306   {
8307     { 0, -1 },
8308     { -1, 0 },
8309     { +1, 0 },
8310     { 0, +1 }
8311   };
8312
8313   if (new_group_nr == 0)
8314     return;
8315
8316   for (i = 0; i < NUM_DIRECTIONS; i++)
8317   {
8318     x = ax + xy[i][0];
8319     y = ay + xy[i][1];
8320
8321     if (!IN_LEV_FIELD(x, y))
8322       continue;
8323
8324     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8325          Feld[x][y] == EL_BD_AMOEBA ||
8326          Feld[x][y] == EL_AMOEBA_DEAD) &&
8327         AmoebaNr[x][y] != new_group_nr)
8328     {
8329       int old_group_nr = AmoebaNr[x][y];
8330
8331       if (old_group_nr == 0)
8332         return;
8333
8334       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8335       AmoebaCnt[old_group_nr] = 0;
8336       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8337       AmoebaCnt2[old_group_nr] = 0;
8338
8339       SCAN_PLAYFIELD(xx, yy)
8340       {
8341         if (AmoebaNr[xx][yy] == old_group_nr)
8342           AmoebaNr[xx][yy] = new_group_nr;
8343       }
8344     }
8345   }
8346 }
8347
8348 void AmoebeUmwandeln(int ax, int ay)
8349 {
8350   int i, x, y;
8351
8352   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8353   {
8354     int group_nr = AmoebaNr[ax][ay];
8355
8356 #ifdef DEBUG
8357     if (group_nr == 0)
8358     {
8359       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8360       printf("AmoebeUmwandeln(): This should never happen!\n");
8361       return;
8362     }
8363 #endif
8364
8365     SCAN_PLAYFIELD(x, y)
8366     {
8367       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8368       {
8369         AmoebaNr[x][y] = 0;
8370         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8371       }
8372     }
8373
8374     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8375                             SND_AMOEBA_TURNING_TO_GEM :
8376                             SND_AMOEBA_TURNING_TO_ROCK));
8377     Bang(ax, ay);
8378   }
8379   else
8380   {
8381     static int xy[4][2] =
8382     {
8383       { 0, -1 },
8384       { -1, 0 },
8385       { +1, 0 },
8386       { 0, +1 }
8387     };
8388
8389     for (i = 0; i < NUM_DIRECTIONS; i++)
8390     {
8391       x = ax + xy[i][0];
8392       y = ay + xy[i][1];
8393
8394       if (!IN_LEV_FIELD(x, y))
8395         continue;
8396
8397       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8398       {
8399         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8400                               SND_AMOEBA_TURNING_TO_GEM :
8401                               SND_AMOEBA_TURNING_TO_ROCK));
8402         Bang(x, y);
8403       }
8404     }
8405   }
8406 }
8407
8408 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8409 {
8410   int x, y;
8411   int group_nr = AmoebaNr[ax][ay];
8412   boolean done = FALSE;
8413
8414 #ifdef DEBUG
8415   if (group_nr == 0)
8416   {
8417     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8418     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8419     return;
8420   }
8421 #endif
8422
8423   SCAN_PLAYFIELD(x, y)
8424   {
8425     if (AmoebaNr[x][y] == group_nr &&
8426         (Feld[x][y] == EL_AMOEBA_DEAD ||
8427          Feld[x][y] == EL_BD_AMOEBA ||
8428          Feld[x][y] == EL_AMOEBA_GROWING))
8429     {
8430       AmoebaNr[x][y] = 0;
8431       Feld[x][y] = new_element;
8432       InitField(x, y, FALSE);
8433       TEST_DrawLevelField(x, y);
8434       done = TRUE;
8435     }
8436   }
8437
8438   if (done)
8439     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8440                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8441                             SND_BD_AMOEBA_TURNING_TO_GEM));
8442 }
8443
8444 void AmoebeWaechst(int x, int y)
8445 {
8446   static unsigned int sound_delay = 0;
8447   static unsigned int sound_delay_value = 0;
8448
8449   if (!MovDelay[x][y])          /* start new growing cycle */
8450   {
8451     MovDelay[x][y] = 7;
8452
8453     if (DelayReached(&sound_delay, sound_delay_value))
8454     {
8455       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8456       sound_delay_value = 30;
8457     }
8458   }
8459
8460   if (MovDelay[x][y])           /* wait some time before growing bigger */
8461   {
8462     MovDelay[x][y]--;
8463     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8464     {
8465       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8466                                            6 - MovDelay[x][y]);
8467
8468       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8469     }
8470
8471     if (!MovDelay[x][y])
8472     {
8473       Feld[x][y] = Store[x][y];
8474       Store[x][y] = 0;
8475       TEST_DrawLevelField(x, y);
8476     }
8477   }
8478 }
8479
8480 void AmoebaDisappearing(int x, int y)
8481 {
8482   static unsigned int sound_delay = 0;
8483   static unsigned int sound_delay_value = 0;
8484
8485   if (!MovDelay[x][y])          /* start new shrinking cycle */
8486   {
8487     MovDelay[x][y] = 7;
8488
8489     if (DelayReached(&sound_delay, sound_delay_value))
8490       sound_delay_value = 30;
8491   }
8492
8493   if (MovDelay[x][y])           /* wait some time before shrinking */
8494   {
8495     MovDelay[x][y]--;
8496     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8497     {
8498       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8499                                            6 - MovDelay[x][y]);
8500
8501       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8502     }
8503
8504     if (!MovDelay[x][y])
8505     {
8506       Feld[x][y] = EL_EMPTY;
8507       TEST_DrawLevelField(x, y);
8508
8509       /* don't let mole enter this field in this cycle;
8510          (give priority to objects falling to this field from above) */
8511       Stop[x][y] = TRUE;
8512     }
8513   }
8514 }
8515
8516 void AmoebeAbleger(int ax, int ay)
8517 {
8518   int i;
8519   int element = Feld[ax][ay];
8520   int graphic = el2img(element);
8521   int newax = ax, neway = ay;
8522   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8523   static int xy[4][2] =
8524   {
8525     { 0, -1 },
8526     { -1, 0 },
8527     { +1, 0 },
8528     { 0, +1 }
8529   };
8530
8531   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8532   {
8533     Feld[ax][ay] = EL_AMOEBA_DEAD;
8534     TEST_DrawLevelField(ax, ay);
8535     return;
8536   }
8537
8538   if (IS_ANIMATED(graphic))
8539     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8540
8541   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8542     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8543
8544   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8545   {
8546     MovDelay[ax][ay]--;
8547     if (MovDelay[ax][ay])
8548       return;
8549   }
8550
8551   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8552   {
8553     int start = RND(4);
8554     int x = ax + xy[start][0];
8555     int y = ay + xy[start][1];
8556
8557     if (!IN_LEV_FIELD(x, y))
8558       return;
8559
8560     if (IS_FREE(x, y) ||
8561         CAN_GROW_INTO(Feld[x][y]) ||
8562         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8563         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8564     {
8565       newax = x;
8566       neway = y;
8567     }
8568
8569     if (newax == ax && neway == ay)
8570       return;
8571   }
8572   else                          /* normal or "filled" (BD style) amoeba */
8573   {
8574     int start = RND(4);
8575     boolean waiting_for_player = FALSE;
8576
8577     for (i = 0; i < NUM_DIRECTIONS; i++)
8578     {
8579       int j = (start + i) % 4;
8580       int x = ax + xy[j][0];
8581       int y = ay + xy[j][1];
8582
8583       if (!IN_LEV_FIELD(x, y))
8584         continue;
8585
8586       if (IS_FREE(x, y) ||
8587           CAN_GROW_INTO(Feld[x][y]) ||
8588           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8589           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8590       {
8591         newax = x;
8592         neway = y;
8593         break;
8594       }
8595       else if (IS_PLAYER(x, y))
8596         waiting_for_player = TRUE;
8597     }
8598
8599     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8600     {
8601       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8602       {
8603         Feld[ax][ay] = EL_AMOEBA_DEAD;
8604         TEST_DrawLevelField(ax, ay);
8605         AmoebaCnt[AmoebaNr[ax][ay]]--;
8606
8607         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8608         {
8609           if (element == EL_AMOEBA_FULL)
8610             AmoebeUmwandeln(ax, ay);
8611           else if (element == EL_BD_AMOEBA)
8612             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8613         }
8614       }
8615       return;
8616     }
8617     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8618     {
8619       /* amoeba gets larger by growing in some direction */
8620
8621       int new_group_nr = AmoebaNr[ax][ay];
8622
8623 #ifdef DEBUG
8624   if (new_group_nr == 0)
8625   {
8626     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8627     printf("AmoebeAbleger(): This should never happen!\n");
8628     return;
8629   }
8630 #endif
8631
8632       AmoebaNr[newax][neway] = new_group_nr;
8633       AmoebaCnt[new_group_nr]++;
8634       AmoebaCnt2[new_group_nr]++;
8635
8636       /* if amoeba touches other amoeba(s) after growing, unify them */
8637       AmoebenVereinigen(newax, neway);
8638
8639       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8640       {
8641         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8642         return;
8643       }
8644     }
8645   }
8646
8647   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8648       (neway == lev_fieldy - 1 && newax != ax))
8649   {
8650     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8651     Store[newax][neway] = element;
8652   }
8653   else if (neway == ay || element == EL_EMC_DRIPPER)
8654   {
8655     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8656
8657     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8658   }
8659   else
8660   {
8661     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8662     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8663     Store[ax][ay] = EL_AMOEBA_DROP;
8664     ContinueMoving(ax, ay);
8665     return;
8666   }
8667
8668   TEST_DrawLevelField(newax, neway);
8669 }
8670
8671 void Life(int ax, int ay)
8672 {
8673   int x1, y1, x2, y2;
8674   int life_time = 40;
8675   int element = Feld[ax][ay];
8676   int graphic = el2img(element);
8677   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8678                          level.biomaze);
8679   boolean changed = FALSE;
8680
8681   if (IS_ANIMATED(graphic))
8682     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8683
8684   if (Stop[ax][ay])
8685     return;
8686
8687   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8688     MovDelay[ax][ay] = life_time;
8689
8690   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8691   {
8692     MovDelay[ax][ay]--;
8693     if (MovDelay[ax][ay])
8694       return;
8695   }
8696
8697   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8698   {
8699     int xx = ax+x1, yy = ay+y1;
8700     int nachbarn = 0;
8701
8702     if (!IN_LEV_FIELD(xx, yy))
8703       continue;
8704
8705     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8706     {
8707       int x = xx+x2, y = yy+y2;
8708
8709       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8710         continue;
8711
8712       if (((Feld[x][y] == element ||
8713             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8714            !Stop[x][y]) ||
8715           (IS_FREE(x, y) && Stop[x][y]))
8716         nachbarn++;
8717     }
8718
8719     if (xx == ax && yy == ay)           /* field in the middle */
8720     {
8721       if (nachbarn < life_parameter[0] ||
8722           nachbarn > life_parameter[1])
8723       {
8724         Feld[xx][yy] = EL_EMPTY;
8725         if (!Stop[xx][yy])
8726           TEST_DrawLevelField(xx, yy);
8727         Stop[xx][yy] = TRUE;
8728         changed = TRUE;
8729       }
8730     }
8731     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8732     {                                   /* free border field */
8733       if (nachbarn >= life_parameter[2] &&
8734           nachbarn <= life_parameter[3])
8735       {
8736         Feld[xx][yy] = element;
8737         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8738         if (!Stop[xx][yy])
8739           TEST_DrawLevelField(xx, yy);
8740         Stop[xx][yy] = TRUE;
8741         changed = TRUE;
8742       }
8743     }
8744   }
8745
8746   if (changed)
8747     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8748                    SND_GAME_OF_LIFE_GROWING);
8749 }
8750
8751 static void InitRobotWheel(int x, int y)
8752 {
8753   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8754 }
8755
8756 static void RunRobotWheel(int x, int y)
8757 {
8758   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8759 }
8760
8761 static void StopRobotWheel(int x, int y)
8762 {
8763   if (ZX == x && ZY == y)
8764   {
8765     ZX = ZY = -1;
8766
8767     game.robot_wheel_active = FALSE;
8768   }
8769 }
8770
8771 static void InitTimegateWheel(int x, int y)
8772 {
8773   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8774 }
8775
8776 static void RunTimegateWheel(int x, int y)
8777 {
8778   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8779 }
8780
8781 static void InitMagicBallDelay(int x, int y)
8782 {
8783   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8784 }
8785
8786 static void ActivateMagicBall(int bx, int by)
8787 {
8788   int x, y;
8789
8790   if (level.ball_random)
8791   {
8792     int pos_border = RND(8);    /* select one of the eight border elements */
8793     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8794     int xx = pos_content % 3;
8795     int yy = pos_content / 3;
8796
8797     x = bx - 1 + xx;
8798     y = by - 1 + yy;
8799
8800     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8801       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8802   }
8803   else
8804   {
8805     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8806     {
8807       int xx = x - bx + 1;
8808       int yy = y - by + 1;
8809
8810       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8811         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8812     }
8813   }
8814
8815   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8816 }
8817
8818 void CheckExit(int x, int y)
8819 {
8820   if (local_player->gems_still_needed > 0 ||
8821       local_player->sokobanfields_still_needed > 0 ||
8822       local_player->lights_still_needed > 0)
8823   {
8824     int element = Feld[x][y];
8825     int graphic = el2img(element);
8826
8827     if (IS_ANIMATED(graphic))
8828       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8829
8830     return;
8831   }
8832
8833   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8834     return;
8835
8836   Feld[x][y] = EL_EXIT_OPENING;
8837
8838   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8839 }
8840
8841 void CheckExitEM(int x, int y)
8842 {
8843   if (local_player->gems_still_needed > 0 ||
8844       local_player->sokobanfields_still_needed > 0 ||
8845       local_player->lights_still_needed > 0)
8846   {
8847     int element = Feld[x][y];
8848     int graphic = el2img(element);
8849
8850     if (IS_ANIMATED(graphic))
8851       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8852
8853     return;
8854   }
8855
8856   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8857     return;
8858
8859   Feld[x][y] = EL_EM_EXIT_OPENING;
8860
8861   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8862 }
8863
8864 void CheckExitSteel(int x, int y)
8865 {
8866   if (local_player->gems_still_needed > 0 ||
8867       local_player->sokobanfields_still_needed > 0 ||
8868       local_player->lights_still_needed > 0)
8869   {
8870     int element = Feld[x][y];
8871     int graphic = el2img(element);
8872
8873     if (IS_ANIMATED(graphic))
8874       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8875
8876     return;
8877   }
8878
8879   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8880     return;
8881
8882   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8883
8884   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8885 }
8886
8887 void CheckExitSteelEM(int x, int y)
8888 {
8889   if (local_player->gems_still_needed > 0 ||
8890       local_player->sokobanfields_still_needed > 0 ||
8891       local_player->lights_still_needed > 0)
8892   {
8893     int element = Feld[x][y];
8894     int graphic = el2img(element);
8895
8896     if (IS_ANIMATED(graphic))
8897       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8898
8899     return;
8900   }
8901
8902   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8903     return;
8904
8905   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8906
8907   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8908 }
8909
8910 void CheckExitSP(int x, int y)
8911 {
8912   if (local_player->gems_still_needed > 0)
8913   {
8914     int element = Feld[x][y];
8915     int graphic = el2img(element);
8916
8917     if (IS_ANIMATED(graphic))
8918       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8919
8920     return;
8921   }
8922
8923   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8924     return;
8925
8926   Feld[x][y] = EL_SP_EXIT_OPENING;
8927
8928   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8929 }
8930
8931 static void CloseAllOpenTimegates()
8932 {
8933   int x, y;
8934
8935   SCAN_PLAYFIELD(x, y)
8936   {
8937     int element = Feld[x][y];
8938
8939     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8940     {
8941       Feld[x][y] = EL_TIMEGATE_CLOSING;
8942
8943       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8944     }
8945   }
8946 }
8947
8948 void DrawTwinkleOnField(int x, int y)
8949 {
8950   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8951     return;
8952
8953   if (Feld[x][y] == EL_BD_DIAMOND)
8954     return;
8955
8956   if (MovDelay[x][y] == 0)      /* next animation frame */
8957     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8958
8959   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8960   {
8961     MovDelay[x][y]--;
8962
8963     DrawLevelElementAnimation(x, y, Feld[x][y]);
8964
8965     if (MovDelay[x][y] != 0)
8966     {
8967       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8968                                            10 - MovDelay[x][y]);
8969
8970       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8971     }
8972   }
8973 }
8974
8975 void MauerWaechst(int x, int y)
8976 {
8977   int delay = 6;
8978
8979   if (!MovDelay[x][y])          /* next animation frame */
8980     MovDelay[x][y] = 3 * delay;
8981
8982   if (MovDelay[x][y])           /* wait some time before next frame */
8983   {
8984     MovDelay[x][y]--;
8985
8986     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8987     {
8988       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8989       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8990
8991       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8992     }
8993
8994     if (!MovDelay[x][y])
8995     {
8996       if (MovDir[x][y] == MV_LEFT)
8997       {
8998         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8999           TEST_DrawLevelField(x - 1, y);
9000       }
9001       else if (MovDir[x][y] == MV_RIGHT)
9002       {
9003         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9004           TEST_DrawLevelField(x + 1, y);
9005       }
9006       else if (MovDir[x][y] == MV_UP)
9007       {
9008         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9009           TEST_DrawLevelField(x, y - 1);
9010       }
9011       else
9012       {
9013         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9014           TEST_DrawLevelField(x, y + 1);
9015       }
9016
9017       Feld[x][y] = Store[x][y];
9018       Store[x][y] = 0;
9019       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9020       TEST_DrawLevelField(x, y);
9021     }
9022   }
9023 }
9024
9025 void MauerAbleger(int ax, int ay)
9026 {
9027   int element = Feld[ax][ay];
9028   int graphic = el2img(element);
9029   boolean oben_frei = FALSE, unten_frei = FALSE;
9030   boolean links_frei = FALSE, rechts_frei = FALSE;
9031   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9032   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9033   boolean new_wall = FALSE;
9034
9035   if (IS_ANIMATED(graphic))
9036     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9037
9038   if (!MovDelay[ax][ay])        /* start building new wall */
9039     MovDelay[ax][ay] = 6;
9040
9041   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9042   {
9043     MovDelay[ax][ay]--;
9044     if (MovDelay[ax][ay])
9045       return;
9046   }
9047
9048   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9049     oben_frei = TRUE;
9050   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9051     unten_frei = TRUE;
9052   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9053     links_frei = TRUE;
9054   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9055     rechts_frei = TRUE;
9056
9057   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9058       element == EL_EXPANDABLE_WALL_ANY)
9059   {
9060     if (oben_frei)
9061     {
9062       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9063       Store[ax][ay-1] = element;
9064       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9065       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9066         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9067                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9068       new_wall = TRUE;
9069     }
9070     if (unten_frei)
9071     {
9072       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9073       Store[ax][ay+1] = element;
9074       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9075       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9076         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9077                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9078       new_wall = TRUE;
9079     }
9080   }
9081
9082   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9083       element == EL_EXPANDABLE_WALL_ANY ||
9084       element == EL_EXPANDABLE_WALL ||
9085       element == EL_BD_EXPANDABLE_WALL)
9086   {
9087     if (links_frei)
9088     {
9089       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9090       Store[ax-1][ay] = element;
9091       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9092       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9093         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9094                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9095       new_wall = TRUE;
9096     }
9097
9098     if (rechts_frei)
9099     {
9100       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9101       Store[ax+1][ay] = element;
9102       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9103       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9104         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9105                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9106       new_wall = TRUE;
9107     }
9108   }
9109
9110   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9111     TEST_DrawLevelField(ax, ay);
9112
9113   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9114     oben_massiv = TRUE;
9115   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9116     unten_massiv = TRUE;
9117   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9118     links_massiv = TRUE;
9119   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9120     rechts_massiv = TRUE;
9121
9122   if (((oben_massiv && unten_massiv) ||
9123        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9124        element == EL_EXPANDABLE_WALL) &&
9125       ((links_massiv && rechts_massiv) ||
9126        element == EL_EXPANDABLE_WALL_VERTICAL))
9127     Feld[ax][ay] = EL_WALL;
9128
9129   if (new_wall)
9130     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9131 }
9132
9133 void MauerAblegerStahl(int ax, int ay)
9134 {
9135   int element = Feld[ax][ay];
9136   int graphic = el2img(element);
9137   boolean oben_frei = FALSE, unten_frei = FALSE;
9138   boolean links_frei = FALSE, rechts_frei = FALSE;
9139   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9140   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9141   boolean new_wall = FALSE;
9142
9143   if (IS_ANIMATED(graphic))
9144     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9145
9146   if (!MovDelay[ax][ay])        /* start building new wall */
9147     MovDelay[ax][ay] = 6;
9148
9149   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9150   {
9151     MovDelay[ax][ay]--;
9152     if (MovDelay[ax][ay])
9153       return;
9154   }
9155
9156   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9157     oben_frei = TRUE;
9158   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9159     unten_frei = TRUE;
9160   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9161     links_frei = TRUE;
9162   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9163     rechts_frei = TRUE;
9164
9165   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9166       element == EL_EXPANDABLE_STEELWALL_ANY)
9167   {
9168     if (oben_frei)
9169     {
9170       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9171       Store[ax][ay-1] = element;
9172       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9173       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9174         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9175                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9176       new_wall = TRUE;
9177     }
9178     if (unten_frei)
9179     {
9180       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9181       Store[ax][ay+1] = element;
9182       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9183       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9184         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9185                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9186       new_wall = TRUE;
9187     }
9188   }
9189
9190   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9191       element == EL_EXPANDABLE_STEELWALL_ANY)
9192   {
9193     if (links_frei)
9194     {
9195       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9196       Store[ax-1][ay] = element;
9197       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9198       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9199         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9200                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9201       new_wall = TRUE;
9202     }
9203
9204     if (rechts_frei)
9205     {
9206       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9207       Store[ax+1][ay] = element;
9208       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9209       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9210         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9211                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9212       new_wall = TRUE;
9213     }
9214   }
9215
9216   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9217     oben_massiv = TRUE;
9218   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9219     unten_massiv = TRUE;
9220   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9221     links_massiv = TRUE;
9222   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9223     rechts_massiv = TRUE;
9224
9225   if (((oben_massiv && unten_massiv) ||
9226        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9227       ((links_massiv && rechts_massiv) ||
9228        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9229     Feld[ax][ay] = EL_STEELWALL;
9230
9231   if (new_wall)
9232     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9233 }
9234
9235 void CheckForDragon(int x, int y)
9236 {
9237   int i, j;
9238   boolean dragon_found = FALSE;
9239   static int xy[4][2] =
9240   {
9241     { 0, -1 },
9242     { -1, 0 },
9243     { +1, 0 },
9244     { 0, +1 }
9245   };
9246
9247   for (i = 0; i < NUM_DIRECTIONS; i++)
9248   {
9249     for (j = 0; j < 4; j++)
9250     {
9251       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9252
9253       if (IN_LEV_FIELD(xx, yy) &&
9254           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9255       {
9256         if (Feld[xx][yy] == EL_DRAGON)
9257           dragon_found = TRUE;
9258       }
9259       else
9260         break;
9261     }
9262   }
9263
9264   if (!dragon_found)
9265   {
9266     for (i = 0; i < NUM_DIRECTIONS; i++)
9267     {
9268       for (j = 0; j < 3; j++)
9269       {
9270         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9271   
9272         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9273         {
9274           Feld[xx][yy] = EL_EMPTY;
9275           TEST_DrawLevelField(xx, yy);
9276         }
9277         else
9278           break;
9279       }
9280     }
9281   }
9282 }
9283
9284 static void InitBuggyBase(int x, int y)
9285 {
9286   int element = Feld[x][y];
9287   int activating_delay = FRAMES_PER_SECOND / 4;
9288
9289   ChangeDelay[x][y] =
9290     (element == EL_SP_BUGGY_BASE ?
9291      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9292      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9293      activating_delay :
9294      element == EL_SP_BUGGY_BASE_ACTIVE ?
9295      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9296 }
9297
9298 static void WarnBuggyBase(int x, int y)
9299 {
9300   int i;
9301   static int xy[4][2] =
9302   {
9303     { 0, -1 },
9304     { -1, 0 },
9305     { +1, 0 },
9306     { 0, +1 }
9307   };
9308
9309   for (i = 0; i < NUM_DIRECTIONS; i++)
9310   {
9311     int xx = x + xy[i][0];
9312     int yy = y + xy[i][1];
9313
9314     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9315     {
9316       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9317
9318       break;
9319     }
9320   }
9321 }
9322
9323 static void InitTrap(int x, int y)
9324 {
9325   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9326 }
9327
9328 static void ActivateTrap(int x, int y)
9329 {
9330   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9331 }
9332
9333 static void ChangeActiveTrap(int x, int y)
9334 {
9335   int graphic = IMG_TRAP_ACTIVE;
9336
9337   /* if new animation frame was drawn, correct crumbled sand border */
9338   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9339     TEST_DrawLevelFieldCrumbled(x, y);
9340 }
9341
9342 static int getSpecialActionElement(int element, int number, int base_element)
9343 {
9344   return (element != EL_EMPTY ? element :
9345           number != -1 ? base_element + number - 1 :
9346           EL_EMPTY);
9347 }
9348
9349 static int getModifiedActionNumber(int value_old, int operator, int operand,
9350                                    int value_min, int value_max)
9351 {
9352   int value_new = (operator == CA_MODE_SET      ? operand :
9353                    operator == CA_MODE_ADD      ? value_old + operand :
9354                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9355                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9356                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9357                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9358                    value_old);
9359
9360   return (value_new < value_min ? value_min :
9361           value_new > value_max ? value_max :
9362           value_new);
9363 }
9364
9365 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9366 {
9367   struct ElementInfo *ei = &element_info[element];
9368   struct ElementChangeInfo *change = &ei->change_page[page];
9369   int target_element = change->target_element;
9370   int action_type = change->action_type;
9371   int action_mode = change->action_mode;
9372   int action_arg = change->action_arg;
9373   int action_element = change->action_element;
9374   int i;
9375
9376   if (!change->has_action)
9377     return;
9378
9379   /* ---------- determine action paramater values -------------------------- */
9380
9381   int level_time_value =
9382     (level.time > 0 ? TimeLeft :
9383      TimePlayed);
9384
9385   int action_arg_element_raw =
9386     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9387      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9388      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9389      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9390      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9391      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9392      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9393      EL_EMPTY);
9394   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9395
9396   int action_arg_direction =
9397     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9398      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9399      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9400      change->actual_trigger_side :
9401      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9402      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9403      MV_NONE);
9404
9405   int action_arg_number_min =
9406     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9407      CA_ARG_MIN);
9408
9409   int action_arg_number_max =
9410     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9411      action_type == CA_SET_LEVEL_GEMS ? 999 :
9412      action_type == CA_SET_LEVEL_TIME ? 9999 :
9413      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9414      action_type == CA_SET_CE_VALUE ? 9999 :
9415      action_type == CA_SET_CE_SCORE ? 9999 :
9416      CA_ARG_MAX);
9417
9418   int action_arg_number_reset =
9419     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9420      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9421      action_type == CA_SET_LEVEL_TIME ? level.time :
9422      action_type == CA_SET_LEVEL_SCORE ? 0 :
9423      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9424      action_type == CA_SET_CE_SCORE ? 0 :
9425      0);
9426
9427   int action_arg_number =
9428     (action_arg <= CA_ARG_MAX ? action_arg :
9429      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9430      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9431      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9432      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9433      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9434      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9435      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9436      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9437      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9438      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9439      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9440      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9441      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9442      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9443      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9444      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9445      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9446      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9447      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9448      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9449      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9450      -1);
9451
9452   int action_arg_number_old =
9453     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9454      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9455      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9456      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9457      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9458      0);
9459
9460   int action_arg_number_new =
9461     getModifiedActionNumber(action_arg_number_old,
9462                             action_mode, action_arg_number,
9463                             action_arg_number_min, action_arg_number_max);
9464
9465   int trigger_player_bits =
9466     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9467      change->actual_trigger_player_bits : change->trigger_player);
9468
9469   int action_arg_player_bits =
9470     (action_arg >= CA_ARG_PLAYER_1 &&
9471      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9472      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9473      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9474      PLAYER_BITS_ANY);
9475
9476   /* ---------- execute action  -------------------------------------------- */
9477
9478   switch (action_type)
9479   {
9480     case CA_NO_ACTION:
9481     {
9482       return;
9483     }
9484
9485     /* ---------- level actions  ------------------------------------------- */
9486
9487     case CA_RESTART_LEVEL:
9488     {
9489       game.restart_level = TRUE;
9490
9491       break;
9492     }
9493
9494     case CA_SHOW_ENVELOPE:
9495     {
9496       int element = getSpecialActionElement(action_arg_element,
9497                                             action_arg_number, EL_ENVELOPE_1);
9498
9499       if (IS_ENVELOPE(element))
9500         local_player->show_envelope = element;
9501
9502       break;
9503     }
9504
9505     case CA_SET_LEVEL_TIME:
9506     {
9507       if (level.time > 0)       /* only modify limited time value */
9508       {
9509         TimeLeft = action_arg_number_new;
9510
9511         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9512
9513         DisplayGameControlValues();
9514
9515         if (!TimeLeft && setup.time_limit)
9516           for (i = 0; i < MAX_PLAYERS; i++)
9517             KillPlayer(&stored_player[i]);
9518       }
9519
9520       break;
9521     }
9522
9523     case CA_SET_LEVEL_SCORE:
9524     {
9525       local_player->score = action_arg_number_new;
9526
9527       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9528
9529       DisplayGameControlValues();
9530
9531       break;
9532     }
9533
9534     case CA_SET_LEVEL_GEMS:
9535     {
9536       local_player->gems_still_needed = action_arg_number_new;
9537
9538       game_panel_controls[GAME_PANEL_GEMS].value =
9539         local_player->gems_still_needed;
9540
9541       DisplayGameControlValues();
9542
9543       break;
9544     }
9545
9546     case CA_SET_LEVEL_WIND:
9547     {
9548       game.wind_direction = action_arg_direction;
9549
9550       break;
9551     }
9552
9553     case CA_SET_LEVEL_RANDOM_SEED:
9554     {
9555       /* ensure that setting a new random seed while playing is predictable */
9556       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9557
9558       break;
9559     }
9560
9561     /* ---------- player actions  ------------------------------------------ */
9562
9563     case CA_MOVE_PLAYER:
9564     {
9565       /* automatically move to the next field in specified direction */
9566       for (i = 0; i < MAX_PLAYERS; i++)
9567         if (trigger_player_bits & (1 << i))
9568           stored_player[i].programmed_action = action_arg_direction;
9569
9570       break;
9571     }
9572
9573     case CA_EXIT_PLAYER:
9574     {
9575       for (i = 0; i < MAX_PLAYERS; i++)
9576         if (action_arg_player_bits & (1 << i))
9577           PlayerWins(&stored_player[i]);
9578
9579       break;
9580     }
9581
9582     case CA_KILL_PLAYER:
9583     {
9584       for (i = 0; i < MAX_PLAYERS; i++)
9585         if (action_arg_player_bits & (1 << i))
9586           KillPlayer(&stored_player[i]);
9587
9588       break;
9589     }
9590
9591     case CA_SET_PLAYER_KEYS:
9592     {
9593       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9594       int element = getSpecialActionElement(action_arg_element,
9595                                             action_arg_number, EL_KEY_1);
9596
9597       if (IS_KEY(element))
9598       {
9599         for (i = 0; i < MAX_PLAYERS; i++)
9600         {
9601           if (trigger_player_bits & (1 << i))
9602           {
9603             stored_player[i].key[KEY_NR(element)] = key_state;
9604
9605             DrawGameDoorValues();
9606           }
9607         }
9608       }
9609
9610       break;
9611     }
9612
9613     case CA_SET_PLAYER_SPEED:
9614     {
9615       for (i = 0; i < MAX_PLAYERS; i++)
9616       {
9617         if (trigger_player_bits & (1 << i))
9618         {
9619           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9620
9621           if (action_arg == CA_ARG_SPEED_FASTER &&
9622               stored_player[i].cannot_move)
9623           {
9624             action_arg_number = STEPSIZE_VERY_SLOW;
9625           }
9626           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9627                    action_arg == CA_ARG_SPEED_FASTER)
9628           {
9629             action_arg_number = 2;
9630             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9631                            CA_MODE_MULTIPLY);
9632           }
9633           else if (action_arg == CA_ARG_NUMBER_RESET)
9634           {
9635             action_arg_number = level.initial_player_stepsize[i];
9636           }
9637
9638           move_stepsize =
9639             getModifiedActionNumber(move_stepsize,
9640                                     action_mode,
9641                                     action_arg_number,
9642                                     action_arg_number_min,
9643                                     action_arg_number_max);
9644
9645           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9646         }
9647       }
9648
9649       break;
9650     }
9651
9652     case CA_SET_PLAYER_SHIELD:
9653     {
9654       for (i = 0; i < MAX_PLAYERS; i++)
9655       {
9656         if (trigger_player_bits & (1 << i))
9657         {
9658           if (action_arg == CA_ARG_SHIELD_OFF)
9659           {
9660             stored_player[i].shield_normal_time_left = 0;
9661             stored_player[i].shield_deadly_time_left = 0;
9662           }
9663           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9664           {
9665             stored_player[i].shield_normal_time_left = 999999;
9666           }
9667           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9668           {
9669             stored_player[i].shield_normal_time_left = 999999;
9670             stored_player[i].shield_deadly_time_left = 999999;
9671           }
9672         }
9673       }
9674
9675       break;
9676     }
9677
9678     case CA_SET_PLAYER_GRAVITY:
9679     {
9680       for (i = 0; i < MAX_PLAYERS; i++)
9681       {
9682         if (trigger_player_bits & (1 << i))
9683         {
9684           stored_player[i].gravity =
9685             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9686              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9687              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9688              stored_player[i].gravity);
9689         }
9690       }
9691
9692       break;
9693     }
9694
9695     case CA_SET_PLAYER_ARTWORK:
9696     {
9697       for (i = 0; i < MAX_PLAYERS; i++)
9698       {
9699         if (trigger_player_bits & (1 << i))
9700         {
9701           int artwork_element = action_arg_element;
9702
9703           if (action_arg == CA_ARG_ELEMENT_RESET)
9704             artwork_element =
9705               (level.use_artwork_element[i] ? level.artwork_element[i] :
9706                stored_player[i].element_nr);
9707
9708           if (stored_player[i].artwork_element != artwork_element)
9709             stored_player[i].Frame = 0;
9710
9711           stored_player[i].artwork_element = artwork_element;
9712
9713           SetPlayerWaiting(&stored_player[i], FALSE);
9714
9715           /* set number of special actions for bored and sleeping animation */
9716           stored_player[i].num_special_action_bored =
9717             get_num_special_action(artwork_element,
9718                                    ACTION_BORING_1, ACTION_BORING_LAST);
9719           stored_player[i].num_special_action_sleeping =
9720             get_num_special_action(artwork_element,
9721                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9722         }
9723       }
9724
9725       break;
9726     }
9727
9728     case CA_SET_PLAYER_INVENTORY:
9729     {
9730       for (i = 0; i < MAX_PLAYERS; i++)
9731       {
9732         struct PlayerInfo *player = &stored_player[i];
9733         int j, k;
9734
9735         if (trigger_player_bits & (1 << i))
9736         {
9737           int inventory_element = action_arg_element;
9738
9739           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9740               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9741               action_arg == CA_ARG_ELEMENT_ACTION)
9742           {
9743             int element = inventory_element;
9744             int collect_count = element_info[element].collect_count_initial;
9745
9746             if (!IS_CUSTOM_ELEMENT(element))
9747               collect_count = 1;
9748
9749             if (collect_count == 0)
9750               player->inventory_infinite_element = element;
9751             else
9752               for (k = 0; k < collect_count; k++)
9753                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9754                   player->inventory_element[player->inventory_size++] =
9755                     element;
9756           }
9757           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9758                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9759                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9760           {
9761             if (player->inventory_infinite_element != EL_UNDEFINED &&
9762                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9763                                      action_arg_element_raw))
9764               player->inventory_infinite_element = EL_UNDEFINED;
9765
9766             for (k = 0, j = 0; j < player->inventory_size; j++)
9767             {
9768               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9769                                         action_arg_element_raw))
9770                 player->inventory_element[k++] = player->inventory_element[j];
9771             }
9772
9773             player->inventory_size = k;
9774           }
9775           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9776           {
9777             if (player->inventory_size > 0)
9778             {
9779               for (j = 0; j < player->inventory_size - 1; j++)
9780                 player->inventory_element[j] = player->inventory_element[j + 1];
9781
9782               player->inventory_size--;
9783             }
9784           }
9785           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9786           {
9787             if (player->inventory_size > 0)
9788               player->inventory_size--;
9789           }
9790           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9791           {
9792             player->inventory_infinite_element = EL_UNDEFINED;
9793             player->inventory_size = 0;
9794           }
9795           else if (action_arg == CA_ARG_INVENTORY_RESET)
9796           {
9797             player->inventory_infinite_element = EL_UNDEFINED;
9798             player->inventory_size = 0;
9799
9800             if (level.use_initial_inventory[i])
9801             {
9802               for (j = 0; j < level.initial_inventory_size[i]; j++)
9803               {
9804                 int element = level.initial_inventory_content[i][j];
9805                 int collect_count = element_info[element].collect_count_initial;
9806
9807                 if (!IS_CUSTOM_ELEMENT(element))
9808                   collect_count = 1;
9809
9810                 if (collect_count == 0)
9811                   player->inventory_infinite_element = element;
9812                 else
9813                   for (k = 0; k < collect_count; k++)
9814                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9815                       player->inventory_element[player->inventory_size++] =
9816                         element;
9817               }
9818             }
9819           }
9820         }
9821       }
9822
9823       break;
9824     }
9825
9826     /* ---------- CE actions  ---------------------------------------------- */
9827
9828     case CA_SET_CE_VALUE:
9829     {
9830       int last_ce_value = CustomValue[x][y];
9831
9832       CustomValue[x][y] = action_arg_number_new;
9833
9834       if (CustomValue[x][y] != last_ce_value)
9835       {
9836         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9837         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9838
9839         if (CustomValue[x][y] == 0)
9840         {
9841           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9842           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9843         }
9844       }
9845
9846       break;
9847     }
9848
9849     case CA_SET_CE_SCORE:
9850     {
9851       int last_ce_score = ei->collect_score;
9852
9853       ei->collect_score = action_arg_number_new;
9854
9855       if (ei->collect_score != last_ce_score)
9856       {
9857         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9858         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9859
9860         if (ei->collect_score == 0)
9861         {
9862           int xx, yy;
9863
9864           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9865           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9866
9867           /*
9868             This is a very special case that seems to be a mixture between
9869             CheckElementChange() and CheckTriggeredElementChange(): while
9870             the first one only affects single elements that are triggered
9871             directly, the second one affects multiple elements in the playfield
9872             that are triggered indirectly by another element. This is a third
9873             case: Changing the CE score always affects multiple identical CEs,
9874             so every affected CE must be checked, not only the single CE for
9875             which the CE score was changed in the first place (as every instance
9876             of that CE shares the same CE score, and therefore also can change)!
9877           */
9878           SCAN_PLAYFIELD(xx, yy)
9879           {
9880             if (Feld[xx][yy] == element)
9881               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9882                                  CE_SCORE_GETS_ZERO);
9883           }
9884         }
9885       }
9886
9887       break;
9888     }
9889
9890     case CA_SET_CE_ARTWORK:
9891     {
9892       int artwork_element = action_arg_element;
9893       boolean reset_frame = FALSE;
9894       int xx, yy;
9895
9896       if (action_arg == CA_ARG_ELEMENT_RESET)
9897         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9898                            element);
9899
9900       if (ei->gfx_element != artwork_element)
9901         reset_frame = TRUE;
9902
9903       ei->gfx_element = artwork_element;
9904
9905       SCAN_PLAYFIELD(xx, yy)
9906       {
9907         if (Feld[xx][yy] == element)
9908         {
9909           if (reset_frame)
9910           {
9911             ResetGfxAnimation(xx, yy);
9912             ResetRandomAnimationValue(xx, yy);
9913           }
9914
9915           TEST_DrawLevelField(xx, yy);
9916         }
9917       }
9918
9919       break;
9920     }
9921
9922     /* ---------- engine actions  ------------------------------------------ */
9923
9924     case CA_SET_ENGINE_SCAN_MODE:
9925     {
9926       InitPlayfieldScanMode(action_arg);
9927
9928       break;
9929     }
9930
9931     default:
9932       break;
9933   }
9934 }
9935
9936 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9937 {
9938   int old_element = Feld[x][y];
9939   int new_element = GetElementFromGroupElement(element);
9940   int previous_move_direction = MovDir[x][y];
9941   int last_ce_value = CustomValue[x][y];
9942   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9943   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9944   boolean add_player_onto_element = (new_element_is_player &&
9945                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9946                                      IS_WALKABLE(old_element));
9947
9948   if (!add_player_onto_element)
9949   {
9950     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9951       RemoveMovingField(x, y);
9952     else
9953       RemoveField(x, y);
9954
9955     Feld[x][y] = new_element;
9956
9957     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9958       MovDir[x][y] = previous_move_direction;
9959
9960     if (element_info[new_element].use_last_ce_value)
9961       CustomValue[x][y] = last_ce_value;
9962
9963     InitField_WithBug1(x, y, FALSE);
9964
9965     new_element = Feld[x][y];   /* element may have changed */
9966
9967     ResetGfxAnimation(x, y);
9968     ResetRandomAnimationValue(x, y);
9969
9970     TEST_DrawLevelField(x, y);
9971
9972     if (GFX_CRUMBLED(new_element))
9973       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9974   }
9975
9976   /* check if element under the player changes from accessible to unaccessible
9977      (needed for special case of dropping element which then changes) */
9978   /* (must be checked after creating new element for walkable group elements) */
9979   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9980       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9981   {
9982     Bang(x, y);
9983
9984     return;
9985   }
9986
9987   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9988   if (new_element_is_player)
9989     RelocatePlayer(x, y, new_element);
9990
9991   if (is_change)
9992     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9993
9994   TestIfBadThingTouchesPlayer(x, y);
9995   TestIfPlayerTouchesCustomElement(x, y);
9996   TestIfElementTouchesCustomElement(x, y);
9997 }
9998
9999 static void CreateField(int x, int y, int element)
10000 {
10001   CreateFieldExt(x, y, element, FALSE);
10002 }
10003
10004 static void CreateElementFromChange(int x, int y, int element)
10005 {
10006   element = GET_VALID_RUNTIME_ELEMENT(element);
10007
10008   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10009   {
10010     int old_element = Feld[x][y];
10011
10012     /* prevent changed element from moving in same engine frame
10013        unless both old and new element can either fall or move */
10014     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10015         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10016       Stop[x][y] = TRUE;
10017   }
10018
10019   CreateFieldExt(x, y, element, TRUE);
10020 }
10021
10022 static boolean ChangeElement(int x, int y, int element, int page)
10023 {
10024   struct ElementInfo *ei = &element_info[element];
10025   struct ElementChangeInfo *change = &ei->change_page[page];
10026   int ce_value = CustomValue[x][y];
10027   int ce_score = ei->collect_score;
10028   int target_element;
10029   int old_element = Feld[x][y];
10030
10031   /* always use default change event to prevent running into a loop */
10032   if (ChangeEvent[x][y] == -1)
10033     ChangeEvent[x][y] = CE_DELAY;
10034
10035   if (ChangeEvent[x][y] == CE_DELAY)
10036   {
10037     /* reset actual trigger element, trigger player and action element */
10038     change->actual_trigger_element = EL_EMPTY;
10039     change->actual_trigger_player = EL_EMPTY;
10040     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10041     change->actual_trigger_side = CH_SIDE_NONE;
10042     change->actual_trigger_ce_value = 0;
10043     change->actual_trigger_ce_score = 0;
10044   }
10045
10046   /* do not change elements more than a specified maximum number of changes */
10047   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10048     return FALSE;
10049
10050   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10051
10052   if (change->explode)
10053   {
10054     Bang(x, y);
10055
10056     return TRUE;
10057   }
10058
10059   if (change->use_target_content)
10060   {
10061     boolean complete_replace = TRUE;
10062     boolean can_replace[3][3];
10063     int xx, yy;
10064
10065     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10066     {
10067       boolean is_empty;
10068       boolean is_walkable;
10069       boolean is_diggable;
10070       boolean is_collectible;
10071       boolean is_removable;
10072       boolean is_destructible;
10073       int ex = x + xx - 1;
10074       int ey = y + yy - 1;
10075       int content_element = change->target_content.e[xx][yy];
10076       int e;
10077
10078       can_replace[xx][yy] = TRUE;
10079
10080       if (ex == x && ey == y)   /* do not check changing element itself */
10081         continue;
10082
10083       if (content_element == EL_EMPTY_SPACE)
10084       {
10085         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10086
10087         continue;
10088       }
10089
10090       if (!IN_LEV_FIELD(ex, ey))
10091       {
10092         can_replace[xx][yy] = FALSE;
10093         complete_replace = FALSE;
10094
10095         continue;
10096       }
10097
10098       e = Feld[ex][ey];
10099
10100       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10101         e = MovingOrBlocked2Element(ex, ey);
10102
10103       is_empty = (IS_FREE(ex, ey) ||
10104                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10105
10106       is_walkable     = (is_empty || IS_WALKABLE(e));
10107       is_diggable     = (is_empty || IS_DIGGABLE(e));
10108       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10109       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10110       is_removable    = (is_diggable || is_collectible);
10111
10112       can_replace[xx][yy] =
10113         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10114           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10115           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10116           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10117           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10118           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10119          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10120
10121       if (!can_replace[xx][yy])
10122         complete_replace = FALSE;
10123     }
10124
10125     if (!change->only_if_complete || complete_replace)
10126     {
10127       boolean something_has_changed = FALSE;
10128
10129       if (change->only_if_complete && change->use_random_replace &&
10130           RND(100) < change->random_percentage)
10131         return FALSE;
10132
10133       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10134       {
10135         int ex = x + xx - 1;
10136         int ey = y + yy - 1;
10137         int content_element;
10138
10139         if (can_replace[xx][yy] && (!change->use_random_replace ||
10140                                     RND(100) < change->random_percentage))
10141         {
10142           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10143             RemoveMovingField(ex, ey);
10144
10145           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10146
10147           content_element = change->target_content.e[xx][yy];
10148           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10149                                               ce_value, ce_score);
10150
10151           CreateElementFromChange(ex, ey, target_element);
10152
10153           something_has_changed = TRUE;
10154
10155           /* for symmetry reasons, freeze newly created border elements */
10156           if (ex != x || ey != y)
10157             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10158         }
10159       }
10160
10161       if (something_has_changed)
10162       {
10163         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10164         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10165       }
10166     }
10167   }
10168   else
10169   {
10170     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10171                                         ce_value, ce_score);
10172
10173     if (element == EL_DIAGONAL_GROWING ||
10174         element == EL_DIAGONAL_SHRINKING)
10175     {
10176       target_element = Store[x][y];
10177
10178       Store[x][y] = EL_EMPTY;
10179     }
10180
10181     CreateElementFromChange(x, y, target_element);
10182
10183     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10184     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10185   }
10186
10187   /* this uses direct change before indirect change */
10188   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10189
10190   return TRUE;
10191 }
10192
10193 static void HandleElementChange(int x, int y, int page)
10194 {
10195   int element = MovingOrBlocked2Element(x, y);
10196   struct ElementInfo *ei = &element_info[element];
10197   struct ElementChangeInfo *change = &ei->change_page[page];
10198   boolean handle_action_before_change = FALSE;
10199
10200 #ifdef DEBUG
10201   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10202       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10203   {
10204     printf("\n\n");
10205     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10206            x, y, element, element_info[element].token_name);
10207     printf("HandleElementChange(): This should never happen!\n");
10208     printf("\n\n");
10209   }
10210 #endif
10211
10212   /* this can happen with classic bombs on walkable, changing elements */
10213   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10214   {
10215     return;
10216   }
10217
10218   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10219   {
10220     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10221
10222     if (change->can_change)
10223     {
10224       /* !!! not clear why graphic animation should be reset at all here !!! */
10225       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10226       /* when a custom element is about to change (for example by change delay),
10227          do not reset graphic animation when the custom element is moving */
10228       if (!IS_MOVING(x, y))
10229       {
10230         ResetGfxAnimation(x, y);
10231         ResetRandomAnimationValue(x, y);
10232       }
10233
10234       if (change->pre_change_function)
10235         change->pre_change_function(x, y);
10236     }
10237   }
10238
10239   ChangeDelay[x][y]--;
10240
10241   if (ChangeDelay[x][y] != 0)           /* continue element change */
10242   {
10243     if (change->can_change)
10244     {
10245       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10246
10247       if (IS_ANIMATED(graphic))
10248         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10249
10250       if (change->change_function)
10251         change->change_function(x, y);
10252     }
10253   }
10254   else                                  /* finish element change */
10255   {
10256     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10257     {
10258       page = ChangePage[x][y];
10259       ChangePage[x][y] = -1;
10260
10261       change = &ei->change_page[page];
10262     }
10263
10264     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10265     {
10266       ChangeDelay[x][y] = 1;            /* try change after next move step */
10267       ChangePage[x][y] = page;          /* remember page to use for change */
10268
10269       return;
10270     }
10271
10272     /* special case: set new level random seed before changing element */
10273     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10274       handle_action_before_change = TRUE;
10275
10276     if (change->has_action && handle_action_before_change)
10277       ExecuteCustomElementAction(x, y, element, page);
10278
10279     if (change->can_change)
10280     {
10281       if (ChangeElement(x, y, element, page))
10282       {
10283         if (change->post_change_function)
10284           change->post_change_function(x, y);
10285       }
10286     }
10287
10288     if (change->has_action && !handle_action_before_change)
10289       ExecuteCustomElementAction(x, y, element, page);
10290   }
10291 }
10292
10293 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10294                                               int trigger_element,
10295                                               int trigger_event,
10296                                               int trigger_player,
10297                                               int trigger_side,
10298                                               int trigger_page)
10299 {
10300   boolean change_done_any = FALSE;
10301   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10302   int i;
10303
10304   if (!(trigger_events[trigger_element][trigger_event]))
10305     return FALSE;
10306
10307   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10308
10309   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10310   {
10311     int element = EL_CUSTOM_START + i;
10312     boolean change_done = FALSE;
10313     int p;
10314
10315     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10316         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10317       continue;
10318
10319     for (p = 0; p < element_info[element].num_change_pages; p++)
10320     {
10321       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10322
10323       if (change->can_change_or_has_action &&
10324           change->has_event[trigger_event] &&
10325           change->trigger_side & trigger_side &&
10326           change->trigger_player & trigger_player &&
10327           change->trigger_page & trigger_page_bits &&
10328           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10329       {
10330         change->actual_trigger_element = trigger_element;
10331         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10332         change->actual_trigger_player_bits = trigger_player;
10333         change->actual_trigger_side = trigger_side;
10334         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10335         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10336
10337         if ((change->can_change && !change_done) || change->has_action)
10338         {
10339           int x, y;
10340
10341           SCAN_PLAYFIELD(x, y)
10342           {
10343             if (Feld[x][y] == element)
10344             {
10345               if (change->can_change && !change_done)
10346               {
10347                 /* if element already changed in this frame, not only prevent
10348                    another element change (checked in ChangeElement()), but
10349                    also prevent additional element actions for this element */
10350
10351                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10352                     !level.use_action_after_change_bug)
10353                   continue;
10354
10355                 ChangeDelay[x][y] = 1;
10356                 ChangeEvent[x][y] = trigger_event;
10357
10358                 HandleElementChange(x, y, p);
10359               }
10360               else if (change->has_action)
10361               {
10362                 /* if element already changed in this frame, not only prevent
10363                    another element change (checked in ChangeElement()), but
10364                    also prevent additional element actions for this element */
10365
10366                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10367                     !level.use_action_after_change_bug)
10368                   continue;
10369
10370                 ExecuteCustomElementAction(x, y, element, p);
10371                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10372               }
10373             }
10374           }
10375
10376           if (change->can_change)
10377           {
10378             change_done = TRUE;
10379             change_done_any = TRUE;
10380           }
10381         }
10382       }
10383     }
10384   }
10385
10386   RECURSION_LOOP_DETECTION_END();
10387
10388   return change_done_any;
10389 }
10390
10391 static boolean CheckElementChangeExt(int x, int y,
10392                                      int element,
10393                                      int trigger_element,
10394                                      int trigger_event,
10395                                      int trigger_player,
10396                                      int trigger_side)
10397 {
10398   boolean change_done = FALSE;
10399   int p;
10400
10401   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10402       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10403     return FALSE;
10404
10405   if (Feld[x][y] == EL_BLOCKED)
10406   {
10407     Blocked2Moving(x, y, &x, &y);
10408     element = Feld[x][y];
10409   }
10410
10411   /* check if element has already changed or is about to change after moving */
10412   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10413        Feld[x][y] != element) ||
10414
10415       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10416        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10417         ChangePage[x][y] != -1)))
10418     return FALSE;
10419
10420   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10421
10422   for (p = 0; p < element_info[element].num_change_pages; p++)
10423   {
10424     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10425
10426     /* check trigger element for all events where the element that is checked
10427        for changing interacts with a directly adjacent element -- this is
10428        different to element changes that affect other elements to change on the
10429        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10430     boolean check_trigger_element =
10431       (trigger_event == CE_TOUCHING_X ||
10432        trigger_event == CE_HITTING_X ||
10433        trigger_event == CE_HIT_BY_X ||
10434        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10435
10436     if (change->can_change_or_has_action &&
10437         change->has_event[trigger_event] &&
10438         change->trigger_side & trigger_side &&
10439         change->trigger_player & trigger_player &&
10440         (!check_trigger_element ||
10441          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10442     {
10443       change->actual_trigger_element = trigger_element;
10444       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10445       change->actual_trigger_player_bits = trigger_player;
10446       change->actual_trigger_side = trigger_side;
10447       change->actual_trigger_ce_value = CustomValue[x][y];
10448       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10449
10450       /* special case: trigger element not at (x,y) position for some events */
10451       if (check_trigger_element)
10452       {
10453         static struct
10454         {
10455           int dx, dy;
10456         } move_xy[] =
10457           {
10458             {  0,  0 },
10459             { -1,  0 },
10460             { +1,  0 },
10461             {  0,  0 },
10462             {  0, -1 },
10463             {  0,  0 }, { 0, 0 }, { 0, 0 },
10464             {  0, +1 }
10465           };
10466
10467         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10468         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10469
10470         change->actual_trigger_ce_value = CustomValue[xx][yy];
10471         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10472       }
10473
10474       if (change->can_change && !change_done)
10475       {
10476         ChangeDelay[x][y] = 1;
10477         ChangeEvent[x][y] = trigger_event;
10478
10479         HandleElementChange(x, y, p);
10480
10481         change_done = TRUE;
10482       }
10483       else if (change->has_action)
10484       {
10485         ExecuteCustomElementAction(x, y, element, p);
10486         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10487       }
10488     }
10489   }
10490
10491   RECURSION_LOOP_DETECTION_END();
10492
10493   return change_done;
10494 }
10495
10496 static void PlayPlayerSound(struct PlayerInfo *player)
10497 {
10498   int jx = player->jx, jy = player->jy;
10499   int sound_element = player->artwork_element;
10500   int last_action = player->last_action_waiting;
10501   int action = player->action_waiting;
10502
10503   if (player->is_waiting)
10504   {
10505     if (action != last_action)
10506       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10507     else
10508       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10509   }
10510   else
10511   {
10512     if (action != last_action)
10513       StopSound(element_info[sound_element].sound[last_action]);
10514
10515     if (last_action == ACTION_SLEEPING)
10516       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10517   }
10518 }
10519
10520 static void PlayAllPlayersSound()
10521 {
10522   int i;
10523
10524   for (i = 0; i < MAX_PLAYERS; i++)
10525     if (stored_player[i].active)
10526       PlayPlayerSound(&stored_player[i]);
10527 }
10528
10529 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10530 {
10531   boolean last_waiting = player->is_waiting;
10532   int move_dir = player->MovDir;
10533
10534   player->dir_waiting = move_dir;
10535   player->last_action_waiting = player->action_waiting;
10536
10537   if (is_waiting)
10538   {
10539     if (!last_waiting)          /* not waiting -> waiting */
10540     {
10541       player->is_waiting = TRUE;
10542
10543       player->frame_counter_bored =
10544         FrameCounter +
10545         game.player_boring_delay_fixed +
10546         GetSimpleRandom(game.player_boring_delay_random);
10547       player->frame_counter_sleeping =
10548         FrameCounter +
10549         game.player_sleeping_delay_fixed +
10550         GetSimpleRandom(game.player_sleeping_delay_random);
10551
10552       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10553     }
10554
10555     if (game.player_sleeping_delay_fixed +
10556         game.player_sleeping_delay_random > 0 &&
10557         player->anim_delay_counter == 0 &&
10558         player->post_delay_counter == 0 &&
10559         FrameCounter >= player->frame_counter_sleeping)
10560       player->is_sleeping = TRUE;
10561     else if (game.player_boring_delay_fixed +
10562              game.player_boring_delay_random > 0 &&
10563              FrameCounter >= player->frame_counter_bored)
10564       player->is_bored = TRUE;
10565
10566     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10567                               player->is_bored ? ACTION_BORING :
10568                               ACTION_WAITING);
10569
10570     if (player->is_sleeping && player->use_murphy)
10571     {
10572       /* special case for sleeping Murphy when leaning against non-free tile */
10573
10574       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10575           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10576            !IS_MOVING(player->jx - 1, player->jy)))
10577         move_dir = MV_LEFT;
10578       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10579                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10580                 !IS_MOVING(player->jx + 1, player->jy)))
10581         move_dir = MV_RIGHT;
10582       else
10583         player->is_sleeping = FALSE;
10584
10585       player->dir_waiting = move_dir;
10586     }
10587
10588     if (player->is_sleeping)
10589     {
10590       if (player->num_special_action_sleeping > 0)
10591       {
10592         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10593         {
10594           int last_special_action = player->special_action_sleeping;
10595           int num_special_action = player->num_special_action_sleeping;
10596           int special_action =
10597             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10598              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10599              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10600              last_special_action + 1 : ACTION_SLEEPING);
10601           int special_graphic =
10602             el_act_dir2img(player->artwork_element, special_action, move_dir);
10603
10604           player->anim_delay_counter =
10605             graphic_info[special_graphic].anim_delay_fixed +
10606             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10607           player->post_delay_counter =
10608             graphic_info[special_graphic].post_delay_fixed +
10609             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10610
10611           player->special_action_sleeping = special_action;
10612         }
10613
10614         if (player->anim_delay_counter > 0)
10615         {
10616           player->action_waiting = player->special_action_sleeping;
10617           player->anim_delay_counter--;
10618         }
10619         else if (player->post_delay_counter > 0)
10620         {
10621           player->post_delay_counter--;
10622         }
10623       }
10624     }
10625     else if (player->is_bored)
10626     {
10627       if (player->num_special_action_bored > 0)
10628       {
10629         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10630         {
10631           int special_action =
10632             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10633           int special_graphic =
10634             el_act_dir2img(player->artwork_element, special_action, move_dir);
10635
10636           player->anim_delay_counter =
10637             graphic_info[special_graphic].anim_delay_fixed +
10638             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10639           player->post_delay_counter =
10640             graphic_info[special_graphic].post_delay_fixed +
10641             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10642
10643           player->special_action_bored = special_action;
10644         }
10645
10646         if (player->anim_delay_counter > 0)
10647         {
10648           player->action_waiting = player->special_action_bored;
10649           player->anim_delay_counter--;
10650         }
10651         else if (player->post_delay_counter > 0)
10652         {
10653           player->post_delay_counter--;
10654         }
10655       }
10656     }
10657   }
10658   else if (last_waiting)        /* waiting -> not waiting */
10659   {
10660     player->is_waiting = FALSE;
10661     player->is_bored = FALSE;
10662     player->is_sleeping = FALSE;
10663
10664     player->frame_counter_bored = -1;
10665     player->frame_counter_sleeping = -1;
10666
10667     player->anim_delay_counter = 0;
10668     player->post_delay_counter = 0;
10669
10670     player->dir_waiting = player->MovDir;
10671     player->action_waiting = ACTION_DEFAULT;
10672
10673     player->special_action_bored = ACTION_DEFAULT;
10674     player->special_action_sleeping = ACTION_DEFAULT;
10675   }
10676 }
10677
10678 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10679 {
10680   static boolean player_was_moving = FALSE;
10681   static boolean player_was_snapping = FALSE;
10682   static boolean player_was_dropping = FALSE;
10683
10684   if ((!player->is_moving  && player_was_moving) ||
10685       (player->MovPos == 0 && player_was_moving) ||
10686       (player->is_snapping && !player_was_snapping) ||
10687       (player->is_dropping && !player_was_dropping))
10688   {
10689     SaveEngineSnapshotToList();
10690
10691     player_was_moving = FALSE;
10692     player_was_snapping = TRUE;
10693     player_was_dropping = TRUE;
10694   }
10695   else
10696   {
10697     if (player->is_moving)
10698       player_was_moving = TRUE;
10699
10700     if (!player->is_snapping)
10701       player_was_snapping = FALSE;
10702
10703     if (!player->is_dropping)
10704       player_was_dropping = FALSE;
10705   }
10706 }
10707
10708 static void CheckSingleStepMode(struct PlayerInfo *player)
10709 {
10710   if (tape.single_step && tape.recording && !tape.pausing)
10711   {
10712     /* as it is called "single step mode", just return to pause mode when the
10713        player stopped moving after one tile (or never starts moving at all) */
10714     if (!player->is_moving && !player->is_pushing)
10715     {
10716       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10717       SnapField(player, 0, 0);                  /* stop snapping */
10718     }
10719   }
10720
10721   CheckSaveEngineSnapshot(player);
10722 }
10723
10724 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10725 {
10726   int left      = player_action & JOY_LEFT;
10727   int right     = player_action & JOY_RIGHT;
10728   int up        = player_action & JOY_UP;
10729   int down      = player_action & JOY_DOWN;
10730   int button1   = player_action & JOY_BUTTON_1;
10731   int button2   = player_action & JOY_BUTTON_2;
10732   int dx        = (left ? -1 : right ? 1 : 0);
10733   int dy        = (up   ? -1 : down  ? 1 : 0);
10734
10735   if (!player->active || tape.pausing)
10736     return 0;
10737
10738   if (player_action)
10739   {
10740     if (button1)
10741       SnapField(player, dx, dy);
10742     else
10743     {
10744       if (button2)
10745         DropElement(player);
10746
10747       MovePlayer(player, dx, dy);
10748     }
10749
10750     CheckSingleStepMode(player);
10751
10752     SetPlayerWaiting(player, FALSE);
10753
10754     return player_action;
10755   }
10756   else
10757   {
10758     /* no actions for this player (no input at player's configured device) */
10759
10760     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10761     SnapField(player, 0, 0);
10762     CheckGravityMovementWhenNotMoving(player);
10763
10764     if (player->MovPos == 0)
10765       SetPlayerWaiting(player, TRUE);
10766
10767     if (player->MovPos == 0)    /* needed for tape.playing */
10768       player->is_moving = FALSE;
10769
10770     player->is_dropping = FALSE;
10771     player->is_dropping_pressed = FALSE;
10772     player->drop_pressed_delay = 0;
10773
10774     CheckSingleStepMode(player);
10775
10776     return 0;
10777   }
10778 }
10779
10780 static void CheckLevelTime()
10781 {
10782   int i;
10783
10784   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10785   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10786   {
10787     if (level.native_em_level->lev->home == 0)  /* all players at home */
10788     {
10789       PlayerWins(local_player);
10790
10791       AllPlayersGone = TRUE;
10792
10793       level.native_em_level->lev->home = -1;
10794     }
10795
10796     if (level.native_em_level->ply[0]->alive == 0 &&
10797         level.native_em_level->ply[1]->alive == 0 &&
10798         level.native_em_level->ply[2]->alive == 0 &&
10799         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10800       AllPlayersGone = TRUE;
10801   }
10802   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10803   {
10804     if (game_sp.LevelSolved &&
10805         !game_sp.GameOver)                              /* game won */
10806     {
10807       PlayerWins(local_player);
10808
10809       game_sp.GameOver = TRUE;
10810
10811       AllPlayersGone = TRUE;
10812     }
10813
10814     if (game_sp.GameOver)                               /* game lost */
10815       AllPlayersGone = TRUE;
10816   }
10817
10818   if (TimeFrames >= FRAMES_PER_SECOND)
10819   {
10820     TimeFrames = 0;
10821     TapeTime++;
10822
10823     for (i = 0; i < MAX_PLAYERS; i++)
10824     {
10825       struct PlayerInfo *player = &stored_player[i];
10826
10827       if (SHIELD_ON(player))
10828       {
10829         player->shield_normal_time_left--;
10830
10831         if (player->shield_deadly_time_left > 0)
10832           player->shield_deadly_time_left--;
10833       }
10834     }
10835
10836     if (!local_player->LevelSolved && !level.use_step_counter)
10837     {
10838       TimePlayed++;
10839
10840       if (TimeLeft > 0)
10841       {
10842         TimeLeft--;
10843
10844         if (TimeLeft <= 10 && setup.time_limit)
10845           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10846
10847         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10848            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10849
10850         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10851
10852         if (!TimeLeft && setup.time_limit)
10853         {
10854           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10855             level.native_em_level->lev->killed_out_of_time = TRUE;
10856           else
10857             for (i = 0; i < MAX_PLAYERS; i++)
10858               KillPlayer(&stored_player[i]);
10859         }
10860       }
10861       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10862       {
10863         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10864       }
10865
10866       level.native_em_level->lev->time =
10867         (game.no_time_limit ? TimePlayed : TimeLeft);
10868     }
10869
10870     if (tape.recording || tape.playing)
10871       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10872   }
10873
10874   if (tape.recording || tape.playing)
10875     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10876
10877   UpdateAndDisplayGameControlValues();
10878 }
10879
10880 void AdvanceFrameAndPlayerCounters(int player_nr)
10881 {
10882   int i;
10883
10884   /* advance frame counters (global frame counter and time frame counter) */
10885   FrameCounter++;
10886   TimeFrames++;
10887
10888   /* advance player counters (counters for move delay, move animation etc.) */
10889   for (i = 0; i < MAX_PLAYERS; i++)
10890   {
10891     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10892     int move_delay_value = stored_player[i].move_delay_value;
10893     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10894
10895     if (!advance_player_counters)       /* not all players may be affected */
10896       continue;
10897
10898     if (move_frames == 0)       /* less than one move per game frame */
10899     {
10900       int stepsize = TILEX / move_delay_value;
10901       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10902       int count = (stored_player[i].is_moving ?
10903                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10904
10905       if (count % delay == 0)
10906         move_frames = 1;
10907     }
10908
10909     stored_player[i].Frame += move_frames;
10910
10911     if (stored_player[i].MovPos != 0)
10912       stored_player[i].StepFrame += move_frames;
10913
10914     if (stored_player[i].move_delay > 0)
10915       stored_player[i].move_delay--;
10916
10917     /* due to bugs in previous versions, counter must count up, not down */
10918     if (stored_player[i].push_delay != -1)
10919       stored_player[i].push_delay++;
10920
10921     if (stored_player[i].drop_delay > 0)
10922       stored_player[i].drop_delay--;
10923
10924     if (stored_player[i].is_dropping_pressed)
10925       stored_player[i].drop_pressed_delay++;
10926   }
10927 }
10928
10929 void StartGameActions(boolean init_network_game, boolean record_tape,
10930                       int random_seed)
10931 {
10932   unsigned int new_random_seed = InitRND(random_seed);
10933
10934   if (record_tape)
10935     TapeStartRecording(new_random_seed);
10936
10937 #if defined(NETWORK_AVALIABLE)
10938   if (init_network_game)
10939   {
10940     SendToServer_StartPlaying();
10941
10942     return;
10943   }
10944 #endif
10945
10946   InitGame();
10947 }
10948
10949 void GameActions()
10950 {
10951   static unsigned int game_frame_delay = 0;
10952   unsigned int game_frame_delay_value;
10953   byte *recorded_player_action;
10954   byte summarized_player_action = 0;
10955   byte tape_action[MAX_PLAYERS];
10956   int i;
10957
10958   /* detect endless loops, caused by custom element programming */
10959   if (recursion_loop_detected && recursion_loop_depth == 0)
10960   {
10961     char *message = getStringCat3("Internal Error! Element ",
10962                                   EL_NAME(recursion_loop_element),
10963                                   " caused endless loop! Quit the game?");
10964
10965     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10966           EL_NAME(recursion_loop_element));
10967
10968     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10969
10970     recursion_loop_detected = FALSE;    /* if game should be continued */
10971
10972     free(message);
10973
10974     return;
10975   }
10976
10977   if (game.restart_level)
10978     StartGameActions(options.network, setup.autorecord, level.random_seed);
10979
10980   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10981   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10982   {
10983     if (level.native_em_level->lev->home == 0)  /* all players at home */
10984     {
10985       PlayerWins(local_player);
10986
10987       AllPlayersGone = TRUE;
10988
10989       level.native_em_level->lev->home = -1;
10990     }
10991
10992     if (level.native_em_level->ply[0]->alive == 0 &&
10993         level.native_em_level->ply[1]->alive == 0 &&
10994         level.native_em_level->ply[2]->alive == 0 &&
10995         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10996       AllPlayersGone = TRUE;
10997   }
10998   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10999   {
11000     if (game_sp.LevelSolved &&
11001         !game_sp.GameOver)                              /* game won */
11002     {
11003       PlayerWins(local_player);
11004
11005       game_sp.GameOver = TRUE;
11006
11007       AllPlayersGone = TRUE;
11008     }
11009
11010     if (game_sp.GameOver)                               /* game lost */
11011       AllPlayersGone = TRUE;
11012   }
11013
11014   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11015     GameWon();
11016
11017   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11018     TapeStop();
11019
11020   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11021     return;
11022
11023   game_frame_delay_value =
11024     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11025
11026   if (tape.playing && tape.warp_forward && !tape.pausing)
11027     game_frame_delay_value = 0;
11028
11029   /* ---------- main game synchronization point ---------- */
11030
11031   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11032
11033   if (network_playing && !network_player_action_received)
11034   {
11035     /* try to get network player actions in time */
11036
11037 #if defined(NETWORK_AVALIABLE)
11038     /* last chance to get network player actions without main loop delay */
11039     HandleNetworking();
11040 #endif
11041
11042     /* game was quit by network peer */
11043     if (game_status != GAME_MODE_PLAYING)
11044       return;
11045
11046     if (!network_player_action_received)
11047       return;           /* failed to get network player actions in time */
11048
11049     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11050   }
11051
11052   if (tape.pausing)
11053     return;
11054
11055   /* at this point we know that we really continue executing the game */
11056
11057   network_player_action_received = FALSE;
11058
11059   /* when playing tape, read previously recorded player input from tape data */
11060   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11061
11062   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11063   if (tape.pausing)
11064     return;
11065
11066   if (tape.set_centered_player)
11067   {
11068     game.centered_player_nr_next = tape.centered_player_nr_next;
11069     game.set_centered_player = TRUE;
11070   }
11071
11072   for (i = 0; i < MAX_PLAYERS; i++)
11073   {
11074     summarized_player_action |= stored_player[i].action;
11075
11076     if (!network_playing && (game.team_mode || tape.playing))
11077       stored_player[i].effective_action = stored_player[i].action;
11078   }
11079
11080 #if defined(NETWORK_AVALIABLE)
11081   if (network_playing)
11082     SendToServer_MovePlayer(summarized_player_action);
11083 #endif
11084
11085   if (!options.network && !game.team_mode)
11086     local_player->effective_action = summarized_player_action;
11087
11088   if (tape.recording &&
11089       setup.team_mode &&
11090       setup.input_on_focus &&
11091       game.centered_player_nr != -1)
11092   {
11093     for (i = 0; i < MAX_PLAYERS; i++)
11094       stored_player[i].effective_action =
11095         (i == game.centered_player_nr ? summarized_player_action : 0);
11096   }
11097
11098   if (recorded_player_action != NULL)
11099     for (i = 0; i < MAX_PLAYERS; i++)
11100       stored_player[i].effective_action = recorded_player_action[i];
11101
11102   for (i = 0; i < MAX_PLAYERS; i++)
11103   {
11104     tape_action[i] = stored_player[i].effective_action;
11105
11106     /* (this may happen in the RND game engine if a player was not present on
11107        the playfield on level start, but appeared later from a custom element */
11108     if (setup.team_mode &&
11109         tape.recording &&
11110         tape_action[i] &&
11111         !tape.player_participates[i])
11112       tape.player_participates[i] = TRUE;
11113   }
11114
11115   /* only record actions from input devices, but not programmed actions */
11116   if (tape.recording)
11117     TapeRecordAction(tape_action);
11118
11119 #if USE_NEW_PLAYER_ASSIGNMENTS
11120   // !!! also map player actions in single player mode !!!
11121   // if (game.team_mode)
11122   {
11123     byte mapped_action[MAX_PLAYERS];
11124
11125 #if DEBUG_PLAYER_ACTIONS
11126     printf(":::");
11127     for (i = 0; i < MAX_PLAYERS; i++)
11128       printf(" %d, ", stored_player[i].effective_action);
11129 #endif
11130
11131     for (i = 0; i < MAX_PLAYERS; i++)
11132       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11133
11134     for (i = 0; i < MAX_PLAYERS; i++)
11135       stored_player[i].effective_action = mapped_action[i];
11136
11137 #if DEBUG_PLAYER_ACTIONS
11138     printf(" =>");
11139     for (i = 0; i < MAX_PLAYERS; i++)
11140       printf(" %d, ", stored_player[i].effective_action);
11141     printf("\n");
11142 #endif
11143   }
11144 #if DEBUG_PLAYER_ACTIONS
11145   else
11146   {
11147     printf(":::");
11148     for (i = 0; i < MAX_PLAYERS; i++)
11149       printf(" %d, ", stored_player[i].effective_action);
11150     printf("\n");
11151   }
11152 #endif
11153 #endif
11154
11155   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11156   {
11157     GameActions_EM_Main();
11158   }
11159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11160   {
11161     GameActions_SP_Main();
11162   }
11163   else
11164   {
11165     GameActions_RND();
11166   }
11167 }
11168
11169 void GameActions_EM_Main()
11170 {
11171   byte effective_action[MAX_PLAYERS];
11172   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11173   int i;
11174
11175   for (i = 0; i < MAX_PLAYERS; i++)
11176     effective_action[i] = stored_player[i].effective_action;
11177
11178   GameActions_EM(effective_action, warp_mode);
11179
11180   CheckLevelTime();
11181
11182   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11183 }
11184
11185 void GameActions_SP_Main()
11186 {
11187   byte effective_action[MAX_PLAYERS];
11188   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11189   int i;
11190
11191   for (i = 0; i < MAX_PLAYERS; i++)
11192     effective_action[i] = stored_player[i].effective_action;
11193
11194   GameActions_SP(effective_action, warp_mode);
11195
11196   CheckLevelTime();
11197
11198   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11199 }
11200
11201 void GameActions_RND()
11202 {
11203   int magic_wall_x = 0, magic_wall_y = 0;
11204   int i, x, y, element, graphic;
11205
11206   InitPlayfieldScanModeVars();
11207
11208   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11209   {
11210     SCAN_PLAYFIELD(x, y)
11211     {
11212       ChangeCount[x][y] = 0;
11213       ChangeEvent[x][y] = -1;
11214     }
11215   }
11216
11217   if (game.set_centered_player)
11218   {
11219     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11220
11221     /* switching to "all players" only possible if all players fit to screen */
11222     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11223     {
11224       game.centered_player_nr_next = game.centered_player_nr;
11225       game.set_centered_player = FALSE;
11226     }
11227
11228     /* do not switch focus to non-existing (or non-active) player */
11229     if (game.centered_player_nr_next >= 0 &&
11230         !stored_player[game.centered_player_nr_next].active)
11231     {
11232       game.centered_player_nr_next = game.centered_player_nr;
11233       game.set_centered_player = FALSE;
11234     }
11235   }
11236
11237   if (game.set_centered_player &&
11238       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11239   {
11240     int sx, sy;
11241
11242     if (game.centered_player_nr_next == -1)
11243     {
11244       setScreenCenteredToAllPlayers(&sx, &sy);
11245     }
11246     else
11247     {
11248       sx = stored_player[game.centered_player_nr_next].jx;
11249       sy = stored_player[game.centered_player_nr_next].jy;
11250     }
11251
11252     game.centered_player_nr = game.centered_player_nr_next;
11253     game.set_centered_player = FALSE;
11254
11255     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11256     DrawGameDoorValues();
11257   }
11258
11259   for (i = 0; i < MAX_PLAYERS; i++)
11260   {
11261     int actual_player_action = stored_player[i].effective_action;
11262
11263 #if 1
11264     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11265        - rnd_equinox_tetrachloride 048
11266        - rnd_equinox_tetrachloride_ii 096
11267        - rnd_emanuel_schmieg 002
11268        - doctor_sloan_ww 001, 020
11269     */
11270     if (stored_player[i].MovPos == 0)
11271       CheckGravityMovement(&stored_player[i]);
11272 #endif
11273
11274     /* overwrite programmed action with tape action */
11275     if (stored_player[i].programmed_action)
11276       actual_player_action = stored_player[i].programmed_action;
11277
11278     PlayerActions(&stored_player[i], actual_player_action);
11279
11280     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11281   }
11282
11283   ScrollScreen(NULL, SCROLL_GO_ON);
11284
11285   /* for backwards compatibility, the following code emulates a fixed bug that
11286      occured when pushing elements (causing elements that just made their last
11287      pushing step to already (if possible) make their first falling step in the
11288      same game frame, which is bad); this code is also needed to use the famous
11289      "spring push bug" which is used in older levels and might be wanted to be
11290      used also in newer levels, but in this case the buggy pushing code is only
11291      affecting the "spring" element and no other elements */
11292
11293   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11294   {
11295     for (i = 0; i < MAX_PLAYERS; i++)
11296     {
11297       struct PlayerInfo *player = &stored_player[i];
11298       int x = player->jx;
11299       int y = player->jy;
11300
11301       if (player->active && player->is_pushing && player->is_moving &&
11302           IS_MOVING(x, y) &&
11303           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11304            Feld[x][y] == EL_SPRING))
11305       {
11306         ContinueMoving(x, y);
11307
11308         /* continue moving after pushing (this is actually a bug) */
11309         if (!IS_MOVING(x, y))
11310           Stop[x][y] = FALSE;
11311       }
11312     }
11313   }
11314
11315   SCAN_PLAYFIELD(x, y)
11316   {
11317     ChangeCount[x][y] = 0;
11318     ChangeEvent[x][y] = -1;
11319
11320     /* this must be handled before main playfield loop */
11321     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11322     {
11323       MovDelay[x][y]--;
11324       if (MovDelay[x][y] <= 0)
11325         RemoveField(x, y);
11326     }
11327
11328     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11329     {
11330       MovDelay[x][y]--;
11331       if (MovDelay[x][y] <= 0)
11332       {
11333         RemoveField(x, y);
11334         TEST_DrawLevelField(x, y);
11335
11336         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11337       }
11338     }
11339
11340 #if DEBUG
11341     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11342     {
11343       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11344       printf("GameActions(): This should never happen!\n");
11345
11346       ChangePage[x][y] = -1;
11347     }
11348 #endif
11349
11350     Stop[x][y] = FALSE;
11351     if (WasJustMoving[x][y] > 0)
11352       WasJustMoving[x][y]--;
11353     if (WasJustFalling[x][y] > 0)
11354       WasJustFalling[x][y]--;
11355     if (CheckCollision[x][y] > 0)
11356       CheckCollision[x][y]--;
11357     if (CheckImpact[x][y] > 0)
11358       CheckImpact[x][y]--;
11359
11360     GfxFrame[x][y]++;
11361
11362     /* reset finished pushing action (not done in ContinueMoving() to allow
11363        continuous pushing animation for elements with zero push delay) */
11364     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11365     {
11366       ResetGfxAnimation(x, y);
11367       TEST_DrawLevelField(x, y);
11368     }
11369
11370 #if DEBUG
11371     if (IS_BLOCKED(x, y))
11372     {
11373       int oldx, oldy;
11374
11375       Blocked2Moving(x, y, &oldx, &oldy);
11376       if (!IS_MOVING(oldx, oldy))
11377       {
11378         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11379         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11380         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11381         printf("GameActions(): This should never happen!\n");
11382       }
11383     }
11384 #endif
11385   }
11386
11387   SCAN_PLAYFIELD(x, y)
11388   {
11389     element = Feld[x][y];
11390     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11391
11392     ResetGfxFrame(x, y, TRUE);
11393
11394     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11395         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11396       ResetRandomAnimationValue(x, y);
11397
11398     SetRandomAnimationValue(x, y);
11399
11400     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11401
11402     if (IS_INACTIVE(element))
11403     {
11404       if (IS_ANIMATED(graphic))
11405         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11406
11407       continue;
11408     }
11409
11410     /* this may take place after moving, so 'element' may have changed */
11411     if (IS_CHANGING(x, y) &&
11412         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11413     {
11414       int page = element_info[element].event_page_nr[CE_DELAY];
11415
11416       HandleElementChange(x, y, page);
11417
11418       element = Feld[x][y];
11419       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11420     }
11421
11422     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11423     {
11424       StartMoving(x, y);
11425
11426       element = Feld[x][y];
11427       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11428
11429       if (IS_ANIMATED(graphic) &&
11430           !IS_MOVING(x, y) &&
11431           !Stop[x][y])
11432         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11433
11434       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11435         TEST_DrawTwinkleOnField(x, y);
11436     }
11437     else if ((element == EL_ACID ||
11438               element == EL_EXIT_OPEN ||
11439               element == EL_EM_EXIT_OPEN ||
11440               element == EL_SP_EXIT_OPEN ||
11441               element == EL_STEEL_EXIT_OPEN ||
11442               element == EL_EM_STEEL_EXIT_OPEN ||
11443               element == EL_SP_TERMINAL ||
11444               element == EL_SP_TERMINAL_ACTIVE ||
11445               element == EL_EXTRA_TIME ||
11446               element == EL_SHIELD_NORMAL ||
11447               element == EL_SHIELD_DEADLY) &&
11448              IS_ANIMATED(graphic))
11449       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11450     else if (IS_MOVING(x, y))
11451       ContinueMoving(x, y);
11452     else if (IS_ACTIVE_BOMB(element))
11453       CheckDynamite(x, y);
11454     else if (element == EL_AMOEBA_GROWING)
11455       AmoebeWaechst(x, y);
11456     else if (element == EL_AMOEBA_SHRINKING)
11457       AmoebaDisappearing(x, y);
11458
11459 #if !USE_NEW_AMOEBA_CODE
11460     else if (IS_AMOEBALIVE(element))
11461       AmoebeAbleger(x, y);
11462 #endif
11463
11464     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11465       Life(x, y);
11466     else if (element == EL_EXIT_CLOSED)
11467       CheckExit(x, y);
11468     else if (element == EL_EM_EXIT_CLOSED)
11469       CheckExitEM(x, y);
11470     else if (element == EL_STEEL_EXIT_CLOSED)
11471       CheckExitSteel(x, y);
11472     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11473       CheckExitSteelEM(x, y);
11474     else if (element == EL_SP_EXIT_CLOSED)
11475       CheckExitSP(x, y);
11476     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11477              element == EL_EXPANDABLE_STEELWALL_GROWING)
11478       MauerWaechst(x, y);
11479     else if (element == EL_EXPANDABLE_WALL ||
11480              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11481              element == EL_EXPANDABLE_WALL_VERTICAL ||
11482              element == EL_EXPANDABLE_WALL_ANY ||
11483              element == EL_BD_EXPANDABLE_WALL)
11484       MauerAbleger(x, y);
11485     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11486              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11487              element == EL_EXPANDABLE_STEELWALL_ANY)
11488       MauerAblegerStahl(x, y);
11489     else if (element == EL_FLAMES)
11490       CheckForDragon(x, y);
11491     else if (element == EL_EXPLOSION)
11492       ; /* drawing of correct explosion animation is handled separately */
11493     else if (element == EL_ELEMENT_SNAPPING ||
11494              element == EL_DIAGONAL_SHRINKING ||
11495              element == EL_DIAGONAL_GROWING)
11496     {
11497       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11498
11499       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11500     }
11501     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11502       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11503
11504     if (IS_BELT_ACTIVE(element))
11505       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11506
11507     if (game.magic_wall_active)
11508     {
11509       int jx = local_player->jx, jy = local_player->jy;
11510
11511       /* play the element sound at the position nearest to the player */
11512       if ((element == EL_MAGIC_WALL_FULL ||
11513            element == EL_MAGIC_WALL_ACTIVE ||
11514            element == EL_MAGIC_WALL_EMPTYING ||
11515            element == EL_BD_MAGIC_WALL_FULL ||
11516            element == EL_BD_MAGIC_WALL_ACTIVE ||
11517            element == EL_BD_MAGIC_WALL_EMPTYING ||
11518            element == EL_DC_MAGIC_WALL_FULL ||
11519            element == EL_DC_MAGIC_WALL_ACTIVE ||
11520            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11521           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11522       {
11523         magic_wall_x = x;
11524         magic_wall_y = y;
11525       }
11526     }
11527   }
11528
11529 #if USE_NEW_AMOEBA_CODE
11530   /* new experimental amoeba growth stuff */
11531   if (!(FrameCounter % 8))
11532   {
11533     static unsigned int random = 1684108901;
11534
11535     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11536     {
11537       x = RND(lev_fieldx);
11538       y = RND(lev_fieldy);
11539       element = Feld[x][y];
11540
11541       if (!IS_PLAYER(x,y) &&
11542           (element == EL_EMPTY ||
11543            CAN_GROW_INTO(element) ||
11544            element == EL_QUICKSAND_EMPTY ||
11545            element == EL_QUICKSAND_FAST_EMPTY ||
11546            element == EL_ACID_SPLASH_LEFT ||
11547            element == EL_ACID_SPLASH_RIGHT))
11548       {
11549         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11550             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11551             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11552             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11553           Feld[x][y] = EL_AMOEBA_DROP;
11554       }
11555
11556       random = random * 129 + 1;
11557     }
11558   }
11559 #endif
11560
11561   game.explosions_delayed = FALSE;
11562
11563   SCAN_PLAYFIELD(x, y)
11564   {
11565     element = Feld[x][y];
11566
11567     if (ExplodeField[x][y])
11568       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11569     else if (element == EL_EXPLOSION)
11570       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11571
11572     ExplodeField[x][y] = EX_TYPE_NONE;
11573   }
11574
11575   game.explosions_delayed = TRUE;
11576
11577   if (game.magic_wall_active)
11578   {
11579     if (!(game.magic_wall_time_left % 4))
11580     {
11581       int element = Feld[magic_wall_x][magic_wall_y];
11582
11583       if (element == EL_BD_MAGIC_WALL_FULL ||
11584           element == EL_BD_MAGIC_WALL_ACTIVE ||
11585           element == EL_BD_MAGIC_WALL_EMPTYING)
11586         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11587       else if (element == EL_DC_MAGIC_WALL_FULL ||
11588                element == EL_DC_MAGIC_WALL_ACTIVE ||
11589                element == EL_DC_MAGIC_WALL_EMPTYING)
11590         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11591       else
11592         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11593     }
11594
11595     if (game.magic_wall_time_left > 0)
11596     {
11597       game.magic_wall_time_left--;
11598
11599       if (!game.magic_wall_time_left)
11600       {
11601         SCAN_PLAYFIELD(x, y)
11602         {
11603           element = Feld[x][y];
11604
11605           if (element == EL_MAGIC_WALL_ACTIVE ||
11606               element == EL_MAGIC_WALL_FULL)
11607           {
11608             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11609             TEST_DrawLevelField(x, y);
11610           }
11611           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11612                    element == EL_BD_MAGIC_WALL_FULL)
11613           {
11614             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11615             TEST_DrawLevelField(x, y);
11616           }
11617           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11618                    element == EL_DC_MAGIC_WALL_FULL)
11619           {
11620             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11621             TEST_DrawLevelField(x, y);
11622           }
11623         }
11624
11625         game.magic_wall_active = FALSE;
11626       }
11627     }
11628   }
11629
11630   if (game.light_time_left > 0)
11631   {
11632     game.light_time_left--;
11633
11634     if (game.light_time_left == 0)
11635       RedrawAllLightSwitchesAndInvisibleElements();
11636   }
11637
11638   if (game.timegate_time_left > 0)
11639   {
11640     game.timegate_time_left--;
11641
11642     if (game.timegate_time_left == 0)
11643       CloseAllOpenTimegates();
11644   }
11645
11646   if (game.lenses_time_left > 0)
11647   {
11648     game.lenses_time_left--;
11649
11650     if (game.lenses_time_left == 0)
11651       RedrawAllInvisibleElementsForLenses();
11652   }
11653
11654   if (game.magnify_time_left > 0)
11655   {
11656     game.magnify_time_left--;
11657
11658     if (game.magnify_time_left == 0)
11659       RedrawAllInvisibleElementsForMagnifier();
11660   }
11661
11662   for (i = 0; i < MAX_PLAYERS; i++)
11663   {
11664     struct PlayerInfo *player = &stored_player[i];
11665
11666     if (SHIELD_ON(player))
11667     {
11668       if (player->shield_deadly_time_left)
11669         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11670       else if (player->shield_normal_time_left)
11671         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11672     }
11673   }
11674
11675 #if USE_DELAYED_GFX_REDRAW
11676   SCAN_PLAYFIELD(x, y)
11677   {
11678     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11679     {
11680       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11681          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11682
11683       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11684         DrawLevelField(x, y);
11685
11686       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11687         DrawLevelFieldCrumbled(x, y);
11688
11689       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11690         DrawLevelFieldCrumbledNeighbours(x, y);
11691
11692       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11693         DrawTwinkleOnField(x, y);
11694     }
11695
11696     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11697   }
11698 #endif
11699
11700   CheckLevelTime();
11701
11702   DrawAllPlayers();
11703   PlayAllPlayersSound();
11704
11705   if (options.debug)                    /* calculate frames per second */
11706   {
11707     static unsigned int fps_counter = 0;
11708     static int fps_frames = 0;
11709     unsigned int fps_delay_ms = Counter() - fps_counter;
11710
11711     fps_frames++;
11712
11713     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11714     {
11715       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11716
11717       fps_frames = 0;
11718       fps_counter = Counter();
11719     }
11720
11721     redraw_mask |= REDRAW_FPS;
11722   }
11723
11724   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11725
11726   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11727   {
11728     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11729
11730     local_player->show_envelope = 0;
11731   }
11732
11733   /* use random number generator in every frame to make it less predictable */
11734   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11735     RND(1);
11736 }
11737
11738 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11739 {
11740   int min_x = x, min_y = y, max_x = x, max_y = y;
11741   int i;
11742
11743   for (i = 0; i < MAX_PLAYERS; i++)
11744   {
11745     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11746
11747     if (!stored_player[i].active || &stored_player[i] == player)
11748       continue;
11749
11750     min_x = MIN(min_x, jx);
11751     min_y = MIN(min_y, jy);
11752     max_x = MAX(max_x, jx);
11753     max_y = MAX(max_y, jy);
11754   }
11755
11756   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11757 }
11758
11759 static boolean AllPlayersInVisibleScreen()
11760 {
11761   int i;
11762
11763   for (i = 0; i < MAX_PLAYERS; i++)
11764   {
11765     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11766
11767     if (!stored_player[i].active)
11768       continue;
11769
11770     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11771       return FALSE;
11772   }
11773
11774   return TRUE;
11775 }
11776
11777 void ScrollLevel(int dx, int dy)
11778 {
11779   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11780   int x, y;
11781
11782   BlitBitmap(drawto_field, drawto_field,
11783              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11784              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11785              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11786              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11787              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11788              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11789
11790   if (dx != 0)
11791   {
11792     x = (dx == 1 ? BX1 : BX2);
11793     for (y = BY1; y <= BY2; y++)
11794       DrawScreenField(x, y);
11795   }
11796
11797   if (dy != 0)
11798   {
11799     y = (dy == 1 ? BY1 : BY2);
11800     for (x = BX1; x <= BX2; x++)
11801       DrawScreenField(x, y);
11802   }
11803
11804   redraw_mask |= REDRAW_FIELD;
11805 }
11806
11807 static boolean canFallDown(struct PlayerInfo *player)
11808 {
11809   int jx = player->jx, jy = player->jy;
11810
11811   return (IN_LEV_FIELD(jx, jy + 1) &&
11812           (IS_FREE(jx, jy + 1) ||
11813            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11814           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11815           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11816 }
11817
11818 static boolean canPassField(int x, int y, int move_dir)
11819 {
11820   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11821   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11822   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11823   int nextx = x + dx;
11824   int nexty = y + dy;
11825   int element = Feld[x][y];
11826
11827   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11828           !CAN_MOVE(element) &&
11829           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11830           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11831           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11832 }
11833
11834 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11835 {
11836   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11837   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11838   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11839   int newx = x + dx;
11840   int newy = y + dy;
11841
11842   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11843           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11844           (IS_DIGGABLE(Feld[newx][newy]) ||
11845            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11846            canPassField(newx, newy, move_dir)));
11847 }
11848
11849 static void CheckGravityMovement(struct PlayerInfo *player)
11850 {
11851   if (player->gravity && !player->programmed_action)
11852   {
11853     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11854     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11855     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11856     int jx = player->jx, jy = player->jy;
11857     boolean player_is_moving_to_valid_field =
11858       (!player_is_snapping &&
11859        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11860         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11861     boolean player_can_fall_down = canFallDown(player);
11862
11863     if (player_can_fall_down &&
11864         !player_is_moving_to_valid_field)
11865       player->programmed_action = MV_DOWN;
11866   }
11867 }
11868
11869 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11870 {
11871   return CheckGravityMovement(player);
11872
11873   if (player->gravity && !player->programmed_action)
11874   {
11875     int jx = player->jx, jy = player->jy;
11876     boolean field_under_player_is_free =
11877       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11878     boolean player_is_standing_on_valid_field =
11879       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11880        (IS_WALKABLE(Feld[jx][jy]) &&
11881         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11882
11883     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11884       player->programmed_action = MV_DOWN;
11885   }
11886 }
11887
11888 /*
11889   MovePlayerOneStep()
11890   -----------------------------------------------------------------------------
11891   dx, dy:               direction (non-diagonal) to try to move the player to
11892   real_dx, real_dy:     direction as read from input device (can be diagonal)
11893 */
11894
11895 boolean MovePlayerOneStep(struct PlayerInfo *player,
11896                           int dx, int dy, int real_dx, int real_dy)
11897 {
11898   int jx = player->jx, jy = player->jy;
11899   int new_jx = jx + dx, new_jy = jy + dy;
11900   int can_move;
11901   boolean player_can_move = !player->cannot_move;
11902
11903   if (!player->active || (!dx && !dy))
11904     return MP_NO_ACTION;
11905
11906   player->MovDir = (dx < 0 ? MV_LEFT :
11907                     dx > 0 ? MV_RIGHT :
11908                     dy < 0 ? MV_UP :
11909                     dy > 0 ? MV_DOWN :  MV_NONE);
11910
11911   if (!IN_LEV_FIELD(new_jx, new_jy))
11912     return MP_NO_ACTION;
11913
11914   if (!player_can_move)
11915   {
11916     if (player->MovPos == 0)
11917     {
11918       player->is_moving = FALSE;
11919       player->is_digging = FALSE;
11920       player->is_collecting = FALSE;
11921       player->is_snapping = FALSE;
11922       player->is_pushing = FALSE;
11923     }
11924   }
11925
11926   if (!options.network && game.centered_player_nr == -1 &&
11927       !AllPlayersInSight(player, new_jx, new_jy))
11928     return MP_NO_ACTION;
11929
11930   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11931   if (can_move != MP_MOVING)
11932     return can_move;
11933
11934   /* check if DigField() has caused relocation of the player */
11935   if (player->jx != jx || player->jy != jy)
11936     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11937
11938   StorePlayer[jx][jy] = 0;
11939   player->last_jx = jx;
11940   player->last_jy = jy;
11941   player->jx = new_jx;
11942   player->jy = new_jy;
11943   StorePlayer[new_jx][new_jy] = player->element_nr;
11944
11945   if (player->move_delay_value_next != -1)
11946   {
11947     player->move_delay_value = player->move_delay_value_next;
11948     player->move_delay_value_next = -1;
11949   }
11950
11951   player->MovPos =
11952     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11953
11954   player->step_counter++;
11955
11956   PlayerVisit[jx][jy] = FrameCounter;
11957
11958   player->is_moving = TRUE;
11959
11960 #if 1
11961   /* should better be called in MovePlayer(), but this breaks some tapes */
11962   ScrollPlayer(player, SCROLL_INIT);
11963 #endif
11964
11965   return MP_MOVING;
11966 }
11967
11968 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11969 {
11970   int jx = player->jx, jy = player->jy;
11971   int old_jx = jx, old_jy = jy;
11972   int moved = MP_NO_ACTION;
11973
11974   if (!player->active)
11975     return FALSE;
11976
11977   if (!dx && !dy)
11978   {
11979     if (player->MovPos == 0)
11980     {
11981       player->is_moving = FALSE;
11982       player->is_digging = FALSE;
11983       player->is_collecting = FALSE;
11984       player->is_snapping = FALSE;
11985       player->is_pushing = FALSE;
11986     }
11987
11988     return FALSE;
11989   }
11990
11991   if (player->move_delay > 0)
11992     return FALSE;
11993
11994   player->move_delay = -1;              /* set to "uninitialized" value */
11995
11996   /* store if player is automatically moved to next field */
11997   player->is_auto_moving = (player->programmed_action != MV_NONE);
11998
11999   /* remove the last programmed player action */
12000   player->programmed_action = 0;
12001
12002   if (player->MovPos)
12003   {
12004     /* should only happen if pre-1.2 tape recordings are played */
12005     /* this is only for backward compatibility */
12006
12007     int original_move_delay_value = player->move_delay_value;
12008
12009 #if DEBUG
12010     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12011            tape.counter);
12012 #endif
12013
12014     /* scroll remaining steps with finest movement resolution */
12015     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12016
12017     while (player->MovPos)
12018     {
12019       ScrollPlayer(player, SCROLL_GO_ON);
12020       ScrollScreen(NULL, SCROLL_GO_ON);
12021
12022       AdvanceFrameAndPlayerCounters(player->index_nr);
12023
12024       DrawAllPlayers();
12025       BackToFront();
12026     }
12027
12028     player->move_delay_value = original_move_delay_value;
12029   }
12030
12031   player->is_active = FALSE;
12032
12033   if (player->last_move_dir & MV_HORIZONTAL)
12034   {
12035     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12036       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12037   }
12038   else
12039   {
12040     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12041       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12042   }
12043
12044   if (!moved && !player->is_active)
12045   {
12046     player->is_moving = FALSE;
12047     player->is_digging = FALSE;
12048     player->is_collecting = FALSE;
12049     player->is_snapping = FALSE;
12050     player->is_pushing = FALSE;
12051   }
12052
12053   jx = player->jx;
12054   jy = player->jy;
12055
12056   if (moved & MP_MOVING && !ScreenMovPos &&
12057       (player->index_nr == game.centered_player_nr ||
12058        game.centered_player_nr == -1))
12059   {
12060     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12061     int offset = game.scroll_delay_value;
12062
12063     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12064     {
12065       /* actual player has left the screen -- scroll in that direction */
12066       if (jx != old_jx)         /* player has moved horizontally */
12067         scroll_x += (jx - old_jx);
12068       else                      /* player has moved vertically */
12069         scroll_y += (jy - old_jy);
12070     }
12071     else
12072     {
12073       if (jx != old_jx)         /* player has moved horizontally */
12074       {
12075         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12076             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12077           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12078
12079         /* don't scroll over playfield boundaries */
12080         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12081           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12082
12083         /* don't scroll more than one field at a time */
12084         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12085
12086         /* don't scroll against the player's moving direction */
12087         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12088             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12089           scroll_x = old_scroll_x;
12090       }
12091       else                      /* player has moved vertically */
12092       {
12093         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12094             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12095           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12096
12097         /* don't scroll over playfield boundaries */
12098         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12099           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12100
12101         /* don't scroll more than one field at a time */
12102         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12103
12104         /* don't scroll against the player's moving direction */
12105         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12106             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12107           scroll_y = old_scroll_y;
12108       }
12109     }
12110
12111     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12112     {
12113       if (!options.network && game.centered_player_nr == -1 &&
12114           !AllPlayersInVisibleScreen())
12115       {
12116         scroll_x = old_scroll_x;
12117         scroll_y = old_scroll_y;
12118       }
12119       else
12120       {
12121         ScrollScreen(player, SCROLL_INIT);
12122         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12123       }
12124     }
12125   }
12126
12127   player->StepFrame = 0;
12128
12129   if (moved & MP_MOVING)
12130   {
12131     if (old_jx != jx && old_jy == jy)
12132       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12133     else if (old_jx == jx && old_jy != jy)
12134       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12135
12136     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12137
12138     player->last_move_dir = player->MovDir;
12139     player->is_moving = TRUE;
12140     player->is_snapping = FALSE;
12141     player->is_switching = FALSE;
12142     player->is_dropping = FALSE;
12143     player->is_dropping_pressed = FALSE;
12144     player->drop_pressed_delay = 0;
12145
12146 #if 0
12147     /* should better be called here than above, but this breaks some tapes */
12148     ScrollPlayer(player, SCROLL_INIT);
12149 #endif
12150   }
12151   else
12152   {
12153     CheckGravityMovementWhenNotMoving(player);
12154
12155     player->is_moving = FALSE;
12156
12157     /* at this point, the player is allowed to move, but cannot move right now
12158        (e.g. because of something blocking the way) -- ensure that the player
12159        is also allowed to move in the next frame (in old versions before 3.1.1,
12160        the player was forced to wait again for eight frames before next try) */
12161
12162     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12163       player->move_delay = 0;   /* allow direct movement in the next frame */
12164   }
12165
12166   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12167     player->move_delay = player->move_delay_value;
12168
12169   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12170   {
12171     TestIfPlayerTouchesBadThing(jx, jy);
12172     TestIfPlayerTouchesCustomElement(jx, jy);
12173   }
12174
12175   if (!player->active)
12176     RemovePlayer(player);
12177
12178   return moved;
12179 }
12180
12181 void ScrollPlayer(struct PlayerInfo *player, int mode)
12182 {
12183   int jx = player->jx, jy = player->jy;
12184   int last_jx = player->last_jx, last_jy = player->last_jy;
12185   int move_stepsize = TILEX / player->move_delay_value;
12186
12187   if (!player->active)
12188     return;
12189
12190   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12191     return;
12192
12193   if (mode == SCROLL_INIT)
12194   {
12195     player->actual_frame_counter = FrameCounter;
12196     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12197
12198     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12199         Feld[last_jx][last_jy] == EL_EMPTY)
12200     {
12201       int last_field_block_delay = 0;   /* start with no blocking at all */
12202       int block_delay_adjustment = player->block_delay_adjustment;
12203
12204       /* if player blocks last field, add delay for exactly one move */
12205       if (player->block_last_field)
12206       {
12207         last_field_block_delay += player->move_delay_value;
12208
12209         /* when blocking enabled, prevent moving up despite gravity */
12210         if (player->gravity && player->MovDir == MV_UP)
12211           block_delay_adjustment = -1;
12212       }
12213
12214       /* add block delay adjustment (also possible when not blocking) */
12215       last_field_block_delay += block_delay_adjustment;
12216
12217       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12218       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12219     }
12220
12221     if (player->MovPos != 0)    /* player has not yet reached destination */
12222       return;
12223   }
12224   else if (!FrameReached(&player->actual_frame_counter, 1))
12225     return;
12226
12227   if (player->MovPos != 0)
12228   {
12229     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12230     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12231
12232     /* before DrawPlayer() to draw correct player graphic for this case */
12233     if (player->MovPos == 0)
12234       CheckGravityMovement(player);
12235   }
12236
12237   if (player->MovPos == 0)      /* player reached destination field */
12238   {
12239     if (player->move_delay_reset_counter > 0)
12240     {
12241       player->move_delay_reset_counter--;
12242
12243       if (player->move_delay_reset_counter == 0)
12244       {
12245         /* continue with normal speed after quickly moving through gate */
12246         HALVE_PLAYER_SPEED(player);
12247
12248         /* be able to make the next move without delay */
12249         player->move_delay = 0;
12250       }
12251     }
12252
12253     player->last_jx = jx;
12254     player->last_jy = jy;
12255
12256     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12257         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12258         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12259         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12260         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12261         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12262         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12263         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12264     {
12265       DrawPlayer(player);       /* needed here only to cleanup last field */
12266       RemovePlayer(player);
12267
12268       if (local_player->friends_still_needed == 0 ||
12269           IS_SP_ELEMENT(Feld[jx][jy]))
12270         PlayerWins(player);
12271     }
12272
12273     /* this breaks one level: "machine", level 000 */
12274     {
12275       int move_direction = player->MovDir;
12276       int enter_side = MV_DIR_OPPOSITE(move_direction);
12277       int leave_side = move_direction;
12278       int old_jx = last_jx;
12279       int old_jy = last_jy;
12280       int old_element = Feld[old_jx][old_jy];
12281       int new_element = Feld[jx][jy];
12282
12283       if (IS_CUSTOM_ELEMENT(old_element))
12284         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12285                                    CE_LEFT_BY_PLAYER,
12286                                    player->index_bit, leave_side);
12287
12288       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12289                                           CE_PLAYER_LEAVES_X,
12290                                           player->index_bit, leave_side);
12291
12292       if (IS_CUSTOM_ELEMENT(new_element))
12293         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12294                                    player->index_bit, enter_side);
12295
12296       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12297                                           CE_PLAYER_ENTERS_X,
12298                                           player->index_bit, enter_side);
12299
12300       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12301                                         CE_MOVE_OF_X, move_direction);
12302     }
12303
12304     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12305     {
12306       TestIfPlayerTouchesBadThing(jx, jy);
12307       TestIfPlayerTouchesCustomElement(jx, jy);
12308
12309       /* needed because pushed element has not yet reached its destination,
12310          so it would trigger a change event at its previous field location */
12311       if (!player->is_pushing)
12312         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12313
12314       if (!player->active)
12315         RemovePlayer(player);
12316     }
12317
12318     if (!local_player->LevelSolved && level.use_step_counter)
12319     {
12320       int i;
12321
12322       TimePlayed++;
12323
12324       if (TimeLeft > 0)
12325       {
12326         TimeLeft--;
12327
12328         if (TimeLeft <= 10 && setup.time_limit)
12329           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12330
12331         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12332
12333         DisplayGameControlValues();
12334
12335         if (!TimeLeft && setup.time_limit)
12336           for (i = 0; i < MAX_PLAYERS; i++)
12337             KillPlayer(&stored_player[i]);
12338       }
12339       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12340       {
12341         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12342
12343         DisplayGameControlValues();
12344       }
12345     }
12346
12347     if (tape.single_step && tape.recording && !tape.pausing &&
12348         !player->programmed_action)
12349       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12350
12351     if (!player->programmed_action)
12352       CheckSaveEngineSnapshot(player);
12353   }
12354 }
12355
12356 void ScrollScreen(struct PlayerInfo *player, int mode)
12357 {
12358   static unsigned int screen_frame_counter = 0;
12359
12360   if (mode == SCROLL_INIT)
12361   {
12362     /* set scrolling step size according to actual player's moving speed */
12363     ScrollStepSize = TILEX / player->move_delay_value;
12364
12365     screen_frame_counter = FrameCounter;
12366     ScreenMovDir = player->MovDir;
12367     ScreenMovPos = player->MovPos;
12368     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12369     return;
12370   }
12371   else if (!FrameReached(&screen_frame_counter, 1))
12372     return;
12373
12374   if (ScreenMovPos)
12375   {
12376     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12377     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12378     redraw_mask |= REDRAW_FIELD;
12379   }
12380   else
12381     ScreenMovDir = MV_NONE;
12382 }
12383
12384 void TestIfPlayerTouchesCustomElement(int x, int y)
12385 {
12386   static int xy[4][2] =
12387   {
12388     { 0, -1 },
12389     { -1, 0 },
12390     { +1, 0 },
12391     { 0, +1 }
12392   };
12393   static int trigger_sides[4][2] =
12394   {
12395     /* center side       border side */
12396     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12397     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12398     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12399     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12400   };
12401   static int touch_dir[4] =
12402   {
12403     MV_LEFT | MV_RIGHT,
12404     MV_UP   | MV_DOWN,
12405     MV_UP   | MV_DOWN,
12406     MV_LEFT | MV_RIGHT
12407   };
12408   int center_element = Feld[x][y];      /* should always be non-moving! */
12409   int i;
12410
12411   for (i = 0; i < NUM_DIRECTIONS; i++)
12412   {
12413     int xx = x + xy[i][0];
12414     int yy = y + xy[i][1];
12415     int center_side = trigger_sides[i][0];
12416     int border_side = trigger_sides[i][1];
12417     int border_element;
12418
12419     if (!IN_LEV_FIELD(xx, yy))
12420       continue;
12421
12422     if (IS_PLAYER(x, y))                /* player found at center element */
12423     {
12424       struct PlayerInfo *player = PLAYERINFO(x, y);
12425
12426       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12427         border_element = Feld[xx][yy];          /* may be moving! */
12428       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12429         border_element = Feld[xx][yy];
12430       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12431         border_element = MovingOrBlocked2Element(xx, yy);
12432       else
12433         continue;               /* center and border element do not touch */
12434
12435       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12436                                  player->index_bit, border_side);
12437       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12438                                           CE_PLAYER_TOUCHES_X,
12439                                           player->index_bit, border_side);
12440
12441       {
12442         /* use player element that is initially defined in the level playfield,
12443            not the player element that corresponds to the runtime player number
12444            (example: a level that contains EL_PLAYER_3 as the only player would
12445            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12446         int player_element = PLAYERINFO(x, y)->initial_element;
12447
12448         CheckElementChangeBySide(xx, yy, border_element, player_element,
12449                                  CE_TOUCHING_X, border_side);
12450       }
12451     }
12452     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12453     {
12454       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12455
12456       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12457       {
12458         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12459           continue;             /* center and border element do not touch */
12460       }
12461
12462       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12463                                  player->index_bit, center_side);
12464       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12465                                           CE_PLAYER_TOUCHES_X,
12466                                           player->index_bit, center_side);
12467
12468       {
12469         /* use player element that is initially defined in the level playfield,
12470            not the player element that corresponds to the runtime player number
12471            (example: a level that contains EL_PLAYER_3 as the only player would
12472            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12473         int player_element = PLAYERINFO(xx, yy)->initial_element;
12474
12475         CheckElementChangeBySide(x, y, center_element, player_element,
12476                                  CE_TOUCHING_X, center_side);
12477       }
12478
12479       break;
12480     }
12481   }
12482 }
12483
12484 void TestIfElementTouchesCustomElement(int x, int y)
12485 {
12486   static int xy[4][2] =
12487   {
12488     { 0, -1 },
12489     { -1, 0 },
12490     { +1, 0 },
12491     { 0, +1 }
12492   };
12493   static int trigger_sides[4][2] =
12494   {
12495     /* center side      border side */
12496     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12497     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12498     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12499     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12500   };
12501   static int touch_dir[4] =
12502   {
12503     MV_LEFT | MV_RIGHT,
12504     MV_UP   | MV_DOWN,
12505     MV_UP   | MV_DOWN,
12506     MV_LEFT | MV_RIGHT
12507   };
12508   boolean change_center_element = FALSE;
12509   int center_element = Feld[x][y];      /* should always be non-moving! */
12510   int border_element_old[NUM_DIRECTIONS];
12511   int i;
12512
12513   for (i = 0; i < NUM_DIRECTIONS; i++)
12514   {
12515     int xx = x + xy[i][0];
12516     int yy = y + xy[i][1];
12517     int border_element;
12518
12519     border_element_old[i] = -1;
12520
12521     if (!IN_LEV_FIELD(xx, yy))
12522       continue;
12523
12524     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12525       border_element = Feld[xx][yy];    /* may be moving! */
12526     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12527       border_element = Feld[xx][yy];
12528     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12529       border_element = MovingOrBlocked2Element(xx, yy);
12530     else
12531       continue;                 /* center and border element do not touch */
12532
12533     border_element_old[i] = border_element;
12534   }
12535
12536   for (i = 0; i < NUM_DIRECTIONS; i++)
12537   {
12538     int xx = x + xy[i][0];
12539     int yy = y + xy[i][1];
12540     int center_side = trigger_sides[i][0];
12541     int border_element = border_element_old[i];
12542
12543     if (border_element == -1)
12544       continue;
12545
12546     /* check for change of border element */
12547     CheckElementChangeBySide(xx, yy, border_element, center_element,
12548                              CE_TOUCHING_X, center_side);
12549
12550     /* (center element cannot be player, so we dont have to check this here) */
12551   }
12552
12553   for (i = 0; i < NUM_DIRECTIONS; i++)
12554   {
12555     int xx = x + xy[i][0];
12556     int yy = y + xy[i][1];
12557     int border_side = trigger_sides[i][1];
12558     int border_element = border_element_old[i];
12559
12560     if (border_element == -1)
12561       continue;
12562
12563     /* check for change of center element (but change it only once) */
12564     if (!change_center_element)
12565       change_center_element =
12566         CheckElementChangeBySide(x, y, center_element, border_element,
12567                                  CE_TOUCHING_X, border_side);
12568
12569     if (IS_PLAYER(xx, yy))
12570     {
12571       /* use player element that is initially defined in the level playfield,
12572          not the player element that corresponds to the runtime player number
12573          (example: a level that contains EL_PLAYER_3 as the only player would
12574          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12575       int player_element = PLAYERINFO(xx, yy)->initial_element;
12576
12577       CheckElementChangeBySide(x, y, center_element, player_element,
12578                                CE_TOUCHING_X, border_side);
12579     }
12580   }
12581 }
12582
12583 void TestIfElementHitsCustomElement(int x, int y, int direction)
12584 {
12585   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12586   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12587   int hitx = x + dx, hity = y + dy;
12588   int hitting_element = Feld[x][y];
12589   int touched_element;
12590
12591   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12592     return;
12593
12594   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12595                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12596
12597   if (IN_LEV_FIELD(hitx, hity))
12598   {
12599     int opposite_direction = MV_DIR_OPPOSITE(direction);
12600     int hitting_side = direction;
12601     int touched_side = opposite_direction;
12602     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12603                           MovDir[hitx][hity] != direction ||
12604                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12605
12606     object_hit = TRUE;
12607
12608     if (object_hit)
12609     {
12610       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12611                                CE_HITTING_X, touched_side);
12612
12613       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12614                                CE_HIT_BY_X, hitting_side);
12615
12616       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12617                                CE_HIT_BY_SOMETHING, opposite_direction);
12618
12619       if (IS_PLAYER(hitx, hity))
12620       {
12621         /* use player element that is initially defined in the level playfield,
12622            not the player element that corresponds to the runtime player number
12623            (example: a level that contains EL_PLAYER_3 as the only player would
12624            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12625         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12626
12627         CheckElementChangeBySide(x, y, hitting_element, player_element,
12628                                  CE_HITTING_X, touched_side);
12629       }
12630     }
12631   }
12632
12633   /* "hitting something" is also true when hitting the playfield border */
12634   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12635                            CE_HITTING_SOMETHING, direction);
12636 }
12637
12638 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12639 {
12640   int i, kill_x = -1, kill_y = -1;
12641
12642   int bad_element = -1;
12643   static int test_xy[4][2] =
12644   {
12645     { 0, -1 },
12646     { -1, 0 },
12647     { +1, 0 },
12648     { 0, +1 }
12649   };
12650   static int test_dir[4] =
12651   {
12652     MV_UP,
12653     MV_LEFT,
12654     MV_RIGHT,
12655     MV_DOWN
12656   };
12657
12658   for (i = 0; i < NUM_DIRECTIONS; i++)
12659   {
12660     int test_x, test_y, test_move_dir, test_element;
12661
12662     test_x = good_x + test_xy[i][0];
12663     test_y = good_y + test_xy[i][1];
12664
12665     if (!IN_LEV_FIELD(test_x, test_y))
12666       continue;
12667
12668     test_move_dir =
12669       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12670
12671     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12672
12673     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12674        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12675     */
12676     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12677         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12678     {
12679       kill_x = test_x;
12680       kill_y = test_y;
12681       bad_element = test_element;
12682
12683       break;
12684     }
12685   }
12686
12687   if (kill_x != -1 || kill_y != -1)
12688   {
12689     if (IS_PLAYER(good_x, good_y))
12690     {
12691       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12692
12693       if (player->shield_deadly_time_left > 0 &&
12694           !IS_INDESTRUCTIBLE(bad_element))
12695         Bang(kill_x, kill_y);
12696       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12697         KillPlayer(player);
12698     }
12699     else
12700       Bang(good_x, good_y);
12701   }
12702 }
12703
12704 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12705 {
12706   int i, kill_x = -1, kill_y = -1;
12707   int bad_element = Feld[bad_x][bad_y];
12708   static int test_xy[4][2] =
12709   {
12710     { 0, -1 },
12711     { -1, 0 },
12712     { +1, 0 },
12713     { 0, +1 }
12714   };
12715   static int touch_dir[4] =
12716   {
12717     MV_LEFT | MV_RIGHT,
12718     MV_UP   | MV_DOWN,
12719     MV_UP   | MV_DOWN,
12720     MV_LEFT | MV_RIGHT
12721   };
12722   static int test_dir[4] =
12723   {
12724     MV_UP,
12725     MV_LEFT,
12726     MV_RIGHT,
12727     MV_DOWN
12728   };
12729
12730   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12731     return;
12732
12733   for (i = 0; i < NUM_DIRECTIONS; i++)
12734   {
12735     int test_x, test_y, test_move_dir, test_element;
12736
12737     test_x = bad_x + test_xy[i][0];
12738     test_y = bad_y + test_xy[i][1];
12739
12740     if (!IN_LEV_FIELD(test_x, test_y))
12741       continue;
12742
12743     test_move_dir =
12744       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12745
12746     test_element = Feld[test_x][test_y];
12747
12748     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12749        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12750     */
12751     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12752         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12753     {
12754       /* good thing is player or penguin that does not move away */
12755       if (IS_PLAYER(test_x, test_y))
12756       {
12757         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12758
12759         if (bad_element == EL_ROBOT && player->is_moving)
12760           continue;     /* robot does not kill player if he is moving */
12761
12762         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12763         {
12764           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12765             continue;           /* center and border element do not touch */
12766         }
12767
12768         kill_x = test_x;
12769         kill_y = test_y;
12770
12771         break;
12772       }
12773       else if (test_element == EL_PENGUIN)
12774       {
12775         kill_x = test_x;
12776         kill_y = test_y;
12777
12778         break;
12779       }
12780     }
12781   }
12782
12783   if (kill_x != -1 || kill_y != -1)
12784   {
12785     if (IS_PLAYER(kill_x, kill_y))
12786     {
12787       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12788
12789       if (player->shield_deadly_time_left > 0 &&
12790           !IS_INDESTRUCTIBLE(bad_element))
12791         Bang(bad_x, bad_y);
12792       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12793         KillPlayer(player);
12794     }
12795     else
12796       Bang(kill_x, kill_y);
12797   }
12798 }
12799
12800 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12801 {
12802   int bad_element = Feld[bad_x][bad_y];
12803   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12804   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12805   int test_x = bad_x + dx, test_y = bad_y + dy;
12806   int test_move_dir, test_element;
12807   int kill_x = -1, kill_y = -1;
12808
12809   if (!IN_LEV_FIELD(test_x, test_y))
12810     return;
12811
12812   test_move_dir =
12813     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12814
12815   test_element = Feld[test_x][test_y];
12816
12817   if (test_move_dir != bad_move_dir)
12818   {
12819     /* good thing can be player or penguin that does not move away */
12820     if (IS_PLAYER(test_x, test_y))
12821     {
12822       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12823
12824       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12825          player as being hit when he is moving towards the bad thing, because
12826          the "get hit by" condition would be lost after the player stops) */
12827       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12828         return;         /* player moves away from bad thing */
12829
12830       kill_x = test_x;
12831       kill_y = test_y;
12832     }
12833     else if (test_element == EL_PENGUIN)
12834     {
12835       kill_x = test_x;
12836       kill_y = test_y;
12837     }
12838   }
12839
12840   if (kill_x != -1 || kill_y != -1)
12841   {
12842     if (IS_PLAYER(kill_x, kill_y))
12843     {
12844       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12845
12846       if (player->shield_deadly_time_left > 0 &&
12847           !IS_INDESTRUCTIBLE(bad_element))
12848         Bang(bad_x, bad_y);
12849       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12850         KillPlayer(player);
12851     }
12852     else
12853       Bang(kill_x, kill_y);
12854   }
12855 }
12856
12857 void TestIfPlayerTouchesBadThing(int x, int y)
12858 {
12859   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12860 }
12861
12862 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12863 {
12864   TestIfGoodThingHitsBadThing(x, y, move_dir);
12865 }
12866
12867 void TestIfBadThingTouchesPlayer(int x, int y)
12868 {
12869   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12870 }
12871
12872 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12873 {
12874   TestIfBadThingHitsGoodThing(x, y, move_dir);
12875 }
12876
12877 void TestIfFriendTouchesBadThing(int x, int y)
12878 {
12879   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12880 }
12881
12882 void TestIfBadThingTouchesFriend(int x, int y)
12883 {
12884   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12885 }
12886
12887 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12888 {
12889   int i, kill_x = bad_x, kill_y = bad_y;
12890   static int xy[4][2] =
12891   {
12892     { 0, -1 },
12893     { -1, 0 },
12894     { +1, 0 },
12895     { 0, +1 }
12896   };
12897
12898   for (i = 0; i < NUM_DIRECTIONS; i++)
12899   {
12900     int x, y, element;
12901
12902     x = bad_x + xy[i][0];
12903     y = bad_y + xy[i][1];
12904     if (!IN_LEV_FIELD(x, y))
12905       continue;
12906
12907     element = Feld[x][y];
12908     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12909         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12910     {
12911       kill_x = x;
12912       kill_y = y;
12913       break;
12914     }
12915   }
12916
12917   if (kill_x != bad_x || kill_y != bad_y)
12918     Bang(bad_x, bad_y);
12919 }
12920
12921 void KillPlayer(struct PlayerInfo *player)
12922 {
12923   int jx = player->jx, jy = player->jy;
12924
12925   if (!player->active)
12926     return;
12927
12928 #if 0
12929   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12930          player->killed, player->active, player->reanimated);
12931 #endif
12932
12933   /* the following code was introduced to prevent an infinite loop when calling
12934      -> Bang()
12935      -> CheckTriggeredElementChangeExt()
12936      -> ExecuteCustomElementAction()
12937      -> KillPlayer()
12938      -> (infinitely repeating the above sequence of function calls)
12939      which occurs when killing the player while having a CE with the setting
12940      "kill player X when explosion of <player X>"; the solution using a new
12941      field "player->killed" was chosen for backwards compatibility, although
12942      clever use of the fields "player->active" etc. would probably also work */
12943 #if 1
12944   if (player->killed)
12945     return;
12946 #endif
12947
12948   player->killed = TRUE;
12949
12950   /* remove accessible field at the player's position */
12951   Feld[jx][jy] = EL_EMPTY;
12952
12953   /* deactivate shield (else Bang()/Explode() would not work right) */
12954   player->shield_normal_time_left = 0;
12955   player->shield_deadly_time_left = 0;
12956
12957 #if 0
12958   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12959          player->killed, player->active, player->reanimated);
12960 #endif
12961
12962   Bang(jx, jy);
12963
12964 #if 0
12965   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12966          player->killed, player->active, player->reanimated);
12967 #endif
12968
12969   if (player->reanimated)       /* killed player may have been reanimated */
12970     player->killed = player->reanimated = FALSE;
12971   else
12972     BuryPlayer(player);
12973 }
12974
12975 static void KillPlayerUnlessEnemyProtected(int x, int y)
12976 {
12977   if (!PLAYER_ENEMY_PROTECTED(x, y))
12978     KillPlayer(PLAYERINFO(x, y));
12979 }
12980
12981 static void KillPlayerUnlessExplosionProtected(int x, int y)
12982 {
12983   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12984     KillPlayer(PLAYERINFO(x, y));
12985 }
12986
12987 void BuryPlayer(struct PlayerInfo *player)
12988 {
12989   int jx = player->jx, jy = player->jy;
12990
12991   if (!player->active)
12992     return;
12993
12994   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12995   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12996
12997   player->GameOver = TRUE;
12998   RemovePlayer(player);
12999 }
13000
13001 void RemovePlayer(struct PlayerInfo *player)
13002 {
13003   int jx = player->jx, jy = player->jy;
13004   int i, found = FALSE;
13005
13006   player->present = FALSE;
13007   player->active = FALSE;
13008
13009   if (!ExplodeField[jx][jy])
13010     StorePlayer[jx][jy] = 0;
13011
13012   if (player->is_moving)
13013     TEST_DrawLevelField(player->last_jx, player->last_jy);
13014
13015   for (i = 0; i < MAX_PLAYERS; i++)
13016     if (stored_player[i].active)
13017       found = TRUE;
13018
13019   if (!found)
13020     AllPlayersGone = TRUE;
13021
13022   ExitX = ZX = jx;
13023   ExitY = ZY = jy;
13024 }
13025
13026 static void setFieldForSnapping(int x, int y, int element, int direction)
13027 {
13028   struct ElementInfo *ei = &element_info[element];
13029   int direction_bit = MV_DIR_TO_BIT(direction);
13030   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13031   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13032                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13033
13034   Feld[x][y] = EL_ELEMENT_SNAPPING;
13035   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13036
13037   ResetGfxAnimation(x, y);
13038
13039   GfxElement[x][y] = element;
13040   GfxAction[x][y] = action;
13041   GfxDir[x][y] = direction;
13042   GfxFrame[x][y] = -1;
13043 }
13044
13045 /*
13046   =============================================================================
13047   checkDiagonalPushing()
13048   -----------------------------------------------------------------------------
13049   check if diagonal input device direction results in pushing of object
13050   (by checking if the alternative direction is walkable, diggable, ...)
13051   =============================================================================
13052 */
13053
13054 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13055                                     int x, int y, int real_dx, int real_dy)
13056 {
13057   int jx, jy, dx, dy, xx, yy;
13058
13059   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13060     return TRUE;
13061
13062   /* diagonal direction: check alternative direction */
13063   jx = player->jx;
13064   jy = player->jy;
13065   dx = x - jx;
13066   dy = y - jy;
13067   xx = jx + (dx == 0 ? real_dx : 0);
13068   yy = jy + (dy == 0 ? real_dy : 0);
13069
13070   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13071 }
13072
13073 /*
13074   =============================================================================
13075   DigField()
13076   -----------------------------------------------------------------------------
13077   x, y:                 field next to player (non-diagonal) to try to dig to
13078   real_dx, real_dy:     direction as read from input device (can be diagonal)
13079   =============================================================================
13080 */
13081
13082 static int DigField(struct PlayerInfo *player,
13083                     int oldx, int oldy, int x, int y,
13084                     int real_dx, int real_dy, int mode)
13085 {
13086   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13087   boolean player_was_pushing = player->is_pushing;
13088   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13089   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13090   int jx = oldx, jy = oldy;
13091   int dx = x - jx, dy = y - jy;
13092   int nextx = x + dx, nexty = y + dy;
13093   int move_direction = (dx == -1 ? MV_LEFT  :
13094                         dx == +1 ? MV_RIGHT :
13095                         dy == -1 ? MV_UP    :
13096                         dy == +1 ? MV_DOWN  : MV_NONE);
13097   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13098   int dig_side = MV_DIR_OPPOSITE(move_direction);
13099   int old_element = Feld[jx][jy];
13100   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13101   int collect_count;
13102
13103   if (is_player)                /* function can also be called by EL_PENGUIN */
13104   {
13105     if (player->MovPos == 0)
13106     {
13107       player->is_digging = FALSE;
13108       player->is_collecting = FALSE;
13109     }
13110
13111     if (player->MovPos == 0)    /* last pushing move finished */
13112       player->is_pushing = FALSE;
13113
13114     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13115     {
13116       player->is_switching = FALSE;
13117       player->push_delay = -1;
13118
13119       return MP_NO_ACTION;
13120     }
13121   }
13122
13123   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13124     old_element = Back[jx][jy];
13125
13126   /* in case of element dropped at player position, check background */
13127   else if (Back[jx][jy] != EL_EMPTY &&
13128            game.engine_version >= VERSION_IDENT(2,2,0,0))
13129     old_element = Back[jx][jy];
13130
13131   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13132     return MP_NO_ACTION;        /* field has no opening in this direction */
13133
13134   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13135     return MP_NO_ACTION;        /* field has no opening in this direction */
13136
13137   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13138   {
13139     SplashAcid(x, y);
13140
13141     Feld[jx][jy] = player->artwork_element;
13142     InitMovingField(jx, jy, MV_DOWN);
13143     Store[jx][jy] = EL_ACID;
13144     ContinueMoving(jx, jy);
13145     BuryPlayer(player);
13146
13147     return MP_DONT_RUN_INTO;
13148   }
13149
13150   if (player_can_move && DONT_RUN_INTO(element))
13151   {
13152     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13153
13154     return MP_DONT_RUN_INTO;
13155   }
13156
13157   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13158     return MP_NO_ACTION;
13159
13160   collect_count = element_info[element].collect_count_initial;
13161
13162   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13163     return MP_NO_ACTION;
13164
13165   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13166     player_can_move = player_can_move_or_snap;
13167
13168   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13169       game.engine_version >= VERSION_IDENT(2,2,0,0))
13170   {
13171     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13172                                player->index_bit, dig_side);
13173     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13174                                         player->index_bit, dig_side);
13175
13176     if (element == EL_DC_LANDMINE)
13177       Bang(x, y);
13178
13179     if (Feld[x][y] != element)          /* field changed by snapping */
13180       return MP_ACTION;
13181
13182     return MP_NO_ACTION;
13183   }
13184
13185   if (player->gravity && is_player && !player->is_auto_moving &&
13186       canFallDown(player) && move_direction != MV_DOWN &&
13187       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13188     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13189
13190   if (player_can_move &&
13191       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13192   {
13193     int sound_element = SND_ELEMENT(element);
13194     int sound_action = ACTION_WALKING;
13195
13196     if (IS_RND_GATE(element))
13197     {
13198       if (!player->key[RND_GATE_NR(element)])
13199         return MP_NO_ACTION;
13200     }
13201     else if (IS_RND_GATE_GRAY(element))
13202     {
13203       if (!player->key[RND_GATE_GRAY_NR(element)])
13204         return MP_NO_ACTION;
13205     }
13206     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13207     {
13208       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13209         return MP_NO_ACTION;
13210     }
13211     else if (element == EL_EXIT_OPEN ||
13212              element == EL_EM_EXIT_OPEN ||
13213              element == EL_EM_EXIT_OPENING ||
13214              element == EL_STEEL_EXIT_OPEN ||
13215              element == EL_EM_STEEL_EXIT_OPEN ||
13216              element == EL_EM_STEEL_EXIT_OPENING ||
13217              element == EL_SP_EXIT_OPEN ||
13218              element == EL_SP_EXIT_OPENING)
13219     {
13220       sound_action = ACTION_PASSING;    /* player is passing exit */
13221     }
13222     else if (element == EL_EMPTY)
13223     {
13224       sound_action = ACTION_MOVING;             /* nothing to walk on */
13225     }
13226
13227     /* play sound from background or player, whatever is available */
13228     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13229       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13230     else
13231       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13232   }
13233   else if (player_can_move &&
13234            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13235   {
13236     if (!ACCESS_FROM(element, opposite_direction))
13237       return MP_NO_ACTION;      /* field not accessible from this direction */
13238
13239     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13240       return MP_NO_ACTION;
13241
13242     if (IS_EM_GATE(element))
13243     {
13244       if (!player->key[EM_GATE_NR(element)])
13245         return MP_NO_ACTION;
13246     }
13247     else if (IS_EM_GATE_GRAY(element))
13248     {
13249       if (!player->key[EM_GATE_GRAY_NR(element)])
13250         return MP_NO_ACTION;
13251     }
13252     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13253     {
13254       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13255         return MP_NO_ACTION;
13256     }
13257     else if (IS_EMC_GATE(element))
13258     {
13259       if (!player->key[EMC_GATE_NR(element)])
13260         return MP_NO_ACTION;
13261     }
13262     else if (IS_EMC_GATE_GRAY(element))
13263     {
13264       if (!player->key[EMC_GATE_GRAY_NR(element)])
13265         return MP_NO_ACTION;
13266     }
13267     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13268     {
13269       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13270         return MP_NO_ACTION;
13271     }
13272     else if (element == EL_DC_GATE_WHITE ||
13273              element == EL_DC_GATE_WHITE_GRAY ||
13274              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13275     {
13276       if (player->num_white_keys == 0)
13277         return MP_NO_ACTION;
13278
13279       player->num_white_keys--;
13280     }
13281     else if (IS_SP_PORT(element))
13282     {
13283       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13284           element == EL_SP_GRAVITY_PORT_RIGHT ||
13285           element == EL_SP_GRAVITY_PORT_UP ||
13286           element == EL_SP_GRAVITY_PORT_DOWN)
13287         player->gravity = !player->gravity;
13288       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13289                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13290                element == EL_SP_GRAVITY_ON_PORT_UP ||
13291                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13292         player->gravity = TRUE;
13293       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13294                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13295                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13296                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13297         player->gravity = FALSE;
13298     }
13299
13300     /* automatically move to the next field with double speed */
13301     player->programmed_action = move_direction;
13302
13303     if (player->move_delay_reset_counter == 0)
13304     {
13305       player->move_delay_reset_counter = 2;     /* two double speed steps */
13306
13307       DOUBLE_PLAYER_SPEED(player);
13308     }
13309
13310     PlayLevelSoundAction(x, y, ACTION_PASSING);
13311   }
13312   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13313   {
13314     RemoveField(x, y);
13315
13316     if (mode != DF_SNAP)
13317     {
13318       GfxElement[x][y] = GFX_ELEMENT(element);
13319       player->is_digging = TRUE;
13320     }
13321
13322     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13323
13324     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13325                                         player->index_bit, dig_side);
13326
13327     if (mode == DF_SNAP)
13328     {
13329       if (level.block_snap_field)
13330         setFieldForSnapping(x, y, element, move_direction);
13331       else
13332         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13333
13334       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13335                                           player->index_bit, dig_side);
13336     }
13337   }
13338   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13339   {
13340     RemoveField(x, y);
13341
13342     if (is_player && mode != DF_SNAP)
13343     {
13344       GfxElement[x][y] = element;
13345       player->is_collecting = TRUE;
13346     }
13347
13348     if (element == EL_SPEED_PILL)
13349     {
13350       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13351     }
13352     else if (element == EL_EXTRA_TIME && level.time > 0)
13353     {
13354       TimeLeft += level.extra_time;
13355
13356       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13357
13358       DisplayGameControlValues();
13359     }
13360     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13361     {
13362       player->shield_normal_time_left += level.shield_normal_time;
13363       if (element == EL_SHIELD_DEADLY)
13364         player->shield_deadly_time_left += level.shield_deadly_time;
13365     }
13366     else if (element == EL_DYNAMITE ||
13367              element == EL_EM_DYNAMITE ||
13368              element == EL_SP_DISK_RED)
13369     {
13370       if (player->inventory_size < MAX_INVENTORY_SIZE)
13371         player->inventory_element[player->inventory_size++] = element;
13372
13373       DrawGameDoorValues();
13374     }
13375     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13376     {
13377       player->dynabomb_count++;
13378       player->dynabombs_left++;
13379     }
13380     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13381     {
13382       player->dynabomb_size++;
13383     }
13384     else if (element == EL_DYNABOMB_INCREASE_POWER)
13385     {
13386       player->dynabomb_xl = TRUE;
13387     }
13388     else if (IS_KEY(element))
13389     {
13390       player->key[KEY_NR(element)] = TRUE;
13391
13392       DrawGameDoorValues();
13393     }
13394     else if (element == EL_DC_KEY_WHITE)
13395     {
13396       player->num_white_keys++;
13397
13398       /* display white keys? */
13399       /* DrawGameDoorValues(); */
13400     }
13401     else if (IS_ENVELOPE(element))
13402     {
13403       player->show_envelope = element;
13404     }
13405     else if (element == EL_EMC_LENSES)
13406     {
13407       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13408
13409       RedrawAllInvisibleElementsForLenses();
13410     }
13411     else if (element == EL_EMC_MAGNIFIER)
13412     {
13413       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13414
13415       RedrawAllInvisibleElementsForMagnifier();
13416     }
13417     else if (IS_DROPPABLE(element) ||
13418              IS_THROWABLE(element))     /* can be collected and dropped */
13419     {
13420       int i;
13421
13422       if (collect_count == 0)
13423         player->inventory_infinite_element = element;
13424       else
13425         for (i = 0; i < collect_count; i++)
13426           if (player->inventory_size < MAX_INVENTORY_SIZE)
13427             player->inventory_element[player->inventory_size++] = element;
13428
13429       DrawGameDoorValues();
13430     }
13431     else if (collect_count > 0)
13432     {
13433       local_player->gems_still_needed -= collect_count;
13434       if (local_player->gems_still_needed < 0)
13435         local_player->gems_still_needed = 0;
13436
13437       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13438
13439       DisplayGameControlValues();
13440     }
13441
13442     RaiseScoreElement(element);
13443     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13444
13445     if (is_player)
13446       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13447                                           player->index_bit, dig_side);
13448
13449     if (mode == DF_SNAP)
13450     {
13451       if (level.block_snap_field)
13452         setFieldForSnapping(x, y, element, move_direction);
13453       else
13454         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13455
13456       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13457                                           player->index_bit, dig_side);
13458     }
13459   }
13460   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13461   {
13462     if (mode == DF_SNAP && element != EL_BD_ROCK)
13463       return MP_NO_ACTION;
13464
13465     if (CAN_FALL(element) && dy)
13466       return MP_NO_ACTION;
13467
13468     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13469         !(element == EL_SPRING && level.use_spring_bug))
13470       return MP_NO_ACTION;
13471
13472     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13473         ((move_direction & MV_VERTICAL &&
13474           ((element_info[element].move_pattern & MV_LEFT &&
13475             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13476            (element_info[element].move_pattern & MV_RIGHT &&
13477             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13478          (move_direction & MV_HORIZONTAL &&
13479           ((element_info[element].move_pattern & MV_UP &&
13480             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13481            (element_info[element].move_pattern & MV_DOWN &&
13482             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13483       return MP_NO_ACTION;
13484
13485     /* do not push elements already moving away faster than player */
13486     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13487         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13488       return MP_NO_ACTION;
13489
13490     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13491     {
13492       if (player->push_delay_value == -1 || !player_was_pushing)
13493         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13494     }
13495     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13496     {
13497       if (player->push_delay_value == -1)
13498         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13499     }
13500     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13501     {
13502       if (!player->is_pushing)
13503         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13504     }
13505
13506     player->is_pushing = TRUE;
13507     player->is_active = TRUE;
13508
13509     if (!(IN_LEV_FIELD(nextx, nexty) &&
13510           (IS_FREE(nextx, nexty) ||
13511            (IS_SB_ELEMENT(element) &&
13512             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13513            (IS_CUSTOM_ELEMENT(element) &&
13514             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13515       return MP_NO_ACTION;
13516
13517     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13518       return MP_NO_ACTION;
13519
13520     if (player->push_delay == -1)       /* new pushing; restart delay */
13521       player->push_delay = 0;
13522
13523     if (player->push_delay < player->push_delay_value &&
13524         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13525         element != EL_SPRING && element != EL_BALLOON)
13526     {
13527       /* make sure that there is no move delay before next try to push */
13528       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13529         player->move_delay = 0;
13530
13531       return MP_NO_ACTION;
13532     }
13533
13534     if (IS_CUSTOM_ELEMENT(element) &&
13535         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13536     {
13537       if (!DigFieldByCE(nextx, nexty, element))
13538         return MP_NO_ACTION;
13539     }
13540
13541     if (IS_SB_ELEMENT(element))
13542     {
13543       if (element == EL_SOKOBAN_FIELD_FULL)
13544       {
13545         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13546         local_player->sokobanfields_still_needed++;
13547       }
13548
13549       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13550       {
13551         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13552         local_player->sokobanfields_still_needed--;
13553       }
13554
13555       Feld[x][y] = EL_SOKOBAN_OBJECT;
13556
13557       if (Back[x][y] == Back[nextx][nexty])
13558         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13559       else if (Back[x][y] != 0)
13560         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13561                                     ACTION_EMPTYING);
13562       else
13563         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13564                                     ACTION_FILLING);
13565
13566       if (local_player->sokobanfields_still_needed == 0 &&
13567           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13568       {
13569         PlayerWins(player);
13570
13571         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13572       }
13573     }
13574     else
13575       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13576
13577     InitMovingField(x, y, move_direction);
13578     GfxAction[x][y] = ACTION_PUSHING;
13579
13580     if (mode == DF_SNAP)
13581       ContinueMoving(x, y);
13582     else
13583       MovPos[x][y] = (dx != 0 ? dx : dy);
13584
13585     Pushed[x][y] = TRUE;
13586     Pushed[nextx][nexty] = TRUE;
13587
13588     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13589       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13590     else
13591       player->push_delay_value = -1;    /* get new value later */
13592
13593     /* check for element change _after_ element has been pushed */
13594     if (game.use_change_when_pushing_bug)
13595     {
13596       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13597                                  player->index_bit, dig_side);
13598       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13599                                           player->index_bit, dig_side);
13600     }
13601   }
13602   else if (IS_SWITCHABLE(element))
13603   {
13604     if (PLAYER_SWITCHING(player, x, y))
13605     {
13606       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13607                                           player->index_bit, dig_side);
13608
13609       return MP_ACTION;
13610     }
13611
13612     player->is_switching = TRUE;
13613     player->switch_x = x;
13614     player->switch_y = y;
13615
13616     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13617
13618     if (element == EL_ROBOT_WHEEL)
13619     {
13620       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13621       ZX = x;
13622       ZY = y;
13623
13624       game.robot_wheel_active = TRUE;
13625
13626       TEST_DrawLevelField(x, y);
13627     }
13628     else if (element == EL_SP_TERMINAL)
13629     {
13630       int xx, yy;
13631
13632       SCAN_PLAYFIELD(xx, yy)
13633       {
13634         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13635           Bang(xx, yy);
13636         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13637           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13638       }
13639     }
13640     else if (IS_BELT_SWITCH(element))
13641     {
13642       ToggleBeltSwitch(x, y);
13643     }
13644     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13645              element == EL_SWITCHGATE_SWITCH_DOWN ||
13646              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13647              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13648     {
13649       ToggleSwitchgateSwitch(x, y);
13650     }
13651     else if (element == EL_LIGHT_SWITCH ||
13652              element == EL_LIGHT_SWITCH_ACTIVE)
13653     {
13654       ToggleLightSwitch(x, y);
13655     }
13656     else if (element == EL_TIMEGATE_SWITCH ||
13657              element == EL_DC_TIMEGATE_SWITCH)
13658     {
13659       ActivateTimegateSwitch(x, y);
13660     }
13661     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13662              element == EL_BALLOON_SWITCH_RIGHT ||
13663              element == EL_BALLOON_SWITCH_UP    ||
13664              element == EL_BALLOON_SWITCH_DOWN  ||
13665              element == EL_BALLOON_SWITCH_NONE  ||
13666              element == EL_BALLOON_SWITCH_ANY)
13667     {
13668       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13669                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13670                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13671                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13672                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13673                              move_direction);
13674     }
13675     else if (element == EL_LAMP)
13676     {
13677       Feld[x][y] = EL_LAMP_ACTIVE;
13678       local_player->lights_still_needed--;
13679
13680       ResetGfxAnimation(x, y);
13681       TEST_DrawLevelField(x, y);
13682     }
13683     else if (element == EL_TIME_ORB_FULL)
13684     {
13685       Feld[x][y] = EL_TIME_ORB_EMPTY;
13686
13687       if (level.time > 0 || level.use_time_orb_bug)
13688       {
13689         TimeLeft += level.time_orb_time;
13690         game.no_time_limit = FALSE;
13691
13692         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13693
13694         DisplayGameControlValues();
13695       }
13696
13697       ResetGfxAnimation(x, y);
13698       TEST_DrawLevelField(x, y);
13699     }
13700     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13701              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13702     {
13703       int xx, yy;
13704
13705       game.ball_state = !game.ball_state;
13706
13707       SCAN_PLAYFIELD(xx, yy)
13708       {
13709         int e = Feld[xx][yy];
13710
13711         if (game.ball_state)
13712         {
13713           if (e == EL_EMC_MAGIC_BALL)
13714             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13715           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13716             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13717         }
13718         else
13719         {
13720           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13721             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13722           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13723             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13724         }
13725       }
13726     }
13727
13728     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13729                                         player->index_bit, dig_side);
13730
13731     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13732                                         player->index_bit, dig_side);
13733
13734     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13735                                         player->index_bit, dig_side);
13736
13737     return MP_ACTION;
13738   }
13739   else
13740   {
13741     if (!PLAYER_SWITCHING(player, x, y))
13742     {
13743       player->is_switching = TRUE;
13744       player->switch_x = x;
13745       player->switch_y = y;
13746
13747       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13748                                  player->index_bit, dig_side);
13749       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13750                                           player->index_bit, dig_side);
13751
13752       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13753                                  player->index_bit, dig_side);
13754       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13755                                           player->index_bit, dig_side);
13756     }
13757
13758     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13759                                player->index_bit, dig_side);
13760     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13761                                         player->index_bit, dig_side);
13762
13763     return MP_NO_ACTION;
13764   }
13765
13766   player->push_delay = -1;
13767
13768   if (is_player)                /* function can also be called by EL_PENGUIN */
13769   {
13770     if (Feld[x][y] != element)          /* really digged/collected something */
13771     {
13772       player->is_collecting = !player->is_digging;
13773       player->is_active = TRUE;
13774     }
13775   }
13776
13777   return MP_MOVING;
13778 }
13779
13780 static boolean DigFieldByCE(int x, int y, int digging_element)
13781 {
13782   int element = Feld[x][y];
13783
13784   if (!IS_FREE(x, y))
13785   {
13786     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13787                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13788                   ACTION_BREAKING);
13789
13790     /* no element can dig solid indestructible elements */
13791     if (IS_INDESTRUCTIBLE(element) &&
13792         !IS_DIGGABLE(element) &&
13793         !IS_COLLECTIBLE(element))
13794       return FALSE;
13795
13796     if (AmoebaNr[x][y] &&
13797         (element == EL_AMOEBA_FULL ||
13798          element == EL_BD_AMOEBA ||
13799          element == EL_AMOEBA_GROWING))
13800     {
13801       AmoebaCnt[AmoebaNr[x][y]]--;
13802       AmoebaCnt2[AmoebaNr[x][y]]--;
13803     }
13804
13805     if (IS_MOVING(x, y))
13806       RemoveMovingField(x, y);
13807     else
13808     {
13809       RemoveField(x, y);
13810       TEST_DrawLevelField(x, y);
13811     }
13812
13813     /* if digged element was about to explode, prevent the explosion */
13814     ExplodeField[x][y] = EX_TYPE_NONE;
13815
13816     PlayLevelSoundAction(x, y, action);
13817   }
13818
13819   Store[x][y] = EL_EMPTY;
13820
13821   /* this makes it possible to leave the removed element again */
13822   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13823     Store[x][y] = element;
13824
13825   return TRUE;
13826 }
13827
13828 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13829 {
13830   int jx = player->jx, jy = player->jy;
13831   int x = jx + dx, y = jy + dy;
13832   int snap_direction = (dx == -1 ? MV_LEFT  :
13833                         dx == +1 ? MV_RIGHT :
13834                         dy == -1 ? MV_UP    :
13835                         dy == +1 ? MV_DOWN  : MV_NONE);
13836   boolean can_continue_snapping = (level.continuous_snapping &&
13837                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13838
13839   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13840     return FALSE;
13841
13842   if (!player->active || !IN_LEV_FIELD(x, y))
13843     return FALSE;
13844
13845   if (dx && dy)
13846     return FALSE;
13847
13848   if (!dx && !dy)
13849   {
13850     if (player->MovPos == 0)
13851       player->is_pushing = FALSE;
13852
13853     player->is_snapping = FALSE;
13854
13855     if (player->MovPos == 0)
13856     {
13857       player->is_moving = FALSE;
13858       player->is_digging = FALSE;
13859       player->is_collecting = FALSE;
13860     }
13861
13862     return FALSE;
13863   }
13864
13865   /* prevent snapping with already pressed snap key when not allowed */
13866   if (player->is_snapping && !can_continue_snapping)
13867     return FALSE;
13868
13869   player->MovDir = snap_direction;
13870
13871   if (player->MovPos == 0)
13872   {
13873     player->is_moving = FALSE;
13874     player->is_digging = FALSE;
13875     player->is_collecting = FALSE;
13876   }
13877
13878   player->is_dropping = FALSE;
13879   player->is_dropping_pressed = FALSE;
13880   player->drop_pressed_delay = 0;
13881
13882   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13883     return FALSE;
13884
13885   player->is_snapping = TRUE;
13886   player->is_active = TRUE;
13887
13888   if (player->MovPos == 0)
13889   {
13890     player->is_moving = FALSE;
13891     player->is_digging = FALSE;
13892     player->is_collecting = FALSE;
13893   }
13894
13895   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13896     TEST_DrawLevelField(player->last_jx, player->last_jy);
13897
13898   TEST_DrawLevelField(x, y);
13899
13900   return TRUE;
13901 }
13902
13903 static boolean DropElement(struct PlayerInfo *player)
13904 {
13905   int old_element, new_element;
13906   int dropx = player->jx, dropy = player->jy;
13907   int drop_direction = player->MovDir;
13908   int drop_side = drop_direction;
13909   int drop_element = get_next_dropped_element(player);
13910
13911   player->is_dropping_pressed = TRUE;
13912
13913   /* do not drop an element on top of another element; when holding drop key
13914      pressed without moving, dropped element must move away before the next
13915      element can be dropped (this is especially important if the next element
13916      is dynamite, which can be placed on background for historical reasons) */
13917   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13918     return MP_ACTION;
13919
13920   if (IS_THROWABLE(drop_element))
13921   {
13922     dropx += GET_DX_FROM_DIR(drop_direction);
13923     dropy += GET_DY_FROM_DIR(drop_direction);
13924
13925     if (!IN_LEV_FIELD(dropx, dropy))
13926       return FALSE;
13927   }
13928
13929   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13930   new_element = drop_element;           /* default: no change when dropping */
13931
13932   /* check if player is active, not moving and ready to drop */
13933   if (!player->active || player->MovPos || player->drop_delay > 0)
13934     return FALSE;
13935
13936   /* check if player has anything that can be dropped */
13937   if (new_element == EL_UNDEFINED)
13938     return FALSE;
13939
13940   /* check if drop key was pressed long enough for EM style dynamite */
13941   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13942     return FALSE;
13943
13944   /* check if anything can be dropped at the current position */
13945   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13946     return FALSE;
13947
13948   /* collected custom elements can only be dropped on empty fields */
13949   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13950     return FALSE;
13951
13952   if (old_element != EL_EMPTY)
13953     Back[dropx][dropy] = old_element;   /* store old element on this field */
13954
13955   ResetGfxAnimation(dropx, dropy);
13956   ResetRandomAnimationValue(dropx, dropy);
13957
13958   if (player->inventory_size > 0 ||
13959       player->inventory_infinite_element != EL_UNDEFINED)
13960   {
13961     if (player->inventory_size > 0)
13962     {
13963       player->inventory_size--;
13964
13965       DrawGameDoorValues();
13966
13967       if (new_element == EL_DYNAMITE)
13968         new_element = EL_DYNAMITE_ACTIVE;
13969       else if (new_element == EL_EM_DYNAMITE)
13970         new_element = EL_EM_DYNAMITE_ACTIVE;
13971       else if (new_element == EL_SP_DISK_RED)
13972         new_element = EL_SP_DISK_RED_ACTIVE;
13973     }
13974
13975     Feld[dropx][dropy] = new_element;
13976
13977     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13978       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13979                           el2img(Feld[dropx][dropy]), 0);
13980
13981     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13982
13983     /* needed if previous element just changed to "empty" in the last frame */
13984     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13985
13986     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13987                                player->index_bit, drop_side);
13988     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13989                                         CE_PLAYER_DROPS_X,
13990                                         player->index_bit, drop_side);
13991
13992     TestIfElementTouchesCustomElement(dropx, dropy);
13993   }
13994   else          /* player is dropping a dyna bomb */
13995   {
13996     player->dynabombs_left--;
13997
13998     Feld[dropx][dropy] = new_element;
13999
14000     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14001       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14002                           el2img(Feld[dropx][dropy]), 0);
14003
14004     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14005   }
14006
14007   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14008     InitField_WithBug1(dropx, dropy, FALSE);
14009
14010   new_element = Feld[dropx][dropy];     /* element might have changed */
14011
14012   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14013       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14014   {
14015     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14016       MovDir[dropx][dropy] = drop_direction;
14017
14018     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14019
14020     /* do not cause impact style collision by dropping elements that can fall */
14021     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14022   }
14023
14024   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14025   player->is_dropping = TRUE;
14026
14027   player->drop_pressed_delay = 0;
14028   player->is_dropping_pressed = FALSE;
14029
14030   player->drop_x = dropx;
14031   player->drop_y = dropy;
14032
14033   return TRUE;
14034 }
14035
14036 /* ------------------------------------------------------------------------- */
14037 /* game sound playing functions                                              */
14038 /* ------------------------------------------------------------------------- */
14039
14040 static int *loop_sound_frame = NULL;
14041 static int *loop_sound_volume = NULL;
14042
14043 void InitPlayLevelSound()
14044 {
14045   int num_sounds = getSoundListSize();
14046
14047   checked_free(loop_sound_frame);
14048   checked_free(loop_sound_volume);
14049
14050   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14051   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14052 }
14053
14054 static void PlayLevelSound(int x, int y, int nr)
14055 {
14056   int sx = SCREENX(x), sy = SCREENY(y);
14057   int volume, stereo_position;
14058   int max_distance = 8;
14059   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14060
14061   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14062       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14063     return;
14064
14065   if (!IN_LEV_FIELD(x, y) ||
14066       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14067       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14068     return;
14069
14070   volume = SOUND_MAX_VOLUME;
14071
14072   if (!IN_SCR_FIELD(sx, sy))
14073   {
14074     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14075     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14076
14077     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14078   }
14079
14080   stereo_position = (SOUND_MAX_LEFT +
14081                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14082                      (SCR_FIELDX + 2 * max_distance));
14083
14084   if (IS_LOOP_SOUND(nr))
14085   {
14086     /* This assures that quieter loop sounds do not overwrite louder ones,
14087        while restarting sound volume comparison with each new game frame. */
14088
14089     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14090       return;
14091
14092     loop_sound_volume[nr] = volume;
14093     loop_sound_frame[nr] = FrameCounter;
14094   }
14095
14096   PlaySoundExt(nr, volume, stereo_position, type);
14097 }
14098
14099 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14100 {
14101   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14102                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14103                  y < LEVELY(BY1) ? LEVELY(BY1) :
14104                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14105                  sound_action);
14106 }
14107
14108 static void PlayLevelSoundAction(int x, int y, int action)
14109 {
14110   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14111 }
14112
14113 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14114 {
14115   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14116
14117   if (sound_effect != SND_UNDEFINED)
14118     PlayLevelSound(x, y, sound_effect);
14119 }
14120
14121 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14122                                               int action)
14123 {
14124   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14125
14126   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14127     PlayLevelSound(x, y, sound_effect);
14128 }
14129
14130 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14131 {
14132   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14133
14134   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14135     PlayLevelSound(x, y, sound_effect);
14136 }
14137
14138 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14139 {
14140   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14141
14142   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14143     StopSound(sound_effect);
14144 }
14145
14146 static void PlayLevelMusic()
14147 {
14148   if (levelset.music[level_nr] != MUS_UNDEFINED)
14149     PlayMusic(levelset.music[level_nr]);        /* from config file */
14150   else
14151     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14152 }
14153
14154 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14155 {
14156   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14157   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14158   int x = xx - 1 - offset;
14159   int y = yy - 1 - offset;
14160
14161   switch (sample)
14162   {
14163     case SAMPLE_blank:
14164       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14165       break;
14166
14167     case SAMPLE_roll:
14168       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14169       break;
14170
14171     case SAMPLE_stone:
14172       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14173       break;
14174
14175     case SAMPLE_nut:
14176       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14177       break;
14178
14179     case SAMPLE_crack:
14180       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14181       break;
14182
14183     case SAMPLE_bug:
14184       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14185       break;
14186
14187     case SAMPLE_tank:
14188       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14189       break;
14190
14191     case SAMPLE_android_clone:
14192       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14193       break;
14194
14195     case SAMPLE_android_move:
14196       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14197       break;
14198
14199     case SAMPLE_spring:
14200       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14201       break;
14202
14203     case SAMPLE_slurp:
14204       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14205       break;
14206
14207     case SAMPLE_eater:
14208       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14209       break;
14210
14211     case SAMPLE_eater_eat:
14212       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14213       break;
14214
14215     case SAMPLE_alien:
14216       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14217       break;
14218
14219     case SAMPLE_collect:
14220       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14221       break;
14222
14223     case SAMPLE_diamond:
14224       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14225       break;
14226
14227     case SAMPLE_squash:
14228       /* !!! CHECK THIS !!! */
14229 #if 1
14230       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14231 #else
14232       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14233 #endif
14234       break;
14235
14236     case SAMPLE_wonderfall:
14237       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14238       break;
14239
14240     case SAMPLE_drip:
14241       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14242       break;
14243
14244     case SAMPLE_push:
14245       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14246       break;
14247
14248     case SAMPLE_dirt:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14250       break;
14251
14252     case SAMPLE_acid:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14254       break;
14255
14256     case SAMPLE_ball:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14258       break;
14259
14260     case SAMPLE_grow:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14262       break;
14263
14264     case SAMPLE_wonder:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14266       break;
14267
14268     case SAMPLE_door:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14270       break;
14271
14272     case SAMPLE_exit_open:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14274       break;
14275
14276     case SAMPLE_exit_leave:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14278       break;
14279
14280     case SAMPLE_dynamite:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14282       break;
14283
14284     case SAMPLE_tick:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14286       break;
14287
14288     case SAMPLE_press:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14290       break;
14291
14292     case SAMPLE_wheel:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14294       break;
14295
14296     case SAMPLE_boom:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14298       break;
14299
14300     case SAMPLE_die:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14302       break;
14303
14304     case SAMPLE_time:
14305       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14306       break;
14307
14308     default:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14310       break;
14311   }
14312 }
14313
14314 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14315 {
14316   int element = map_element_SP_to_RND(element_sp);
14317   int action = map_action_SP_to_RND(action_sp);
14318   int offset = (setup.sp_show_border_elements ? 0 : 1);
14319   int x = xx - offset;
14320   int y = yy - offset;
14321
14322   PlayLevelSoundElementAction(x, y, element, action);
14323 }
14324
14325 void RaiseScore(int value)
14326 {
14327   local_player->score += value;
14328
14329   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14330
14331   DisplayGameControlValues();
14332 }
14333
14334 void RaiseScoreElement(int element)
14335 {
14336   switch (element)
14337   {
14338     case EL_EMERALD:
14339     case EL_BD_DIAMOND:
14340     case EL_EMERALD_YELLOW:
14341     case EL_EMERALD_RED:
14342     case EL_EMERALD_PURPLE:
14343     case EL_SP_INFOTRON:
14344       RaiseScore(level.score[SC_EMERALD]);
14345       break;
14346     case EL_DIAMOND:
14347       RaiseScore(level.score[SC_DIAMOND]);
14348       break;
14349     case EL_CRYSTAL:
14350       RaiseScore(level.score[SC_CRYSTAL]);
14351       break;
14352     case EL_PEARL:
14353       RaiseScore(level.score[SC_PEARL]);
14354       break;
14355     case EL_BUG:
14356     case EL_BD_BUTTERFLY:
14357     case EL_SP_ELECTRON:
14358       RaiseScore(level.score[SC_BUG]);
14359       break;
14360     case EL_SPACESHIP:
14361     case EL_BD_FIREFLY:
14362     case EL_SP_SNIKSNAK:
14363       RaiseScore(level.score[SC_SPACESHIP]);
14364       break;
14365     case EL_YAMYAM:
14366     case EL_DARK_YAMYAM:
14367       RaiseScore(level.score[SC_YAMYAM]);
14368       break;
14369     case EL_ROBOT:
14370       RaiseScore(level.score[SC_ROBOT]);
14371       break;
14372     case EL_PACMAN:
14373       RaiseScore(level.score[SC_PACMAN]);
14374       break;
14375     case EL_NUT:
14376       RaiseScore(level.score[SC_NUT]);
14377       break;
14378     case EL_DYNAMITE:
14379     case EL_EM_DYNAMITE:
14380     case EL_SP_DISK_RED:
14381     case EL_DYNABOMB_INCREASE_NUMBER:
14382     case EL_DYNABOMB_INCREASE_SIZE:
14383     case EL_DYNABOMB_INCREASE_POWER:
14384       RaiseScore(level.score[SC_DYNAMITE]);
14385       break;
14386     case EL_SHIELD_NORMAL:
14387     case EL_SHIELD_DEADLY:
14388       RaiseScore(level.score[SC_SHIELD]);
14389       break;
14390     case EL_EXTRA_TIME:
14391       RaiseScore(level.extra_time_score);
14392       break;
14393     case EL_KEY_1:
14394     case EL_KEY_2:
14395     case EL_KEY_3:
14396     case EL_KEY_4:
14397     case EL_EM_KEY_1:
14398     case EL_EM_KEY_2:
14399     case EL_EM_KEY_3:
14400     case EL_EM_KEY_4:
14401     case EL_EMC_KEY_5:
14402     case EL_EMC_KEY_6:
14403     case EL_EMC_KEY_7:
14404     case EL_EMC_KEY_8:
14405     case EL_DC_KEY_WHITE:
14406       RaiseScore(level.score[SC_KEY]);
14407       break;
14408     default:
14409       RaiseScore(element_info[element].collect_score);
14410       break;
14411   }
14412 }
14413
14414 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14415 {
14416   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14417   {
14418     /* closing door required in case of envelope style request dialogs */
14419     if (!skip_request)
14420       CloseDoor(DOOR_CLOSE_1);
14421
14422 #if defined(NETWORK_AVALIABLE)
14423     if (options.network)
14424       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14425     else
14426 #endif
14427     {
14428       if (quick_quit)
14429       {
14430         FadeSkipNextFadeIn();
14431
14432         game_status = GAME_MODE_MAIN;
14433
14434         DrawAndFadeInMainMenu(REDRAW_FIELD);
14435       }
14436       else
14437       {
14438         game_status = GAME_MODE_MAIN;
14439
14440         DrawAndFadeInMainMenu(REDRAW_FIELD);
14441       }
14442     }
14443   }
14444   else          /* continue playing the game */
14445   {
14446     if (tape.playing && tape.deactivate_display)
14447       TapeDeactivateDisplayOff(TRUE);
14448
14449     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14450
14451     if (tape.playing && tape.deactivate_display)
14452       TapeDeactivateDisplayOn();
14453   }
14454 }
14455
14456 void RequestQuitGame(boolean ask_if_really_quit)
14457 {
14458   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14459   boolean skip_request = AllPlayersGone || quick_quit;
14460
14461   RequestQuitGameExt(skip_request, quick_quit,
14462                      "Do you really want to quit the game?");
14463 }
14464
14465
14466 /* ------------------------------------------------------------------------- */
14467 /* random generator functions                                                */
14468 /* ------------------------------------------------------------------------- */
14469
14470 unsigned int InitEngineRandom_RND(int seed)
14471 {
14472   game.num_random_calls = 0;
14473
14474   return InitEngineRandom(seed);
14475 }
14476
14477 unsigned int RND(int max)
14478 {
14479   if (max > 0)
14480   {
14481     game.num_random_calls++;
14482
14483     return GetEngineRandom(max);
14484   }
14485
14486   return 0;
14487 }
14488
14489
14490 /* ------------------------------------------------------------------------- */
14491 /* game engine snapshot handling functions                                   */
14492 /* ------------------------------------------------------------------------- */
14493
14494 struct EngineSnapshotInfo
14495 {
14496   /* runtime values for custom element collect score */
14497   int collect_score[NUM_CUSTOM_ELEMENTS];
14498
14499   /* runtime values for group element choice position */
14500   int choice_pos[NUM_GROUP_ELEMENTS];
14501
14502   /* runtime values for belt position animations */
14503   int belt_graphic[4][NUM_BELT_PARTS];
14504   int belt_anim_mode[4][NUM_BELT_PARTS];
14505 };
14506
14507 static struct EngineSnapshotInfo engine_snapshot_rnd;
14508 static char *snapshot_level_identifier = NULL;
14509 static int snapshot_level_nr = -1;
14510
14511 static void SaveEngineSnapshotValues_RND()
14512 {
14513   static int belt_base_active_element[4] =
14514   {
14515     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14516     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14517     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14518     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14519   };
14520   int i, j;
14521
14522   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14523   {
14524     int element = EL_CUSTOM_START + i;
14525
14526     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14527   }
14528
14529   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14530   {
14531     int element = EL_GROUP_START + i;
14532
14533     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14534   }
14535
14536   for (i = 0; i < 4; i++)
14537   {
14538     for (j = 0; j < NUM_BELT_PARTS; j++)
14539     {
14540       int element = belt_base_active_element[i] + j;
14541       int graphic = el2img(element);
14542       int anim_mode = graphic_info[graphic].anim_mode;
14543
14544       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14545       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14546     }
14547   }
14548 }
14549
14550 static void LoadEngineSnapshotValues_RND()
14551 {
14552   unsigned int num_random_calls = game.num_random_calls;
14553   int i, j;
14554
14555   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14556   {
14557     int element = EL_CUSTOM_START + i;
14558
14559     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14560   }
14561
14562   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14563   {
14564     int element = EL_GROUP_START + i;
14565
14566     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14567   }
14568
14569   for (i = 0; i < 4; i++)
14570   {
14571     for (j = 0; j < NUM_BELT_PARTS; j++)
14572     {
14573       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14574       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14575
14576       graphic_info[graphic].anim_mode = anim_mode;
14577     }
14578   }
14579
14580   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14581   {
14582     InitRND(tape.random_seed);
14583     for (i = 0; i < num_random_calls; i++)
14584       RND(1);
14585   }
14586
14587   if (game.num_random_calls != num_random_calls)
14588   {
14589     Error(ERR_INFO, "number of random calls out of sync");
14590     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14591     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14592     Error(ERR_EXIT, "this should not happen -- please debug");
14593   }
14594 }
14595
14596 void FreeEngineSnapshotSingle()
14597 {
14598   FreeSnapshotSingle();
14599
14600   setString(&snapshot_level_identifier, NULL);
14601   snapshot_level_nr = -1;
14602 }
14603
14604 void FreeEngineSnapshotList()
14605 {
14606   FreeSnapshotList();
14607 }
14608
14609 ListNode *SaveEngineSnapshotBuffers()
14610 {
14611   ListNode *buffers = NULL;
14612
14613   /* do not save snapshots from editor */
14614   if (level_editor_test_game)
14615     return NULL;
14616
14617   /* copy some special values to a structure better suited for the snapshot */
14618
14619   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14620     SaveEngineSnapshotValues_RND();
14621   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14622     SaveEngineSnapshotValues_EM();
14623   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14624     SaveEngineSnapshotValues_SP(&buffers);
14625
14626   /* save values stored in special snapshot structure */
14627
14628   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14629     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14630   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14631     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14632   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14633     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14634
14635   /* save further RND engine values */
14636
14637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14638   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14640
14641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14642   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14645
14646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14651
14652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14655
14656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14657
14658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14659
14660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14662
14663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14681
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14684
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14688
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14691
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14697
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14700
14701   /* save level identification information */
14702
14703   setString(&snapshot_level_identifier, leveldir_current->identifier);
14704   snapshot_level_nr = level_nr;
14705
14706 #if 0
14707   ListNode *node = engine_snapshot_list_rnd;
14708   int num_bytes = 0;
14709
14710   while (node != NULL)
14711   {
14712     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14713
14714     node = node->next;
14715   }
14716
14717   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14718 #endif
14719
14720   return buffers;
14721 }
14722
14723 void SaveEngineSnapshotSingle()
14724 {
14725   ListNode *buffers = SaveEngineSnapshotBuffers();
14726
14727   /* finally save all snapshot buffers to single snapshot */
14728   SaveSnapshotSingle(buffers);
14729 }
14730
14731 void SaveEngineSnapshotToList()
14732 {
14733   ListNode *buffers = SaveEngineSnapshotBuffers();
14734
14735   /* finally save all snapshot buffers to snapshot list */
14736   SaveSnapshotToList(buffers);
14737 }
14738
14739 void LoadEngineSnapshotValues()
14740 {
14741   /* restore special values from snapshot structure */
14742
14743   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14744     LoadEngineSnapshotValues_RND();
14745   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14746     LoadEngineSnapshotValues_EM();
14747   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14748     LoadEngineSnapshotValues_SP();
14749 }
14750
14751 void LoadEngineSnapshotSingle()
14752 {
14753   LoadSnapshotSingle();
14754
14755   LoadEngineSnapshotValues();
14756 }
14757
14758 void LoadEngineSnapshot_Undo(int steps)
14759 {
14760   LoadSnapshotFromList_Older(steps);
14761
14762   LoadEngineSnapshotValues();
14763 }
14764
14765 void LoadEngineSnapshot_Redo(int steps)
14766 {
14767   LoadSnapshotFromList_Newer(steps);
14768
14769   LoadEngineSnapshotValues();
14770 }
14771
14772 boolean CheckEngineSnapshot()
14773 {
14774   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14775           snapshot_level_nr == level_nr);
14776 }
14777
14778
14779 /* ---------- new game button stuff ---------------------------------------- */
14780
14781 static struct
14782 {
14783   int graphic;
14784   struct XY *pos;
14785   int gadget_id;
14786   char *infotext;
14787 } gamebutton_info[NUM_GAME_BUTTONS] =
14788 {
14789   {
14790     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14791     GAME_CTRL_ID_STOP,                  "stop game"
14792   },
14793   {
14794     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14795     GAME_CTRL_ID_PAUSE,                 "pause game"
14796   },
14797   {
14798     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14799     GAME_CTRL_ID_PLAY,                  "play game"
14800   },
14801   {
14802     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14803     GAME_CTRL_ID_UNDO,                  "undo step"
14804   },
14805   {
14806     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14807     GAME_CTRL_ID_REDO,                  "redo step"
14808   },
14809   {
14810     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14811     GAME_CTRL_ID_SAVE,                  "save game"
14812   },
14813   {
14814     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14815     GAME_CTRL_ID_LOAD,                  "load game"
14816   },
14817   {
14818     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14819     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14820   },
14821   {
14822     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14823     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14824   },
14825   {
14826     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14827     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14828   }
14829 };
14830
14831 void CreateGameButtons()
14832 {
14833   int i;
14834
14835   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14836   {
14837     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14838     struct XY *pos = gamebutton_info[i].pos;
14839     struct GadgetInfo *gi;
14840     int button_type;
14841     boolean checked;
14842     unsigned int event_mask;
14843     int base_x = (tape.show_game_buttons ? VX : DX);
14844     int base_y = (tape.show_game_buttons ? VY : DY);
14845     int gd_x   = gfx->src_x;
14846     int gd_y   = gfx->src_y;
14847     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14848     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14849     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14850     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14851     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14852     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14853     int id = i;
14854
14855     if (gfx->bitmap == NULL)
14856     {
14857       game_gadget[id] = NULL;
14858
14859       continue;
14860     }
14861
14862     if (id == GAME_CTRL_ID_STOP ||
14863         id == GAME_CTRL_ID_PAUSE ||
14864         id == GAME_CTRL_ID_PLAY ||
14865         id == GAME_CTRL_ID_SAVE ||
14866         id == GAME_CTRL_ID_LOAD)
14867     {
14868       button_type = GD_TYPE_NORMAL_BUTTON;
14869       checked = FALSE;
14870       event_mask = GD_EVENT_RELEASED;
14871     }
14872     else if (id == GAME_CTRL_ID_UNDO ||
14873              id == GAME_CTRL_ID_REDO)
14874     {
14875       button_type = GD_TYPE_NORMAL_BUTTON;
14876       checked = FALSE;
14877       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14878     }
14879     else
14880     {
14881       button_type = GD_TYPE_CHECK_BUTTON;
14882       checked =
14883         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14884          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14885          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14886       event_mask = GD_EVENT_PRESSED;
14887     }
14888
14889     gi = CreateGadget(GDI_CUSTOM_ID, id,
14890                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14891                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14892                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14893                       GDI_WIDTH, gfx->width,
14894                       GDI_HEIGHT, gfx->height,
14895                       GDI_TYPE, button_type,
14896                       GDI_STATE, GD_BUTTON_UNPRESSED,
14897                       GDI_CHECKED, checked,
14898                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14899                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14900                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14901                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14902                       GDI_DIRECT_DRAW, FALSE,
14903                       GDI_EVENT_MASK, event_mask,
14904                       GDI_CALLBACK_ACTION, HandleGameButtons,
14905                       GDI_END);
14906
14907     if (gi == NULL)
14908       Error(ERR_EXIT, "cannot create gadget");
14909
14910     game_gadget[id] = gi;
14911   }
14912 }
14913
14914 void FreeGameButtons()
14915 {
14916   int i;
14917
14918   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14919     FreeGadget(game_gadget[i]);
14920 }
14921
14922 void MapStopPlayButtons()
14923 {
14924   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14925   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14926
14927   MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14928   MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14929 }
14930
14931 void MapUndoRedoButtons()
14932 {
14933   UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14934   UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14935
14936   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14937   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14938 }
14939
14940 void MapGameButtons()
14941 {
14942   int i;
14943
14944   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14945     if (i != GAME_CTRL_ID_UNDO &&
14946         i != GAME_CTRL_ID_REDO)
14947       MapGadget(game_gadget[i]);
14948 }
14949
14950 void UnmapGameButtons()
14951 {
14952   int i;
14953
14954   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14955     UnmapGadget(game_gadget[i]);
14956 }
14957
14958 void RedrawGameButtons()
14959 {
14960   int i;
14961
14962   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14963     RedrawGadget(game_gadget[i]);
14964
14965   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14966   redraw_mask &= ~REDRAW_ALL;
14967 }
14968
14969 void GameUndoRedoExt()
14970 {
14971   ClearPlayerAction();
14972
14973   tape.pausing = TRUE;
14974
14975   RedrawPlayfield();
14976   UpdateAndDisplayGameControlValues();
14977
14978   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
14979   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
14980
14981   BackToFront();
14982 }
14983
14984 void GameUndo(int steps)
14985 {
14986   if (!CheckEngineSnapshot())
14987     return;
14988
14989   LoadEngineSnapshot_Undo(steps);
14990
14991   GameUndoRedoExt();
14992 }
14993
14994 void GameRedo(int steps)
14995 {
14996   if (!CheckEngineSnapshot())
14997     return;
14998
14999   LoadEngineSnapshot_Redo(steps);
15000
15001   GameUndoRedoExt();
15002 }
15003
15004 static void HandleGameButtonsExt(int id, int button)
15005 {
15006   int steps = BUTTON_STEPSIZE(button);
15007   boolean handle_game_buttons =
15008     (game_status == GAME_MODE_PLAYING ||
15009      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15010
15011   if (!handle_game_buttons)
15012     return;
15013
15014   switch (id)
15015   {
15016     case GAME_CTRL_ID_STOP:
15017       if (game_status == GAME_MODE_MAIN)
15018         break;
15019
15020       if (tape.playing)
15021         TapeStop();
15022       else
15023         RequestQuitGame(TRUE);
15024
15025       break;
15026
15027     case GAME_CTRL_ID_PAUSE:
15028       if (options.network && game_status == GAME_MODE_PLAYING)
15029       {
15030 #if defined(NETWORK_AVALIABLE)
15031         if (tape.pausing)
15032           SendToServer_ContinuePlaying();
15033         else
15034           SendToServer_PausePlaying();
15035 #endif
15036       }
15037       else
15038         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15039       break;
15040
15041     case GAME_CTRL_ID_PLAY:
15042       if (game_status == GAME_MODE_MAIN)
15043       {
15044         StartGameActions(options.network, setup.autorecord, level.random_seed);
15045       }
15046       else if (tape.pausing)
15047       {
15048 #if defined(NETWORK_AVALIABLE)
15049         if (options.network)
15050           SendToServer_ContinuePlaying();
15051         else
15052 #endif
15053         {
15054           tape.pausing = FALSE;
15055           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15056         }
15057       }
15058       break;
15059
15060     case GAME_CTRL_ID_UNDO:
15061       GameUndo(steps);
15062       break;
15063
15064     case GAME_CTRL_ID_REDO:
15065       GameRedo(steps);
15066       break;
15067
15068     case GAME_CTRL_ID_SAVE:
15069       TapeQuickSave();
15070       break;
15071
15072     case GAME_CTRL_ID_LOAD:
15073       TapeQuickLoad();
15074       break;
15075
15076     case SOUND_CTRL_ID_MUSIC:
15077       if (setup.sound_music)
15078       { 
15079         setup.sound_music = FALSE;
15080
15081         FadeMusic();
15082       }
15083       else if (audio.music_available)
15084       { 
15085         setup.sound = setup.sound_music = TRUE;
15086
15087         SetAudioMode(setup.sound);
15088
15089         PlayLevelMusic();
15090       }
15091       break;
15092
15093     case SOUND_CTRL_ID_LOOPS:
15094       if (setup.sound_loops)
15095         setup.sound_loops = FALSE;
15096       else if (audio.loops_available)
15097       {
15098         setup.sound = setup.sound_loops = TRUE;
15099
15100         SetAudioMode(setup.sound);
15101       }
15102       break;
15103
15104     case SOUND_CTRL_ID_SIMPLE:
15105       if (setup.sound_simple)
15106         setup.sound_simple = FALSE;
15107       else if (audio.sound_available)
15108       {
15109         setup.sound = setup.sound_simple = TRUE;
15110
15111         SetAudioMode(setup.sound);
15112       }
15113       break;
15114
15115     default:
15116       break;
15117   }
15118 }
15119
15120 static void HandleGameButtons(struct GadgetInfo *gi)
15121 {
15122   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15123 }
15124
15125 void HandleSoundButtonKeys(Key key)
15126 {
15127
15128   if (key == setup.shortcut.sound_simple)
15129     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15130   else if (key == setup.shortcut.sound_loops)
15131     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15132   else if (key == setup.shortcut.sound_music)
15133     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15134 }