small cleanup of screen fading code
[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_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   game_status = GAME_MODE_PSEUDO_PANEL;
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   game_status = GAME_MODE_PLAYING;
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.mode =
3044     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3045      SNAPSHOT_MODE_EVERY_STEP :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3047      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 }
3049
3050 int get_num_special_action(int element, int action_first, int action_last)
3051 {
3052   int num_special_action = 0;
3053   int i, j;
3054
3055   for (i = action_first; i <= action_last; i++)
3056   {
3057     boolean found = FALSE;
3058
3059     for (j = 0; j < NUM_DIRECTIONS; j++)
3060       if (el_act_dir2img(element, i, j) !=
3061           el_act_dir2img(element, ACTION_DEFAULT, j))
3062         found = TRUE;
3063
3064     if (found)
3065       num_special_action++;
3066     else
3067       break;
3068   }
3069
3070   return num_special_action;
3071 }
3072
3073
3074 /*
3075   =============================================================================
3076   InitGame()
3077   -----------------------------------------------------------------------------
3078   initialize and start new game
3079   =============================================================================
3080 */
3081
3082 void InitGame()
3083 {
3084   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3085   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int fade_mask = REDRAW_FIELD;
3087
3088   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3089   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3090   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3091   int initial_move_dir = MV_DOWN;
3092   int i, j, x, y;
3093
3094   game_status = GAME_MODE_PLAYING;
3095
3096   StopAnimation();
3097
3098   if (!game.restart_level)
3099     CloseDoor(DOOR_CLOSE_1);
3100
3101   /* needed if different viewport properties defined for playing */
3102   ChangeViewportPropertiesIfNeeded();
3103
3104   if (level_editor_test_game)
3105     FadeSkipNextFadeIn();
3106   else
3107     FadeSetEnterScreen();
3108
3109   if (CheckIfRedrawGlobalBorderIsNeeded())
3110     fade_mask = REDRAW_ALL;
3111
3112   FadeOut(fade_mask);
3113
3114   ClearField();
3115
3116   DrawCompleteVideoDisplay();
3117
3118   InitGameEngine();
3119   InitGameControlValues();
3120
3121   /* don't play tapes over network */
3122   network_playing = (options.network && !tape.playing);
3123
3124   for (i = 0; i < MAX_PLAYERS; i++)
3125   {
3126     struct PlayerInfo *player = &stored_player[i];
3127
3128     player->index_nr = i;
3129     player->index_bit = (1 << i);
3130     player->element_nr = EL_PLAYER_1 + i;
3131
3132     player->present = FALSE;
3133     player->active = FALSE;
3134     player->mapped = FALSE;
3135
3136     player->killed = FALSE;
3137     player->reanimated = FALSE;
3138
3139     player->action = 0;
3140     player->effective_action = 0;
3141     player->programmed_action = 0;
3142
3143     player->score = 0;
3144     player->score_final = 0;
3145
3146     player->gems_still_needed = level.gems_needed;
3147     player->sokobanfields_still_needed = 0;
3148     player->lights_still_needed = 0;
3149     player->friends_still_needed = 0;
3150
3151     for (j = 0; j < MAX_NUM_KEYS; j++)
3152       player->key[j] = FALSE;
3153
3154     player->num_white_keys = 0;
3155
3156     player->dynabomb_count = 0;
3157     player->dynabomb_size = 1;
3158     player->dynabombs_left = 0;
3159     player->dynabomb_xl = FALSE;
3160
3161     player->MovDir = initial_move_dir;
3162     player->MovPos = 0;
3163     player->GfxPos = 0;
3164     player->GfxDir = initial_move_dir;
3165     player->GfxAction = ACTION_DEFAULT;
3166     player->Frame = 0;
3167     player->StepFrame = 0;
3168
3169     player->initial_element = player->element_nr;
3170     player->artwork_element =
3171       (level.use_artwork_element[i] ? level.artwork_element[i] :
3172        player->element_nr);
3173     player->use_murphy = FALSE;
3174
3175     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3176     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3177
3178     player->gravity = level.initial_player_gravity[i];
3179
3180     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3181
3182     player->actual_frame_counter = 0;
3183
3184     player->step_counter = 0;
3185
3186     player->last_move_dir = initial_move_dir;
3187
3188     player->is_active = FALSE;
3189
3190     player->is_waiting = FALSE;
3191     player->is_moving = FALSE;
3192     player->is_auto_moving = FALSE;
3193     player->is_digging = FALSE;
3194     player->is_snapping = FALSE;
3195     player->is_collecting = FALSE;
3196     player->is_pushing = FALSE;
3197     player->is_switching = FALSE;
3198     player->is_dropping = FALSE;
3199     player->is_dropping_pressed = FALSE;
3200
3201     player->is_bored = FALSE;
3202     player->is_sleeping = FALSE;
3203
3204     player->frame_counter_bored = -1;
3205     player->frame_counter_sleeping = -1;
3206
3207     player->anim_delay_counter = 0;
3208     player->post_delay_counter = 0;
3209
3210     player->dir_waiting = initial_move_dir;
3211     player->action_waiting = ACTION_DEFAULT;
3212     player->last_action_waiting = ACTION_DEFAULT;
3213     player->special_action_bored = ACTION_DEFAULT;
3214     player->special_action_sleeping = ACTION_DEFAULT;
3215
3216     player->switch_x = -1;
3217     player->switch_y = -1;
3218
3219     player->drop_x = -1;
3220     player->drop_y = -1;
3221
3222     player->show_envelope = 0;
3223
3224     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3225
3226     player->push_delay       = -1;      /* initialized when pushing starts */
3227     player->push_delay_value = game.initial_push_delay_value;
3228
3229     player->drop_delay = 0;
3230     player->drop_pressed_delay = 0;
3231
3232     player->last_jx = -1;
3233     player->last_jy = -1;
3234     player->jx = -1;
3235     player->jy = -1;
3236
3237     player->shield_normal_time_left = 0;
3238     player->shield_deadly_time_left = 0;
3239
3240     player->inventory_infinite_element = EL_UNDEFINED;
3241     player->inventory_size = 0;
3242
3243     if (level.use_initial_inventory[i])
3244     {
3245       for (j = 0; j < level.initial_inventory_size[i]; j++)
3246       {
3247         int element = level.initial_inventory_content[i][j];
3248         int collect_count = element_info[element].collect_count_initial;
3249         int k;
3250
3251         if (!IS_CUSTOM_ELEMENT(element))
3252           collect_count = 1;
3253
3254         if (collect_count == 0)
3255           player->inventory_infinite_element = element;
3256         else
3257           for (k = 0; k < collect_count; k++)
3258             if (player->inventory_size < MAX_INVENTORY_SIZE)
3259               player->inventory_element[player->inventory_size++] = element;
3260       }
3261     }
3262
3263     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3264     SnapField(player, 0, 0);
3265
3266     player->LevelSolved = FALSE;
3267     player->GameOver = FALSE;
3268
3269     player->LevelSolved_GameWon = FALSE;
3270     player->LevelSolved_GameEnd = FALSE;
3271     player->LevelSolved_PanelOff = FALSE;
3272     player->LevelSolved_SaveTape = FALSE;
3273     player->LevelSolved_SaveScore = FALSE;
3274     player->LevelSolved_CountingTime = 0;
3275     player->LevelSolved_CountingScore = 0;
3276
3277     map_player_action[i] = i;
3278   }
3279
3280   network_player_action_received = FALSE;
3281
3282 #if defined(NETWORK_AVALIABLE)
3283   /* initial null action */
3284   if (network_playing)
3285     SendToServer_MovePlayer(MV_NONE);
3286 #endif
3287
3288   ZX = ZY = -1;
3289   ExitX = ExitY = -1;
3290
3291   FrameCounter = 0;
3292   TimeFrames = 0;
3293   TimePlayed = 0;
3294   TimeLeft = level.time;
3295   TapeTime = 0;
3296
3297   ScreenMovDir = MV_NONE;
3298   ScreenMovPos = 0;
3299   ScreenGfxPos = 0;
3300
3301   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3302
3303   AllPlayersGone = FALSE;
3304
3305   game.no_time_limit = (level.time == 0);
3306
3307   game.yamyam_content_nr = 0;
3308   game.robot_wheel_active = FALSE;
3309   game.magic_wall_active = FALSE;
3310   game.magic_wall_time_left = 0;
3311   game.light_time_left = 0;
3312   game.timegate_time_left = 0;
3313   game.switchgate_pos = 0;
3314   game.wind_direction = level.wind_direction_initial;
3315
3316   game.lenses_time_left = 0;
3317   game.magnify_time_left = 0;
3318
3319   game.ball_state = level.ball_state_initial;
3320   game.ball_content_nr = 0;
3321
3322   game.envelope_active = FALSE;
3323
3324   /* set focus to local player for network games, else to all players */
3325   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3326   game.centered_player_nr_next = game.centered_player_nr;
3327   game.set_centered_player = FALSE;
3328
3329   if (network_playing && tape.recording)
3330   {
3331     /* store client dependent player focus when recording network games */
3332     tape.centered_player_nr_next = game.centered_player_nr_next;
3333     tape.set_centered_player = TRUE;
3334   }
3335
3336   for (i = 0; i < NUM_BELTS; i++)
3337   {
3338     game.belt_dir[i] = MV_NONE;
3339     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3340   }
3341
3342   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3343     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3344
3345 #if DEBUG_INIT_PLAYER
3346   if (options.debug)
3347   {
3348     printf("Player status at level initialization:\n");
3349   }
3350 #endif
3351
3352   SCAN_PLAYFIELD(x, y)
3353   {
3354     Feld[x][y] = level.field[x][y];
3355     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3356     ChangeDelay[x][y] = 0;
3357     ChangePage[x][y] = -1;
3358     CustomValue[x][y] = 0;              /* initialized in InitField() */
3359     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3360     AmoebaNr[x][y] = 0;
3361     WasJustMoving[x][y] = 0;
3362     WasJustFalling[x][y] = 0;
3363     CheckCollision[x][y] = 0;
3364     CheckImpact[x][y] = 0;
3365     Stop[x][y] = FALSE;
3366     Pushed[x][y] = FALSE;
3367
3368     ChangeCount[x][y] = 0;
3369     ChangeEvent[x][y] = -1;
3370
3371     ExplodePhase[x][y] = 0;
3372     ExplodeDelay[x][y] = 0;
3373     ExplodeField[x][y] = EX_TYPE_NONE;
3374
3375     RunnerVisit[x][y] = 0;
3376     PlayerVisit[x][y] = 0;
3377
3378     GfxFrame[x][y] = 0;
3379     GfxRandom[x][y] = INIT_GFX_RANDOM();
3380     GfxElement[x][y] = EL_UNDEFINED;
3381     GfxAction[x][y] = ACTION_DEFAULT;
3382     GfxDir[x][y] = MV_NONE;
3383     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3384   }
3385
3386   SCAN_PLAYFIELD(x, y)
3387   {
3388     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3389       emulate_bd = FALSE;
3390     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3391       emulate_sb = FALSE;
3392     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3393       emulate_sp = FALSE;
3394
3395     InitField(x, y, TRUE);
3396
3397     ResetGfxAnimation(x, y);
3398   }
3399
3400   InitBeltMovement();
3401
3402   for (i = 0; i < MAX_PLAYERS; i++)
3403   {
3404     struct PlayerInfo *player = &stored_player[i];
3405
3406     /* set number of special actions for bored and sleeping animation */
3407     player->num_special_action_bored =
3408       get_num_special_action(player->artwork_element,
3409                              ACTION_BORING_1, ACTION_BORING_LAST);
3410     player->num_special_action_sleeping =
3411       get_num_special_action(player->artwork_element,
3412                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3413   }
3414
3415   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3416                     emulate_sb ? EMU_SOKOBAN :
3417                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3418
3419   /* initialize type of slippery elements */
3420   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421   {
3422     if (!IS_CUSTOM_ELEMENT(i))
3423     {
3424       /* default: elements slip down either to the left or right randomly */
3425       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3426
3427       /* SP style elements prefer to slip down on the left side */
3428       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3429         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3430
3431       /* BD style elements prefer to slip down on the left side */
3432       if (game.emulation == EMU_BOULDERDASH)
3433         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3434     }
3435   }
3436
3437   /* initialize explosion and ignition delay */
3438   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3439   {
3440     if (!IS_CUSTOM_ELEMENT(i))
3441     {
3442       int num_phase = 8;
3443       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3444                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3445                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3446       int last_phase = (num_phase + 1) * delay;
3447       int half_phase = (num_phase / 2) * delay;
3448
3449       element_info[i].explosion_delay = last_phase - 1;
3450       element_info[i].ignition_delay = half_phase;
3451
3452       if (i == EL_BLACK_ORB)
3453         element_info[i].ignition_delay = 1;
3454     }
3455   }
3456
3457   /* correct non-moving belts to start moving left */
3458   for (i = 0; i < NUM_BELTS; i++)
3459     if (game.belt_dir[i] == MV_NONE)
3460       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3461
3462 #if USE_NEW_PLAYER_ASSIGNMENTS
3463   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3464   /* choose default local player */
3465   local_player = &stored_player[0];
3466
3467   for (i = 0; i < MAX_PLAYERS; i++)
3468     stored_player[i].connected = FALSE;
3469
3470   local_player->connected = TRUE;
3471   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3472
3473   if (tape.playing)
3474   {
3475     for (i = 0; i < MAX_PLAYERS; i++)
3476       stored_player[i].connected = tape.player_participates[i];
3477   }
3478   else if (game.team_mode && !options.network)
3479   {
3480     /* try to guess locally connected team mode players (needed for correct
3481        assignment of player figures from level to locally playing players) */
3482
3483     for (i = 0; i < MAX_PLAYERS; i++)
3484       if (setup.input[i].use_joystick ||
3485           setup.input[i].key.left != KSYM_UNDEFINED)
3486         stored_player[i].connected = TRUE;
3487   }
3488
3489 #if DEBUG_INIT_PLAYER
3490   if (options.debug)
3491   {
3492     printf("Player status after level initialization:\n");
3493
3494     for (i = 0; i < MAX_PLAYERS; i++)
3495     {
3496       struct PlayerInfo *player = &stored_player[i];
3497
3498       printf("- player %d: present == %d, connected == %d, active == %d",
3499              i + 1,
3500              player->present,
3501              player->connected,
3502              player->active);
3503
3504       if (local_player == player)
3505         printf(" (local player)");
3506
3507       printf("\n");
3508     }
3509   }
3510 #endif
3511
3512 #if DEBUG_INIT_PLAYER
3513   if (options.debug)
3514     printf("Reassigning players ...\n");
3515 #endif
3516
3517   /* check if any connected player was not found in playfield */
3518   for (i = 0; i < MAX_PLAYERS; i++)
3519   {
3520     struct PlayerInfo *player = &stored_player[i];
3521
3522     if (player->connected && !player->present)
3523     {
3524       struct PlayerInfo *field_player = NULL;
3525
3526 #if DEBUG_INIT_PLAYER
3527       if (options.debug)
3528         printf("- looking for field player for player %d ...\n", i + 1);
3529 #endif
3530
3531       /* assign first free player found that is present in the playfield */
3532
3533       /* first try: look for unmapped playfield player that is not connected */
3534       for (j = 0; j < MAX_PLAYERS; j++)
3535         if (field_player == NULL &&
3536             stored_player[j].present &&
3537             !stored_player[j].mapped &&
3538             !stored_player[j].connected)
3539           field_player = &stored_player[j];
3540
3541       /* second try: look for *any* unmapped playfield player */
3542       for (j = 0; j < MAX_PLAYERS; j++)
3543         if (field_player == NULL &&
3544             stored_player[j].present &&
3545             !stored_player[j].mapped)
3546           field_player = &stored_player[j];
3547
3548       if (field_player != NULL)
3549       {
3550         int jx = field_player->jx, jy = field_player->jy;
3551
3552 #if DEBUG_INIT_PLAYER
3553         if (options.debug)
3554           printf("- found player %d\n", field_player->index_nr + 1);
3555 #endif
3556
3557         player->present = FALSE;
3558         player->active = FALSE;
3559
3560         field_player->present = TRUE;
3561         field_player->active = TRUE;
3562
3563         /*
3564         player->initial_element = field_player->initial_element;
3565         player->artwork_element = field_player->artwork_element;
3566
3567         player->block_last_field       = field_player->block_last_field;
3568         player->block_delay_adjustment = field_player->block_delay_adjustment;
3569         */
3570
3571         StorePlayer[jx][jy] = field_player->element_nr;
3572
3573         field_player->jx = field_player->last_jx = jx;
3574         field_player->jy = field_player->last_jy = jy;
3575
3576         if (local_player == player)
3577           local_player = field_player;
3578
3579         map_player_action[field_player->index_nr] = i;
3580
3581         field_player->mapped = TRUE;
3582
3583 #if DEBUG_INIT_PLAYER
3584         if (options.debug)
3585           printf("- map_player_action[%d] == %d\n",
3586                  field_player->index_nr + 1, i + 1);
3587 #endif
3588       }
3589     }
3590
3591     if (player->connected && player->present)
3592       player->mapped = TRUE;
3593   }
3594
3595 #if DEBUG_INIT_PLAYER
3596   if (options.debug)
3597   {
3598     printf("Player status after player assignment (first stage):\n");
3599
3600     for (i = 0; i < MAX_PLAYERS; i++)
3601     {
3602       struct PlayerInfo *player = &stored_player[i];
3603
3604       printf("- player %d: present == %d, connected == %d, active == %d",
3605              i + 1,
3606              player->present,
3607              player->connected,
3608              player->active);
3609
3610       if (local_player == player)
3611         printf(" (local player)");
3612
3613       printf("\n");
3614     }
3615   }
3616 #endif
3617
3618 #else
3619
3620   /* check if any connected player was not found in playfield */
3621   for (i = 0; i < MAX_PLAYERS; i++)
3622   {
3623     struct PlayerInfo *player = &stored_player[i];
3624
3625     if (player->connected && !player->present)
3626     {
3627       for (j = 0; j < MAX_PLAYERS; j++)
3628       {
3629         struct PlayerInfo *field_player = &stored_player[j];
3630         int jx = field_player->jx, jy = field_player->jy;
3631
3632         /* assign first free player found that is present in the playfield */
3633         if (field_player->present && !field_player->connected)
3634         {
3635           player->present = TRUE;
3636           player->active = TRUE;
3637
3638           field_player->present = FALSE;
3639           field_player->active = FALSE;
3640
3641           player->initial_element = field_player->initial_element;
3642           player->artwork_element = field_player->artwork_element;
3643
3644           player->block_last_field       = field_player->block_last_field;
3645           player->block_delay_adjustment = field_player->block_delay_adjustment;
3646
3647           StorePlayer[jx][jy] = player->element_nr;
3648
3649           player->jx = player->last_jx = jx;
3650           player->jy = player->last_jy = jy;
3651
3652           break;
3653         }
3654       }
3655     }
3656   }
3657 #endif
3658
3659 #if 0
3660   printf("::: local_player->present == %d\n", local_player->present);
3661 #endif
3662
3663   if (tape.playing)
3664   {
3665     /* when playing a tape, eliminate all players who do not participate */
3666
3667 #if USE_NEW_PLAYER_ASSIGNMENTS
3668
3669     if (!game.team_mode)
3670     {
3671       for (i = 0; i < MAX_PLAYERS; i++)
3672       {
3673         if (stored_player[i].active &&
3674             !tape.player_participates[map_player_action[i]])
3675         {
3676           struct PlayerInfo *player = &stored_player[i];
3677           int jx = player->jx, jy = player->jy;
3678
3679 #if DEBUG_INIT_PLAYER
3680           if (options.debug)
3681             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3682 #endif
3683
3684           player->active = FALSE;
3685           StorePlayer[jx][jy] = 0;
3686           Feld[jx][jy] = EL_EMPTY;
3687         }
3688       }
3689     }
3690
3691 #else
3692
3693     for (i = 0; i < MAX_PLAYERS; i++)
3694     {
3695       if (stored_player[i].active &&
3696           !tape.player_participates[i])
3697       {
3698         struct PlayerInfo *player = &stored_player[i];
3699         int jx = player->jx, jy = player->jy;
3700
3701         player->active = FALSE;
3702         StorePlayer[jx][jy] = 0;
3703         Feld[jx][jy] = EL_EMPTY;
3704       }
3705     }
3706 #endif
3707   }
3708   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3709   {
3710     /* when in single player mode, eliminate all but the first active player */
3711
3712     for (i = 0; i < MAX_PLAYERS; i++)
3713     {
3714       if (stored_player[i].active)
3715       {
3716         for (j = i + 1; j < MAX_PLAYERS; j++)
3717         {
3718           if (stored_player[j].active)
3719           {
3720             struct PlayerInfo *player = &stored_player[j];
3721             int jx = player->jx, jy = player->jy;
3722
3723             player->active = FALSE;
3724             player->present = FALSE;
3725
3726             StorePlayer[jx][jy] = 0;
3727             Feld[jx][jy] = EL_EMPTY;
3728           }
3729         }
3730       }
3731     }
3732   }
3733
3734   /* when recording the game, store which players take part in the game */
3735   if (tape.recording)
3736   {
3737 #if USE_NEW_PLAYER_ASSIGNMENTS
3738     for (i = 0; i < MAX_PLAYERS; i++)
3739       if (stored_player[i].connected)
3740         tape.player_participates[i] = TRUE;
3741 #else
3742     for (i = 0; i < MAX_PLAYERS; i++)
3743       if (stored_player[i].active)
3744         tape.player_participates[i] = TRUE;
3745 #endif
3746   }
3747
3748 #if DEBUG_INIT_PLAYER
3749   if (options.debug)
3750   {
3751     printf("Player status after player assignment (final stage):\n");
3752
3753     for (i = 0; i < MAX_PLAYERS; i++)
3754     {
3755       struct PlayerInfo *player = &stored_player[i];
3756
3757       printf("- player %d: present == %d, connected == %d, active == %d",
3758              i + 1,
3759              player->present,
3760              player->connected,
3761              player->active);
3762
3763       if (local_player == player)
3764         printf(" (local player)");
3765
3766       printf("\n");
3767     }
3768   }
3769 #endif
3770
3771   if (BorderElement == EL_EMPTY)
3772   {
3773     SBX_Left = 0;
3774     SBX_Right = lev_fieldx - SCR_FIELDX;
3775     SBY_Upper = 0;
3776     SBY_Lower = lev_fieldy - SCR_FIELDY;
3777   }
3778   else
3779   {
3780     SBX_Left = -1;
3781     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3782     SBY_Upper = -1;
3783     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3784   }
3785
3786   if (full_lev_fieldx <= SCR_FIELDX)
3787     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3788   if (full_lev_fieldy <= SCR_FIELDY)
3789     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3790
3791   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3792     SBX_Left--;
3793   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3794     SBY_Upper--;
3795
3796   /* if local player not found, look for custom element that might create
3797      the player (make some assumptions about the right custom element) */
3798   if (!local_player->present)
3799   {
3800     int start_x = 0, start_y = 0;
3801     int found_rating = 0;
3802     int found_element = EL_UNDEFINED;
3803     int player_nr = local_player->index_nr;
3804
3805     SCAN_PLAYFIELD(x, y)
3806     {
3807       int element = Feld[x][y];
3808       int content;
3809       int xx, yy;
3810       boolean is_player;
3811
3812       if (level.use_start_element[player_nr] &&
3813           level.start_element[player_nr] == element &&
3814           found_rating < 4)
3815       {
3816         start_x = x;
3817         start_y = y;
3818
3819         found_rating = 4;
3820         found_element = element;
3821       }
3822
3823       if (!IS_CUSTOM_ELEMENT(element))
3824         continue;
3825
3826       if (CAN_CHANGE(element))
3827       {
3828         for (i = 0; i < element_info[element].num_change_pages; i++)
3829         {
3830           /* check for player created from custom element as single target */
3831           content = element_info[element].change_page[i].target_element;
3832           is_player = ELEM_IS_PLAYER(content);
3833
3834           if (is_player && (found_rating < 3 ||
3835                             (found_rating == 3 && element < found_element)))
3836           {
3837             start_x = x;
3838             start_y = y;
3839
3840             found_rating = 3;
3841             found_element = element;
3842           }
3843         }
3844       }
3845
3846       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3847       {
3848         /* check for player created from custom element as explosion content */
3849         content = element_info[element].content.e[xx][yy];
3850         is_player = ELEM_IS_PLAYER(content);
3851
3852         if (is_player && (found_rating < 2 ||
3853                           (found_rating == 2 && element < found_element)))
3854         {
3855           start_x = x + xx - 1;
3856           start_y = y + yy - 1;
3857
3858           found_rating = 2;
3859           found_element = element;
3860         }
3861
3862         if (!CAN_CHANGE(element))
3863           continue;
3864
3865         for (i = 0; i < element_info[element].num_change_pages; i++)
3866         {
3867           /* check for player created from custom element as extended target */
3868           content =
3869             element_info[element].change_page[i].target_content.e[xx][yy];
3870
3871           is_player = ELEM_IS_PLAYER(content);
3872
3873           if (is_player && (found_rating < 1 ||
3874                             (found_rating == 1 && element < found_element)))
3875           {
3876             start_x = x + xx - 1;
3877             start_y = y + yy - 1;
3878
3879             found_rating = 1;
3880             found_element = element;
3881           }
3882         }
3883       }
3884     }
3885
3886     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3887                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3888                 start_x - MIDPOSX);
3889
3890     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3891                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3892                 start_y - MIDPOSY);
3893   }
3894   else
3895   {
3896     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3897                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3898                 local_player->jx - MIDPOSX);
3899
3900     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3901                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3902                 local_player->jy - MIDPOSY);
3903   }
3904
3905   /* !!! FIX THIS (START) !!! */
3906   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3907   {
3908     InitGameEngine_EM();
3909   }
3910   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3911   {
3912     InitGameEngine_SP();
3913   }
3914   else
3915   {
3916     DrawLevel(REDRAW_FIELD);
3917     DrawAllPlayers();
3918
3919     /* after drawing the level, correct some elements */
3920     if (game.timegate_time_left == 0)
3921       CloseAllOpenTimegates();
3922   }
3923
3924   /* blit playfield from scroll buffer to normal back buffer for fading in */
3925   BlitScreenToBitmap(backbuffer);
3926   /* !!! FIX THIS (END) !!! */
3927
3928   FadeIn(fade_mask);
3929
3930 #if 1
3931   // full screen redraw is required at this point in the following cases:
3932   // - special editor door undrawn when game was started from level editor
3933   // - drawing area (playfield) was changed and has to be removed completely
3934   redraw_mask = REDRAW_ALL;
3935   BackToFront();
3936 #endif
3937
3938   if (!game.restart_level)
3939   {
3940     /* copy default game door content to main double buffer */
3941
3942     /* !!! CHECK AGAIN !!! */
3943     SetPanelBackground();
3944     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3945     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3946   }
3947
3948   SetPanelBackground();
3949   SetDrawBackgroundMask(REDRAW_DOOR_1);
3950
3951   UpdateAndDisplayGameControlValues();
3952
3953   if (!game.restart_level)
3954   {
3955     UnmapGameButtons();
3956     UnmapTapeButtons();
3957     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3958     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3959     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3960     MapGameButtons();
3961     MapTapeButtons();
3962
3963     /* copy actual game door content to door double buffer for OpenDoor() */
3964     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3965
3966     OpenDoor(DOOR_OPEN_ALL);
3967
3968     PlaySound(SND_GAME_STARTING);
3969
3970     if (setup.sound_music)
3971       PlayLevelMusic();
3972
3973     KeyboardAutoRepeatOffUnlessAutoplay();
3974
3975 #if DEBUG_INIT_PLAYER
3976     if (options.debug)
3977     {
3978       printf("Player status (final):\n");
3979
3980       for (i = 0; i < MAX_PLAYERS; i++)
3981       {
3982         struct PlayerInfo *player = &stored_player[i];
3983
3984         printf("- player %d: present == %d, connected == %d, active == %d",
3985                i + 1,
3986                player->present,
3987                player->connected,
3988                player->active);
3989
3990         if (local_player == player)
3991           printf(" (local player)");
3992
3993         printf("\n");
3994       }
3995     }
3996 #endif
3997   }
3998
3999   UnmapAllGadgets();
4000
4001   MapGameButtons();
4002   MapTapeButtons();
4003
4004   if (!game.restart_level && !tape.playing)
4005   {
4006     LevelStats_incPlayed(level_nr);
4007
4008     SaveLevelSetup_SeriesInfo();
4009   }
4010
4011   game.restart_level = FALSE;
4012
4013   SaveEngineSnapshotToListInitial();
4014 }
4015
4016 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4017 {
4018   /* this is used for non-R'n'D game engines to update certain engine values */
4019
4020   /* needed to determine if sounds are played within the visible screen area */
4021   scroll_x = actual_scroll_x;
4022   scroll_y = actual_scroll_y;
4023 }
4024
4025 void InitMovDir(int x, int y)
4026 {
4027   int i, element = Feld[x][y];
4028   static int xy[4][2] =
4029   {
4030     {  0, +1 },
4031     { +1,  0 },
4032     {  0, -1 },
4033     { -1,  0 }
4034   };
4035   static int direction[3][4] =
4036   {
4037     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4038     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4039     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4040   };
4041
4042   switch (element)
4043   {
4044     case EL_BUG_RIGHT:
4045     case EL_BUG_UP:
4046     case EL_BUG_LEFT:
4047     case EL_BUG_DOWN:
4048       Feld[x][y] = EL_BUG;
4049       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4050       break;
4051
4052     case EL_SPACESHIP_RIGHT:
4053     case EL_SPACESHIP_UP:
4054     case EL_SPACESHIP_LEFT:
4055     case EL_SPACESHIP_DOWN:
4056       Feld[x][y] = EL_SPACESHIP;
4057       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4058       break;
4059
4060     case EL_BD_BUTTERFLY_RIGHT:
4061     case EL_BD_BUTTERFLY_UP:
4062     case EL_BD_BUTTERFLY_LEFT:
4063     case EL_BD_BUTTERFLY_DOWN:
4064       Feld[x][y] = EL_BD_BUTTERFLY;
4065       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4066       break;
4067
4068     case EL_BD_FIREFLY_RIGHT:
4069     case EL_BD_FIREFLY_UP:
4070     case EL_BD_FIREFLY_LEFT:
4071     case EL_BD_FIREFLY_DOWN:
4072       Feld[x][y] = EL_BD_FIREFLY;
4073       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4074       break;
4075
4076     case EL_PACMAN_RIGHT:
4077     case EL_PACMAN_UP:
4078     case EL_PACMAN_LEFT:
4079     case EL_PACMAN_DOWN:
4080       Feld[x][y] = EL_PACMAN;
4081       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4082       break;
4083
4084     case EL_YAMYAM_LEFT:
4085     case EL_YAMYAM_RIGHT:
4086     case EL_YAMYAM_UP:
4087     case EL_YAMYAM_DOWN:
4088       Feld[x][y] = EL_YAMYAM;
4089       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4090       break;
4091
4092     case EL_SP_SNIKSNAK:
4093       MovDir[x][y] = MV_UP;
4094       break;
4095
4096     case EL_SP_ELECTRON:
4097       MovDir[x][y] = MV_LEFT;
4098       break;
4099
4100     case EL_MOLE_LEFT:
4101     case EL_MOLE_RIGHT:
4102     case EL_MOLE_UP:
4103     case EL_MOLE_DOWN:
4104       Feld[x][y] = EL_MOLE;
4105       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4106       break;
4107
4108     default:
4109       if (IS_CUSTOM_ELEMENT(element))
4110       {
4111         struct ElementInfo *ei = &element_info[element];
4112         int move_direction_initial = ei->move_direction_initial;
4113         int move_pattern = ei->move_pattern;
4114
4115         if (move_direction_initial == MV_START_PREVIOUS)
4116         {
4117           if (MovDir[x][y] != MV_NONE)
4118             return;
4119
4120           move_direction_initial = MV_START_AUTOMATIC;
4121         }
4122
4123         if (move_direction_initial == MV_START_RANDOM)
4124           MovDir[x][y] = 1 << RND(4);
4125         else if (move_direction_initial & MV_ANY_DIRECTION)
4126           MovDir[x][y] = move_direction_initial;
4127         else if (move_pattern == MV_ALL_DIRECTIONS ||
4128                  move_pattern == MV_TURNING_LEFT ||
4129                  move_pattern == MV_TURNING_RIGHT ||
4130                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4131                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4132                  move_pattern == MV_TURNING_RANDOM)
4133           MovDir[x][y] = 1 << RND(4);
4134         else if (move_pattern == MV_HORIZONTAL)
4135           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4136         else if (move_pattern == MV_VERTICAL)
4137           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4138         else if (move_pattern & MV_ANY_DIRECTION)
4139           MovDir[x][y] = element_info[element].move_pattern;
4140         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4141                  move_pattern == MV_ALONG_RIGHT_SIDE)
4142         {
4143           /* use random direction as default start direction */
4144           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4145             MovDir[x][y] = 1 << RND(4);
4146
4147           for (i = 0; i < NUM_DIRECTIONS; i++)
4148           {
4149             int x1 = x + xy[i][0];
4150             int y1 = y + xy[i][1];
4151
4152             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4153             {
4154               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4155                 MovDir[x][y] = direction[0][i];
4156               else
4157                 MovDir[x][y] = direction[1][i];
4158
4159               break;
4160             }
4161           }
4162         }                
4163       }
4164       else
4165       {
4166         MovDir[x][y] = 1 << RND(4);
4167
4168         if (element != EL_BUG &&
4169             element != EL_SPACESHIP &&
4170             element != EL_BD_BUTTERFLY &&
4171             element != EL_BD_FIREFLY)
4172           break;
4173
4174         for (i = 0; i < NUM_DIRECTIONS; i++)
4175         {
4176           int x1 = x + xy[i][0];
4177           int y1 = y + xy[i][1];
4178
4179           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4180           {
4181             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4182             {
4183               MovDir[x][y] = direction[0][i];
4184               break;
4185             }
4186             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4187                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4188             {
4189               MovDir[x][y] = direction[1][i];
4190               break;
4191             }
4192           }
4193         }
4194       }
4195       break;
4196   }
4197
4198   GfxDir[x][y] = MovDir[x][y];
4199 }
4200
4201 void InitAmoebaNr(int x, int y)
4202 {
4203   int i;
4204   int group_nr = AmoebeNachbarNr(x, y);
4205
4206   if (group_nr == 0)
4207   {
4208     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4209     {
4210       if (AmoebaCnt[i] == 0)
4211       {
4212         group_nr = i;
4213         break;
4214       }
4215     }
4216   }
4217
4218   AmoebaNr[x][y] = group_nr;
4219   AmoebaCnt[group_nr]++;
4220   AmoebaCnt2[group_nr]++;
4221 }
4222
4223 static void PlayerWins(struct PlayerInfo *player)
4224 {
4225   player->LevelSolved = TRUE;
4226   player->GameOver = TRUE;
4227
4228   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4229                          level.native_em_level->lev->score : player->score);
4230
4231   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4232                                       TimeLeft);
4233   player->LevelSolved_CountingScore = player->score_final;
4234 }
4235
4236 void GameWon()
4237 {
4238   static int time, time_final;
4239   static int score, score_final;
4240   static int game_over_delay_1 = 0;
4241   static int game_over_delay_2 = 0;
4242   int game_over_delay_value_1 = 50;
4243   int game_over_delay_value_2 = 50;
4244
4245   if (!local_player->LevelSolved_GameWon)
4246   {
4247     int i;
4248
4249     /* do not start end game actions before the player stops moving (to exit) */
4250     if (local_player->MovPos)
4251       return;
4252
4253     local_player->LevelSolved_GameWon = TRUE;
4254     local_player->LevelSolved_SaveTape = tape.recording;
4255     local_player->LevelSolved_SaveScore = !tape.playing;
4256
4257     if (!tape.playing)
4258     {
4259       LevelStats_incSolved(level_nr);
4260
4261       SaveLevelSetup_SeriesInfo();
4262     }
4263
4264     if (tape.auto_play)         /* tape might already be stopped here */
4265       tape.auto_play_level_solved = TRUE;
4266
4267     TapeStop();
4268
4269     game_over_delay_1 = game_over_delay_value_1;
4270     game_over_delay_2 = game_over_delay_value_2;
4271
4272     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4273     score = score_final = local_player->score_final;
4274
4275     if (TimeLeft > 0)
4276     {
4277       time_final = 0;
4278       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4279     }
4280     else if (game.no_time_limit && TimePlayed < 999)
4281     {
4282       time_final = 999;
4283       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4284     }
4285
4286     local_player->score_final = score_final;
4287
4288     if (level_editor_test_game)
4289     {
4290       time = time_final;
4291       score = score_final;
4292
4293       local_player->LevelSolved_CountingTime = time;
4294       local_player->LevelSolved_CountingScore = score;
4295
4296       game_panel_controls[GAME_PANEL_TIME].value = time;
4297       game_panel_controls[GAME_PANEL_SCORE].value = score;
4298
4299       DisplayGameControlValues();
4300     }
4301
4302     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4303     {
4304       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4305       {
4306         /* close exit door after last player */
4307         if ((AllPlayersGone &&
4308              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4309               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4310               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4311             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4312             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4313         {
4314           int element = Feld[ExitX][ExitY];
4315
4316           Feld[ExitX][ExitY] =
4317             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4318              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4319              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4320              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4321              EL_EM_STEEL_EXIT_CLOSING);
4322
4323           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4324         }
4325
4326         /* player disappears */
4327         DrawLevelField(ExitX, ExitY);
4328       }
4329
4330       for (i = 0; i < MAX_PLAYERS; i++)
4331       {
4332         struct PlayerInfo *player = &stored_player[i];
4333
4334         if (player->present)
4335         {
4336           RemovePlayer(player);
4337
4338           /* player disappears */
4339           DrawLevelField(player->jx, player->jy);
4340         }
4341       }
4342     }
4343
4344     PlaySound(SND_GAME_WINNING);
4345   }
4346
4347   if (game_over_delay_1 > 0)
4348   {
4349     game_over_delay_1--;
4350
4351     return;
4352   }
4353
4354   if (time != time_final)
4355   {
4356     int time_to_go = ABS(time_final - time);
4357     int time_count_dir = (time < time_final ? +1 : -1);
4358     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4359
4360     time  += time_count_steps * time_count_dir;
4361     score += time_count_steps * level.score[SC_TIME_BONUS];
4362
4363     local_player->LevelSolved_CountingTime = time;
4364     local_player->LevelSolved_CountingScore = score;
4365
4366     game_panel_controls[GAME_PANEL_TIME].value = time;
4367     game_panel_controls[GAME_PANEL_SCORE].value = score;
4368
4369     DisplayGameControlValues();
4370
4371     if (time == time_final)
4372       StopSound(SND_GAME_LEVELTIME_BONUS);
4373     else if (setup.sound_loops)
4374       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4375     else
4376       PlaySound(SND_GAME_LEVELTIME_BONUS);
4377
4378     return;
4379   }
4380
4381   local_player->LevelSolved_PanelOff = TRUE;
4382
4383   if (game_over_delay_2 > 0)
4384   {
4385     game_over_delay_2--;
4386
4387     return;
4388   }
4389
4390   GameEnd();
4391 }
4392
4393 void GameEnd()
4394 {
4395   int hi_pos;
4396   boolean raise_level = FALSE;
4397
4398   local_player->LevelSolved_GameEnd = TRUE;
4399
4400   if (!global.use_envelope_request)
4401     CloseDoor(DOOR_CLOSE_1);
4402
4403   if (local_player->LevelSolved_SaveTape)
4404   {
4405     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4406   }
4407
4408   CloseDoor(DOOR_CLOSE_ALL);
4409
4410   if (level_editor_test_game)
4411   {
4412     game_status = GAME_MODE_MAIN;
4413
4414     DrawMainMenu();
4415
4416     return;
4417   }
4418
4419   if (!local_player->LevelSolved_SaveScore)
4420   {
4421     FadeOut(REDRAW_FIELD);
4422
4423     game_status = GAME_MODE_MAIN;
4424
4425     DrawMainMenu();
4426
4427     return;
4428   }
4429
4430   if (level_nr == leveldir_current->handicap_level)
4431   {
4432     leveldir_current->handicap_level++;
4433
4434     SaveLevelSetup_SeriesInfo();
4435   }
4436
4437   if (level_nr < leveldir_current->last_level)
4438     raise_level = TRUE;                 /* advance to next level */
4439
4440   if ((hi_pos = NewHiScore()) >= 0) 
4441   {
4442     game_status = GAME_MODE_SCORES;
4443
4444     DrawHallOfFame(hi_pos);
4445
4446     if (raise_level)
4447     {
4448       level_nr++;
4449       TapeErase();
4450     }
4451   }
4452   else
4453   {
4454     FadeOut(REDRAW_FIELD);
4455
4456     game_status = GAME_MODE_MAIN;
4457
4458     if (raise_level)
4459     {
4460       level_nr++;
4461       TapeErase();
4462     }
4463
4464     DrawMainMenu();
4465   }
4466 }
4467
4468 int NewHiScore()
4469 {
4470   int k, l;
4471   int position = -1;
4472
4473   LoadScore(level_nr);
4474
4475   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4476       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4477     return -1;
4478
4479   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4480   {
4481     if (local_player->score_final > highscore[k].Score)
4482     {
4483       /* player has made it to the hall of fame */
4484
4485       if (k < MAX_SCORE_ENTRIES - 1)
4486       {
4487         int m = MAX_SCORE_ENTRIES - 1;
4488
4489 #ifdef ONE_PER_NAME
4490         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4491           if (strEqual(setup.player_name, highscore[l].Name))
4492             m = l;
4493         if (m == k)     /* player's new highscore overwrites his old one */
4494           goto put_into_list;
4495 #endif
4496
4497         for (l = m; l > k; l--)
4498         {
4499           strcpy(highscore[l].Name, highscore[l - 1].Name);
4500           highscore[l].Score = highscore[l - 1].Score;
4501         }
4502       }
4503
4504 #ifdef ONE_PER_NAME
4505       put_into_list:
4506 #endif
4507       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4508       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4509       highscore[k].Score = local_player->score_final; 
4510       position = k;
4511       break;
4512     }
4513
4514 #ifdef ONE_PER_NAME
4515     else if (!strncmp(setup.player_name, highscore[k].Name,
4516                       MAX_PLAYER_NAME_LEN))
4517       break;    /* player already there with a higher score */
4518 #endif
4519
4520   }
4521
4522   if (position >= 0) 
4523     SaveScore(level_nr);
4524
4525   return position;
4526 }
4527
4528 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4529 {
4530   int element = Feld[x][y];
4531   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4532   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4533   int horiz_move = (dx != 0);
4534   int sign = (horiz_move ? dx : dy);
4535   int step = sign * element_info[element].move_stepsize;
4536
4537   /* special values for move stepsize for spring and things on conveyor belt */
4538   if (horiz_move)
4539   {
4540     if (CAN_FALL(element) &&
4541         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4542       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4543     else if (element == EL_SPRING)
4544       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4545   }
4546
4547   return step;
4548 }
4549
4550 inline static int getElementMoveStepsize(int x, int y)
4551 {
4552   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4553 }
4554
4555 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4556 {
4557   if (player->GfxAction != action || player->GfxDir != dir)
4558   {
4559     player->GfxAction = action;
4560     player->GfxDir = dir;
4561     player->Frame = 0;
4562     player->StepFrame = 0;
4563   }
4564 }
4565
4566 static void ResetGfxFrame(int x, int y, boolean redraw)
4567 {
4568   int element = Feld[x][y];
4569   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4570   int last_gfx_frame = GfxFrame[x][y];
4571
4572   if (graphic_info[graphic].anim_global_sync)
4573     GfxFrame[x][y] = FrameCounter;
4574   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4575     GfxFrame[x][y] = CustomValue[x][y];
4576   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4577     GfxFrame[x][y] = element_info[element].collect_score;
4578   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4579     GfxFrame[x][y] = ChangeDelay[x][y];
4580
4581   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4582     DrawLevelGraphicAnimation(x, y, graphic);
4583 }
4584
4585 static void ResetGfxAnimation(int x, int y)
4586 {
4587   GfxAction[x][y] = ACTION_DEFAULT;
4588   GfxDir[x][y] = MovDir[x][y];
4589   GfxFrame[x][y] = 0;
4590
4591   ResetGfxFrame(x, y, FALSE);
4592 }
4593
4594 static void ResetRandomAnimationValue(int x, int y)
4595 {
4596   GfxRandom[x][y] = INIT_GFX_RANDOM();
4597 }
4598
4599 void InitMovingField(int x, int y, int direction)
4600 {
4601   int element = Feld[x][y];
4602   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4603   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4604   int newx = x + dx;
4605   int newy = y + dy;
4606   boolean is_moving_before, is_moving_after;
4607
4608   /* check if element was/is moving or being moved before/after mode change */
4609   is_moving_before = (WasJustMoving[x][y] != 0);
4610   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4611
4612   /* reset animation only for moving elements which change direction of moving
4613      or which just started or stopped moving
4614      (else CEs with property "can move" / "not moving" are reset each frame) */
4615   if (is_moving_before != is_moving_after ||
4616       direction != MovDir[x][y])
4617     ResetGfxAnimation(x, y);
4618
4619   MovDir[x][y] = direction;
4620   GfxDir[x][y] = direction;
4621
4622   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4623                      direction == MV_DOWN && CAN_FALL(element) ?
4624                      ACTION_FALLING : ACTION_MOVING);
4625
4626   /* this is needed for CEs with property "can move" / "not moving" */
4627
4628   if (is_moving_after)
4629   {
4630     if (Feld[newx][newy] == EL_EMPTY)
4631       Feld[newx][newy] = EL_BLOCKED;
4632
4633     MovDir[newx][newy] = MovDir[x][y];
4634
4635     CustomValue[newx][newy] = CustomValue[x][y];
4636
4637     GfxFrame[newx][newy] = GfxFrame[x][y];
4638     GfxRandom[newx][newy] = GfxRandom[x][y];
4639     GfxAction[newx][newy] = GfxAction[x][y];
4640     GfxDir[newx][newy] = GfxDir[x][y];
4641   }
4642 }
4643
4644 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4645 {
4646   int direction = MovDir[x][y];
4647   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4648   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4649
4650   *goes_to_x = newx;
4651   *goes_to_y = newy;
4652 }
4653
4654 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4655 {
4656   int oldx = x, oldy = y;
4657   int direction = MovDir[x][y];
4658
4659   if (direction == MV_LEFT)
4660     oldx++;
4661   else if (direction == MV_RIGHT)
4662     oldx--;
4663   else if (direction == MV_UP)
4664     oldy++;
4665   else if (direction == MV_DOWN)
4666     oldy--;
4667
4668   *comes_from_x = oldx;
4669   *comes_from_y = oldy;
4670 }
4671
4672 int MovingOrBlocked2Element(int x, int y)
4673 {
4674   int element = Feld[x][y];
4675
4676   if (element == EL_BLOCKED)
4677   {
4678     int oldx, oldy;
4679
4680     Blocked2Moving(x, y, &oldx, &oldy);
4681     return Feld[oldx][oldy];
4682   }
4683   else
4684     return element;
4685 }
4686
4687 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4688 {
4689   /* like MovingOrBlocked2Element(), but if element is moving
4690      and (x,y) is the field the moving element is just leaving,
4691      return EL_BLOCKED instead of the element value */
4692   int element = Feld[x][y];
4693
4694   if (IS_MOVING(x, y))
4695   {
4696     if (element == EL_BLOCKED)
4697     {
4698       int oldx, oldy;
4699
4700       Blocked2Moving(x, y, &oldx, &oldy);
4701       return Feld[oldx][oldy];
4702     }
4703     else
4704       return EL_BLOCKED;
4705   }
4706   else
4707     return element;
4708 }
4709
4710 static void RemoveField(int x, int y)
4711 {
4712   Feld[x][y] = EL_EMPTY;
4713
4714   MovPos[x][y] = 0;
4715   MovDir[x][y] = 0;
4716   MovDelay[x][y] = 0;
4717
4718   CustomValue[x][y] = 0;
4719
4720   AmoebaNr[x][y] = 0;
4721   ChangeDelay[x][y] = 0;
4722   ChangePage[x][y] = -1;
4723   Pushed[x][y] = FALSE;
4724
4725   GfxElement[x][y] = EL_UNDEFINED;
4726   GfxAction[x][y] = ACTION_DEFAULT;
4727   GfxDir[x][y] = MV_NONE;
4728 }
4729
4730 void RemoveMovingField(int x, int y)
4731 {
4732   int oldx = x, oldy = y, newx = x, newy = y;
4733   int element = Feld[x][y];
4734   int next_element = EL_UNDEFINED;
4735
4736   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4737     return;
4738
4739   if (IS_MOVING(x, y))
4740   {
4741     Moving2Blocked(x, y, &newx, &newy);
4742
4743     if (Feld[newx][newy] != EL_BLOCKED)
4744     {
4745       /* element is moving, but target field is not free (blocked), but
4746          already occupied by something different (example: acid pool);
4747          in this case, only remove the moving field, but not the target */
4748
4749       RemoveField(oldx, oldy);
4750
4751       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4752
4753       TEST_DrawLevelField(oldx, oldy);
4754
4755       return;
4756     }
4757   }
4758   else if (element == EL_BLOCKED)
4759   {
4760     Blocked2Moving(x, y, &oldx, &oldy);
4761     if (!IS_MOVING(oldx, oldy))
4762       return;
4763   }
4764
4765   if (element == EL_BLOCKED &&
4766       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4767        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4768        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4769        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4770        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4771        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4772     next_element = get_next_element(Feld[oldx][oldy]);
4773
4774   RemoveField(oldx, oldy);
4775   RemoveField(newx, newy);
4776
4777   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4778
4779   if (next_element != EL_UNDEFINED)
4780     Feld[oldx][oldy] = next_element;
4781
4782   TEST_DrawLevelField(oldx, oldy);
4783   TEST_DrawLevelField(newx, newy);
4784 }
4785
4786 void DrawDynamite(int x, int y)
4787 {
4788   int sx = SCREENX(x), sy = SCREENY(y);
4789   int graphic = el2img(Feld[x][y]);
4790   int frame;
4791
4792   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4793     return;
4794
4795   if (IS_WALKABLE_INSIDE(Back[x][y]))
4796     return;
4797
4798   if (Back[x][y])
4799     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4800   else if (Store[x][y])
4801     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4802
4803   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4804
4805   if (Back[x][y] || Store[x][y])
4806     DrawGraphicThruMask(sx, sy, graphic, frame);
4807   else
4808     DrawGraphic(sx, sy, graphic, frame);
4809 }
4810
4811 void CheckDynamite(int x, int y)
4812 {
4813   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4814   {
4815     MovDelay[x][y]--;
4816
4817     if (MovDelay[x][y] != 0)
4818     {
4819       DrawDynamite(x, y);
4820       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4821
4822       return;
4823     }
4824   }
4825
4826   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4827
4828   Bang(x, y);
4829 }
4830
4831 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4832 {
4833   boolean num_checked_players = 0;
4834   int i;
4835
4836   for (i = 0; i < MAX_PLAYERS; i++)
4837   {
4838     if (stored_player[i].active)
4839     {
4840       int sx = stored_player[i].jx;
4841       int sy = stored_player[i].jy;
4842
4843       if (num_checked_players == 0)
4844       {
4845         *sx1 = *sx2 = sx;
4846         *sy1 = *sy2 = sy;
4847       }
4848       else
4849       {
4850         *sx1 = MIN(*sx1, sx);
4851         *sy1 = MIN(*sy1, sy);
4852         *sx2 = MAX(*sx2, sx);
4853         *sy2 = MAX(*sy2, sy);
4854       }
4855
4856       num_checked_players++;
4857     }
4858   }
4859 }
4860
4861 static boolean checkIfAllPlayersFitToScreen_RND()
4862 {
4863   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4864
4865   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4866
4867   return (sx2 - sx1 < SCR_FIELDX &&
4868           sy2 - sy1 < SCR_FIELDY);
4869 }
4870
4871 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4872 {
4873   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4874
4875   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4876
4877   *sx = (sx1 + sx2) / 2;
4878   *sy = (sy1 + sy2) / 2;
4879 }
4880
4881 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4882                         boolean center_screen, boolean quick_relocation)
4883 {
4884   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4885   boolean no_delay = (tape.warp_forward);
4886   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4887   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4888
4889   if (quick_relocation)
4890   {
4891     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4892     {
4893       if (!level.shifted_relocation || center_screen)
4894       {
4895         /* quick relocation (without scrolling), with centering of screen */
4896
4897         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4898                     x > SBX_Right + MIDPOSX ? SBX_Right :
4899                     x - MIDPOSX);
4900
4901         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4902                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4903                     y - MIDPOSY);
4904       }
4905       else
4906       {
4907         /* quick relocation (without scrolling), but do not center screen */
4908
4909         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4910                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4911                                old_x - MIDPOSX);
4912
4913         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4914                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4915                                old_y - MIDPOSY);
4916
4917         int offset_x = x + (scroll_x - center_scroll_x);
4918         int offset_y = y + (scroll_y - center_scroll_y);
4919
4920         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4921                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4922                     offset_x - MIDPOSX);
4923
4924         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4925                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4926                     offset_y - MIDPOSY);
4927       }
4928     }
4929     else
4930     {
4931       if (!level.shifted_relocation || center_screen)
4932       {
4933         /* quick relocation (without scrolling), with centering of screen */
4934
4935         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4936                     x > SBX_Right + MIDPOSX ? SBX_Right :
4937                     x - MIDPOSX);
4938
4939         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4940                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4941                     y - MIDPOSY);
4942       }
4943       else
4944       {
4945         /* quick relocation (without scrolling), but do not center screen */
4946
4947         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4948                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4949                                old_x - MIDPOSX);
4950
4951         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4952                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4953                                old_y - MIDPOSY);
4954
4955         int offset_x = x + (scroll_x - center_scroll_x);
4956         int offset_y = y + (scroll_y - center_scroll_y);
4957
4958         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4959                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4960                     offset_x - MIDPOSX);
4961
4962         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4963                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4964                     offset_y - MIDPOSY);
4965       }
4966     }
4967
4968     RedrawPlayfield(TRUE, 0,0,0,0);
4969   }
4970   else
4971   {
4972     int scroll_xx, scroll_yy;
4973
4974     if (!level.shifted_relocation || center_screen)
4975     {
4976       /* visible relocation (with scrolling), with centering of screen */
4977
4978       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4979                    x > SBX_Right + MIDPOSX ? SBX_Right :
4980                    x - MIDPOSX);
4981
4982       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4983                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4984                    y - MIDPOSY);
4985     }
4986     else
4987     {
4988       /* visible relocation (with scrolling), but do not center screen */
4989
4990       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4991                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4992                              old_x - MIDPOSX);
4993
4994       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4995                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4996                              old_y - MIDPOSY);
4997
4998       int offset_x = x + (scroll_x - center_scroll_x);
4999       int offset_y = y + (scroll_y - center_scroll_y);
5000
5001       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5002                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5003                    offset_x - MIDPOSX);
5004
5005       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5006                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5007                    offset_y - MIDPOSY);
5008     }
5009
5010
5011     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5012
5013     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5014     {
5015       int dx = 0, dy = 0;
5016       int fx = FX, fy = FY;
5017
5018       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5019       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5020
5021       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5022         break;
5023
5024       scroll_x -= dx;
5025       scroll_y -= dy;
5026
5027       fx += dx * TILEX / 2;
5028       fy += dy * TILEY / 2;
5029
5030       ScrollLevel(dx, dy);
5031       DrawAllPlayers();
5032
5033       /* scroll in two steps of half tile size to make things smoother */
5034       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5035       Delay(wait_delay_value);
5036
5037       /* scroll second step to align at full tile size */
5038       BackToFront();
5039       Delay(wait_delay_value);
5040     }
5041
5042     DrawAllPlayers();
5043     BackToFront();
5044     Delay(wait_delay_value);
5045   }
5046 }
5047
5048 void RelocatePlayer(int jx, int jy, int el_player_raw)
5049 {
5050   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5051   int player_nr = GET_PLAYER_NR(el_player);
5052   struct PlayerInfo *player = &stored_player[player_nr];
5053   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5054   boolean no_delay = (tape.warp_forward);
5055   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5056   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5057   int old_jx = player->jx;
5058   int old_jy = player->jy;
5059   int old_element = Feld[old_jx][old_jy];
5060   int element = Feld[jx][jy];
5061   boolean player_relocated = (old_jx != jx || old_jy != jy);
5062
5063   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5064   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5065   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5066   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5067   int leave_side_horiz = move_dir_horiz;
5068   int leave_side_vert  = move_dir_vert;
5069   int enter_side = enter_side_horiz | enter_side_vert;
5070   int leave_side = leave_side_horiz | leave_side_vert;
5071
5072   if (player->GameOver)         /* do not reanimate dead player */
5073     return;
5074
5075   if (!player_relocated)        /* no need to relocate the player */
5076     return;
5077
5078   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5079   {
5080     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5081     DrawLevelField(jx, jy);
5082   }
5083
5084   if (player->present)
5085   {
5086     while (player->MovPos)
5087     {
5088       ScrollPlayer(player, SCROLL_GO_ON);
5089       ScrollScreen(NULL, SCROLL_GO_ON);
5090
5091       AdvanceFrameAndPlayerCounters(player->index_nr);
5092
5093       DrawPlayer(player);
5094
5095       BackToFront();
5096       Delay(wait_delay_value);
5097     }
5098
5099     DrawPlayer(player);         /* needed here only to cleanup last field */
5100     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5101
5102     player->is_moving = FALSE;
5103   }
5104
5105   if (IS_CUSTOM_ELEMENT(old_element))
5106     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5107                                CE_LEFT_BY_PLAYER,
5108                                player->index_bit, leave_side);
5109
5110   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5111                                       CE_PLAYER_LEAVES_X,
5112                                       player->index_bit, leave_side);
5113
5114   Feld[jx][jy] = el_player;
5115   InitPlayerField(jx, jy, el_player, TRUE);
5116
5117   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5118      possible that the relocation target field did not contain a player element,
5119      but a walkable element, to which the new player was relocated -- in this
5120      case, restore that (already initialized!) element on the player field */
5121   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5122   {
5123     Feld[jx][jy] = element;     /* restore previously existing element */
5124   }
5125
5126   /* only visually relocate centered player */
5127   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5128                      FALSE, level.instant_relocation);
5129
5130   TestIfPlayerTouchesBadThing(jx, jy);
5131   TestIfPlayerTouchesCustomElement(jx, jy);
5132
5133   if (IS_CUSTOM_ELEMENT(element))
5134     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5135                                player->index_bit, enter_side);
5136
5137   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5138                                       player->index_bit, enter_side);
5139
5140   if (player->is_switching)
5141   {
5142     /* ensure that relocation while still switching an element does not cause
5143        a new element to be treated as also switched directly after relocation
5144        (this is important for teleporter switches that teleport the player to
5145        a place where another teleporter switch is in the same direction, which
5146        would then incorrectly be treated as immediately switched before the
5147        direction key that caused the switch was released) */
5148
5149     player->switch_x += jx - old_jx;
5150     player->switch_y += jy - old_jy;
5151   }
5152 }
5153
5154 void Explode(int ex, int ey, int phase, int mode)
5155 {
5156   int x, y;
5157   int last_phase;
5158   int border_element;
5159
5160   /* !!! eliminate this variable !!! */
5161   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5162
5163   if (game.explosions_delayed)
5164   {
5165     ExplodeField[ex][ey] = mode;
5166     return;
5167   }
5168
5169   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5170   {
5171     int center_element = Feld[ex][ey];
5172     int artwork_element, explosion_element;     /* set these values later */
5173
5174     /* remove things displayed in background while burning dynamite */
5175     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5176       Back[ex][ey] = 0;
5177
5178     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5179     {
5180       /* put moving element to center field (and let it explode there) */
5181       center_element = MovingOrBlocked2Element(ex, ey);
5182       RemoveMovingField(ex, ey);
5183       Feld[ex][ey] = center_element;
5184     }
5185
5186     /* now "center_element" is finally determined -- set related values now */
5187     artwork_element = center_element;           /* for custom player artwork */
5188     explosion_element = center_element;         /* for custom player artwork */
5189
5190     if (IS_PLAYER(ex, ey))
5191     {
5192       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5193
5194       artwork_element = stored_player[player_nr].artwork_element;
5195
5196       if (level.use_explosion_element[player_nr])
5197       {
5198         explosion_element = level.explosion_element[player_nr];
5199         artwork_element = explosion_element;
5200       }
5201     }
5202
5203     if (mode == EX_TYPE_NORMAL ||
5204         mode == EX_TYPE_CENTER ||
5205         mode == EX_TYPE_CROSS)
5206       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5207
5208     last_phase = element_info[explosion_element].explosion_delay + 1;
5209
5210     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5211     {
5212       int xx = x - ex + 1;
5213       int yy = y - ey + 1;
5214       int element;
5215
5216       if (!IN_LEV_FIELD(x, y) ||
5217           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5218           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5219         continue;
5220
5221       element = Feld[x][y];
5222
5223       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5224       {
5225         element = MovingOrBlocked2Element(x, y);
5226
5227         if (!IS_EXPLOSION_PROOF(element))
5228           RemoveMovingField(x, y);
5229       }
5230
5231       /* indestructible elements can only explode in center (but not flames) */
5232       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5233                                            mode == EX_TYPE_BORDER)) ||
5234           element == EL_FLAMES)
5235         continue;
5236
5237       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5238          behaviour, for example when touching a yamyam that explodes to rocks
5239          with active deadly shield, a rock is created under the player !!! */
5240       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5241 #if 0
5242       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5243           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5244            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5245 #else
5246       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5247 #endif
5248       {
5249         if (IS_ACTIVE_BOMB(element))
5250         {
5251           /* re-activate things under the bomb like gate or penguin */
5252           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5253           Back[x][y] = 0;
5254         }
5255
5256         continue;
5257       }
5258
5259       /* save walkable background elements while explosion on same tile */
5260       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5261           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5262         Back[x][y] = element;
5263
5264       /* ignite explodable elements reached by other explosion */
5265       if (element == EL_EXPLOSION)
5266         element = Store2[x][y];
5267
5268       if (AmoebaNr[x][y] &&
5269           (element == EL_AMOEBA_FULL ||
5270            element == EL_BD_AMOEBA ||
5271            element == EL_AMOEBA_GROWING))
5272       {
5273         AmoebaCnt[AmoebaNr[x][y]]--;
5274         AmoebaCnt2[AmoebaNr[x][y]]--;
5275       }
5276
5277       RemoveField(x, y);
5278
5279       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5280       {
5281         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5282
5283         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5284
5285         if (PLAYERINFO(ex, ey)->use_murphy)
5286           Store[x][y] = EL_EMPTY;
5287       }
5288
5289       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5290          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5291       else if (ELEM_IS_PLAYER(center_element))
5292         Store[x][y] = EL_EMPTY;
5293       else if (center_element == EL_YAMYAM)
5294         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5295       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5296         Store[x][y] = element_info[center_element].content.e[xx][yy];
5297 #if 1
5298       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5299          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5300          otherwise) -- FIX THIS !!! */
5301       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5302         Store[x][y] = element_info[element].content.e[1][1];
5303 #else
5304       else if (!CAN_EXPLODE(element))
5305         Store[x][y] = element_info[element].content.e[1][1];
5306 #endif
5307       else
5308         Store[x][y] = EL_EMPTY;
5309
5310       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5311           center_element == EL_AMOEBA_TO_DIAMOND)
5312         Store2[x][y] = element;
5313
5314       Feld[x][y] = EL_EXPLOSION;
5315       GfxElement[x][y] = artwork_element;
5316
5317       ExplodePhase[x][y] = 1;
5318       ExplodeDelay[x][y] = last_phase;
5319
5320       Stop[x][y] = TRUE;
5321     }
5322
5323     if (center_element == EL_YAMYAM)
5324       game.yamyam_content_nr =
5325         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5326
5327     return;
5328   }
5329
5330   if (Stop[ex][ey])
5331     return;
5332
5333   x = ex;
5334   y = ey;
5335
5336   if (phase == 1)
5337     GfxFrame[x][y] = 0;         /* restart explosion animation */
5338
5339   last_phase = ExplodeDelay[x][y];
5340
5341   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5342
5343   /* this can happen if the player leaves an explosion just in time */
5344   if (GfxElement[x][y] == EL_UNDEFINED)
5345     GfxElement[x][y] = EL_EMPTY;
5346
5347   border_element = Store2[x][y];
5348   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5349     border_element = StorePlayer[x][y];
5350
5351   if (phase == element_info[border_element].ignition_delay ||
5352       phase == last_phase)
5353   {
5354     boolean border_explosion = FALSE;
5355
5356     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5357         !PLAYER_EXPLOSION_PROTECTED(x, y))
5358     {
5359       KillPlayerUnlessExplosionProtected(x, y);
5360       border_explosion = TRUE;
5361     }
5362     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5363     {
5364       Feld[x][y] = Store2[x][y];
5365       Store2[x][y] = 0;
5366       Bang(x, y);
5367       border_explosion = TRUE;
5368     }
5369     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5370     {
5371       AmoebeUmwandeln(x, y);
5372       Store2[x][y] = 0;
5373       border_explosion = TRUE;
5374     }
5375
5376     /* if an element just explodes due to another explosion (chain-reaction),
5377        do not immediately end the new explosion when it was the last frame of
5378        the explosion (as it would be done in the following "if"-statement!) */
5379     if (border_explosion && phase == last_phase)
5380       return;
5381   }
5382
5383   if (phase == last_phase)
5384   {
5385     int element;
5386
5387     element = Feld[x][y] = Store[x][y];
5388     Store[x][y] = Store2[x][y] = 0;
5389     GfxElement[x][y] = EL_UNDEFINED;
5390
5391     /* player can escape from explosions and might therefore be still alive */
5392     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5393         element <= EL_PLAYER_IS_EXPLODING_4)
5394     {
5395       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5396       int explosion_element = EL_PLAYER_1 + player_nr;
5397       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5398       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5399
5400       if (level.use_explosion_element[player_nr])
5401         explosion_element = level.explosion_element[player_nr];
5402
5403       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5404                     element_info[explosion_element].content.e[xx][yy]);
5405     }
5406
5407     /* restore probably existing indestructible background element */
5408     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5409       element = Feld[x][y] = Back[x][y];
5410     Back[x][y] = 0;
5411
5412     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5413     GfxDir[x][y] = MV_NONE;
5414     ChangeDelay[x][y] = 0;
5415     ChangePage[x][y] = -1;
5416
5417     CustomValue[x][y] = 0;
5418
5419     InitField_WithBug2(x, y, FALSE);
5420
5421     TEST_DrawLevelField(x, y);
5422
5423     TestIfElementTouchesCustomElement(x, y);
5424
5425     if (GFX_CRUMBLED(element))
5426       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5427
5428     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5429       StorePlayer[x][y] = 0;
5430
5431     if (ELEM_IS_PLAYER(element))
5432       RelocatePlayer(x, y, element);
5433   }
5434   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5435   {
5436     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5437     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5438
5439     if (phase == delay)
5440       TEST_DrawLevelFieldCrumbled(x, y);
5441
5442     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5443     {
5444       DrawLevelElement(x, y, Back[x][y]);
5445       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5446     }
5447     else if (IS_WALKABLE_UNDER(Back[x][y]))
5448     {
5449       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5450       DrawLevelElementThruMask(x, y, Back[x][y]);
5451     }
5452     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5453       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5454   }
5455 }
5456
5457 void DynaExplode(int ex, int ey)
5458 {
5459   int i, j;
5460   int dynabomb_element = Feld[ex][ey];
5461   int dynabomb_size = 1;
5462   boolean dynabomb_xl = FALSE;
5463   struct PlayerInfo *player;
5464   static int xy[4][2] =
5465   {
5466     { 0, -1 },
5467     { -1, 0 },
5468     { +1, 0 },
5469     { 0, +1 }
5470   };
5471
5472   if (IS_ACTIVE_BOMB(dynabomb_element))
5473   {
5474     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5475     dynabomb_size = player->dynabomb_size;
5476     dynabomb_xl = player->dynabomb_xl;
5477     player->dynabombs_left++;
5478   }
5479
5480   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5481
5482   for (i = 0; i < NUM_DIRECTIONS; i++)
5483   {
5484     for (j = 1; j <= dynabomb_size; j++)
5485     {
5486       int x = ex + j * xy[i][0];
5487       int y = ey + j * xy[i][1];
5488       int element;
5489
5490       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5491         break;
5492
5493       element = Feld[x][y];
5494
5495       /* do not restart explosions of fields with active bombs */
5496       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5497         continue;
5498
5499       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5500
5501       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5502           !IS_DIGGABLE(element) && !dynabomb_xl)
5503         break;
5504     }
5505   }
5506 }
5507
5508 void Bang(int x, int y)
5509 {
5510   int element = MovingOrBlocked2Element(x, y);
5511   int explosion_type = EX_TYPE_NORMAL;
5512
5513   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5514   {
5515     struct PlayerInfo *player = PLAYERINFO(x, y);
5516
5517     element = Feld[x][y] = player->initial_element;
5518
5519     if (level.use_explosion_element[player->index_nr])
5520     {
5521       int explosion_element = level.explosion_element[player->index_nr];
5522
5523       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5524         explosion_type = EX_TYPE_CROSS;
5525       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5526         explosion_type = EX_TYPE_CENTER;
5527     }
5528   }
5529
5530   switch (element)
5531   {
5532     case EL_BUG:
5533     case EL_SPACESHIP:
5534     case EL_BD_BUTTERFLY:
5535     case EL_BD_FIREFLY:
5536     case EL_YAMYAM:
5537     case EL_DARK_YAMYAM:
5538     case EL_ROBOT:
5539     case EL_PACMAN:
5540     case EL_MOLE:
5541       RaiseScoreElement(element);
5542       break;
5543
5544     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5545     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5546     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5547     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5548     case EL_DYNABOMB_INCREASE_NUMBER:
5549     case EL_DYNABOMB_INCREASE_SIZE:
5550     case EL_DYNABOMB_INCREASE_POWER:
5551       explosion_type = EX_TYPE_DYNA;
5552       break;
5553
5554     case EL_DC_LANDMINE:
5555       explosion_type = EX_TYPE_CENTER;
5556       break;
5557
5558     case EL_PENGUIN:
5559     case EL_LAMP:
5560     case EL_LAMP_ACTIVE:
5561     case EL_AMOEBA_TO_DIAMOND:
5562       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5563         explosion_type = EX_TYPE_CENTER;
5564       break;
5565
5566     default:
5567       if (element_info[element].explosion_type == EXPLODES_CROSS)
5568         explosion_type = EX_TYPE_CROSS;
5569       else if (element_info[element].explosion_type == EXPLODES_1X1)
5570         explosion_type = EX_TYPE_CENTER;
5571       break;
5572   }
5573
5574   if (explosion_type == EX_TYPE_DYNA)
5575     DynaExplode(x, y);
5576   else
5577     Explode(x, y, EX_PHASE_START, explosion_type);
5578
5579   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5580 }
5581
5582 void SplashAcid(int x, int y)
5583 {
5584   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5585       (!IN_LEV_FIELD(x - 1, y - 2) ||
5586        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5587     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5588
5589   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5590       (!IN_LEV_FIELD(x + 1, y - 2) ||
5591        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5592     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5593
5594   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5595 }
5596
5597 static void InitBeltMovement()
5598 {
5599   static int belt_base_element[4] =
5600   {
5601     EL_CONVEYOR_BELT_1_LEFT,
5602     EL_CONVEYOR_BELT_2_LEFT,
5603     EL_CONVEYOR_BELT_3_LEFT,
5604     EL_CONVEYOR_BELT_4_LEFT
5605   };
5606   static int belt_base_active_element[4] =
5607   {
5608     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5609     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5610     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5611     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5612   };
5613
5614   int x, y, i, j;
5615
5616   /* set frame order for belt animation graphic according to belt direction */
5617   for (i = 0; i < NUM_BELTS; i++)
5618   {
5619     int belt_nr = i;
5620
5621     for (j = 0; j < NUM_BELT_PARTS; j++)
5622     {
5623       int element = belt_base_active_element[belt_nr] + j;
5624       int graphic_1 = el2img(element);
5625       int graphic_2 = el2panelimg(element);
5626
5627       if (game.belt_dir[i] == MV_LEFT)
5628       {
5629         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5630         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5631       }
5632       else
5633       {
5634         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5635         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5636       }
5637     }
5638   }
5639
5640   SCAN_PLAYFIELD(x, y)
5641   {
5642     int element = Feld[x][y];
5643
5644     for (i = 0; i < NUM_BELTS; i++)
5645     {
5646       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5647       {
5648         int e_belt_nr = getBeltNrFromBeltElement(element);
5649         int belt_nr = i;
5650
5651         if (e_belt_nr == belt_nr)
5652         {
5653           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5654
5655           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5656         }
5657       }
5658     }
5659   }
5660 }
5661
5662 static void ToggleBeltSwitch(int x, int y)
5663 {
5664   static int belt_base_element[4] =
5665   {
5666     EL_CONVEYOR_BELT_1_LEFT,
5667     EL_CONVEYOR_BELT_2_LEFT,
5668     EL_CONVEYOR_BELT_3_LEFT,
5669     EL_CONVEYOR_BELT_4_LEFT
5670   };
5671   static int belt_base_active_element[4] =
5672   {
5673     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5674     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5675     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5676     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5677   };
5678   static int belt_base_switch_element[4] =
5679   {
5680     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5681     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5682     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5683     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5684   };
5685   static int belt_move_dir[4] =
5686   {
5687     MV_LEFT,
5688     MV_NONE,
5689     MV_RIGHT,
5690     MV_NONE,
5691   };
5692
5693   int element = Feld[x][y];
5694   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5695   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5696   int belt_dir = belt_move_dir[belt_dir_nr];
5697   int xx, yy, i;
5698
5699   if (!IS_BELT_SWITCH(element))
5700     return;
5701
5702   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5703   game.belt_dir[belt_nr] = belt_dir;
5704
5705   if (belt_dir_nr == 3)
5706     belt_dir_nr = 1;
5707
5708   /* set frame order for belt animation graphic according to belt direction */
5709   for (i = 0; i < NUM_BELT_PARTS; i++)
5710   {
5711     int element = belt_base_active_element[belt_nr] + i;
5712     int graphic_1 = el2img(element);
5713     int graphic_2 = el2panelimg(element);
5714
5715     if (belt_dir == MV_LEFT)
5716     {
5717       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5718       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5719     }
5720     else
5721     {
5722       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5723       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5724     }
5725   }
5726
5727   SCAN_PLAYFIELD(xx, yy)
5728   {
5729     int element = Feld[xx][yy];
5730
5731     if (IS_BELT_SWITCH(element))
5732     {
5733       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5734
5735       if (e_belt_nr == belt_nr)
5736       {
5737         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5738         TEST_DrawLevelField(xx, yy);
5739       }
5740     }
5741     else if (IS_BELT(element) && belt_dir != MV_NONE)
5742     {
5743       int e_belt_nr = getBeltNrFromBeltElement(element);
5744
5745       if (e_belt_nr == belt_nr)
5746       {
5747         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5748
5749         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5750         TEST_DrawLevelField(xx, yy);
5751       }
5752     }
5753     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5754     {
5755       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5756
5757       if (e_belt_nr == belt_nr)
5758       {
5759         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5760
5761         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5762         TEST_DrawLevelField(xx, yy);
5763       }
5764     }
5765   }
5766 }
5767
5768 static void ToggleSwitchgateSwitch(int x, int y)
5769 {
5770   int xx, yy;
5771
5772   game.switchgate_pos = !game.switchgate_pos;
5773
5774   SCAN_PLAYFIELD(xx, yy)
5775   {
5776     int element = Feld[xx][yy];
5777
5778     if (element == EL_SWITCHGATE_SWITCH_UP)
5779     {
5780       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5781       TEST_DrawLevelField(xx, yy);
5782     }
5783     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5784     {
5785       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5786       TEST_DrawLevelField(xx, yy);
5787     }
5788     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5789     {
5790       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5791       TEST_DrawLevelField(xx, yy);
5792     }
5793     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5794     {
5795       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5796       TEST_DrawLevelField(xx, yy);
5797     }
5798     else if (element == EL_SWITCHGATE_OPEN ||
5799              element == EL_SWITCHGATE_OPENING)
5800     {
5801       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5802
5803       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5804     }
5805     else if (element == EL_SWITCHGATE_CLOSED ||
5806              element == EL_SWITCHGATE_CLOSING)
5807     {
5808       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5809
5810       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5811     }
5812   }
5813 }
5814
5815 static int getInvisibleActiveFromInvisibleElement(int element)
5816 {
5817   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5818           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5819           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5820           element);
5821 }
5822
5823 static int getInvisibleFromInvisibleActiveElement(int element)
5824 {
5825   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5826           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5827           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5828           element);
5829 }
5830
5831 static void RedrawAllLightSwitchesAndInvisibleElements()
5832 {
5833   int x, y;
5834
5835   SCAN_PLAYFIELD(x, y)
5836   {
5837     int element = Feld[x][y];
5838
5839     if (element == EL_LIGHT_SWITCH &&
5840         game.light_time_left > 0)
5841     {
5842       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5843       TEST_DrawLevelField(x, y);
5844     }
5845     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5846              game.light_time_left == 0)
5847     {
5848       Feld[x][y] = EL_LIGHT_SWITCH;
5849       TEST_DrawLevelField(x, y);
5850     }
5851     else if (element == EL_EMC_DRIPPER &&
5852              game.light_time_left > 0)
5853     {
5854       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5855       TEST_DrawLevelField(x, y);
5856     }
5857     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5858              game.light_time_left == 0)
5859     {
5860       Feld[x][y] = EL_EMC_DRIPPER;
5861       TEST_DrawLevelField(x, y);
5862     }
5863     else if (element == EL_INVISIBLE_STEELWALL ||
5864              element == EL_INVISIBLE_WALL ||
5865              element == EL_INVISIBLE_SAND)
5866     {
5867       if (game.light_time_left > 0)
5868         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5869
5870       TEST_DrawLevelField(x, y);
5871
5872       /* uncrumble neighbour fields, if needed */
5873       if (element == EL_INVISIBLE_SAND)
5874         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5875     }
5876     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5877              element == EL_INVISIBLE_WALL_ACTIVE ||
5878              element == EL_INVISIBLE_SAND_ACTIVE)
5879     {
5880       if (game.light_time_left == 0)
5881         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5882
5883       TEST_DrawLevelField(x, y);
5884
5885       /* re-crumble neighbour fields, if needed */
5886       if (element == EL_INVISIBLE_SAND)
5887         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5888     }
5889   }
5890 }
5891
5892 static void RedrawAllInvisibleElementsForLenses()
5893 {
5894   int x, y;
5895
5896   SCAN_PLAYFIELD(x, y)
5897   {
5898     int element = Feld[x][y];
5899
5900     if (element == EL_EMC_DRIPPER &&
5901         game.lenses_time_left > 0)
5902     {
5903       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5904       TEST_DrawLevelField(x, y);
5905     }
5906     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5907              game.lenses_time_left == 0)
5908     {
5909       Feld[x][y] = EL_EMC_DRIPPER;
5910       TEST_DrawLevelField(x, y);
5911     }
5912     else if (element == EL_INVISIBLE_STEELWALL ||
5913              element == EL_INVISIBLE_WALL ||
5914              element == EL_INVISIBLE_SAND)
5915     {
5916       if (game.lenses_time_left > 0)
5917         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5918
5919       TEST_DrawLevelField(x, y);
5920
5921       /* uncrumble neighbour fields, if needed */
5922       if (element == EL_INVISIBLE_SAND)
5923         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5924     }
5925     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5926              element == EL_INVISIBLE_WALL_ACTIVE ||
5927              element == EL_INVISIBLE_SAND_ACTIVE)
5928     {
5929       if (game.lenses_time_left == 0)
5930         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5931
5932       TEST_DrawLevelField(x, y);
5933
5934       /* re-crumble neighbour fields, if needed */
5935       if (element == EL_INVISIBLE_SAND)
5936         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5937     }
5938   }
5939 }
5940
5941 static void RedrawAllInvisibleElementsForMagnifier()
5942 {
5943   int x, y;
5944
5945   SCAN_PLAYFIELD(x, y)
5946   {
5947     int element = Feld[x][y];
5948
5949     if (element == EL_EMC_FAKE_GRASS &&
5950         game.magnify_time_left > 0)
5951     {
5952       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5953       TEST_DrawLevelField(x, y);
5954     }
5955     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5956              game.magnify_time_left == 0)
5957     {
5958       Feld[x][y] = EL_EMC_FAKE_GRASS;
5959       TEST_DrawLevelField(x, y);
5960     }
5961     else if (IS_GATE_GRAY(element) &&
5962              game.magnify_time_left > 0)
5963     {
5964       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5965                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5966                     IS_EM_GATE_GRAY(element) ?
5967                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5968                     IS_EMC_GATE_GRAY(element) ?
5969                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5970                     IS_DC_GATE_GRAY(element) ?
5971                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5972                     element);
5973       TEST_DrawLevelField(x, y);
5974     }
5975     else if (IS_GATE_GRAY_ACTIVE(element) &&
5976              game.magnify_time_left == 0)
5977     {
5978       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5979                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5980                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5981                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5982                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5983                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5984                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5985                     EL_DC_GATE_WHITE_GRAY :
5986                     element);
5987       TEST_DrawLevelField(x, y);
5988     }
5989   }
5990 }
5991
5992 static void ToggleLightSwitch(int x, int y)
5993 {
5994   int element = Feld[x][y];
5995
5996   game.light_time_left =
5997     (element == EL_LIGHT_SWITCH ?
5998      level.time_light * FRAMES_PER_SECOND : 0);
5999
6000   RedrawAllLightSwitchesAndInvisibleElements();
6001 }
6002
6003 static void ActivateTimegateSwitch(int x, int y)
6004 {
6005   int xx, yy;
6006
6007   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6008
6009   SCAN_PLAYFIELD(xx, yy)
6010   {
6011     int element = Feld[xx][yy];
6012
6013     if (element == EL_TIMEGATE_CLOSED ||
6014         element == EL_TIMEGATE_CLOSING)
6015     {
6016       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6017       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6018     }
6019
6020     /*
6021     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6022     {
6023       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6024       TEST_DrawLevelField(xx, yy);
6025     }
6026     */
6027
6028   }
6029
6030   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6031                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6032 }
6033
6034 void Impact(int x, int y)
6035 {
6036   boolean last_line = (y == lev_fieldy - 1);
6037   boolean object_hit = FALSE;
6038   boolean impact = (last_line || object_hit);
6039   int element = Feld[x][y];
6040   int smashed = EL_STEELWALL;
6041
6042   if (!last_line)       /* check if element below was hit */
6043   {
6044     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6045       return;
6046
6047     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6048                                          MovDir[x][y + 1] != MV_DOWN ||
6049                                          MovPos[x][y + 1] <= TILEY / 2));
6050
6051     /* do not smash moving elements that left the smashed field in time */
6052     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6053         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6054       object_hit = FALSE;
6055
6056 #if USE_QUICKSAND_IMPACT_BUGFIX
6057     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6058     {
6059       RemoveMovingField(x, y + 1);
6060       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6061       Feld[x][y + 2] = EL_ROCK;
6062       TEST_DrawLevelField(x, y + 2);
6063
6064       object_hit = TRUE;
6065     }
6066
6067     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6068     {
6069       RemoveMovingField(x, y + 1);
6070       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6071       Feld[x][y + 2] = EL_ROCK;
6072       TEST_DrawLevelField(x, y + 2);
6073
6074       object_hit = TRUE;
6075     }
6076 #endif
6077
6078     if (object_hit)
6079       smashed = MovingOrBlocked2Element(x, y + 1);
6080
6081     impact = (last_line || object_hit);
6082   }
6083
6084   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6085   {
6086     SplashAcid(x, y + 1);
6087     return;
6088   }
6089
6090   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6091   /* only reset graphic animation if graphic really changes after impact */
6092   if (impact &&
6093       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6094   {
6095     ResetGfxAnimation(x, y);
6096     TEST_DrawLevelField(x, y);
6097   }
6098
6099   if (impact && CAN_EXPLODE_IMPACT(element))
6100   {
6101     Bang(x, y);
6102     return;
6103   }
6104   else if (impact && element == EL_PEARL &&
6105            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6106   {
6107     ResetGfxAnimation(x, y);
6108
6109     Feld[x][y] = EL_PEARL_BREAKING;
6110     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6111     return;
6112   }
6113   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6114   {
6115     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6116
6117     return;
6118   }
6119
6120   if (impact && element == EL_AMOEBA_DROP)
6121   {
6122     if (object_hit && IS_PLAYER(x, y + 1))
6123       KillPlayerUnlessEnemyProtected(x, y + 1);
6124     else if (object_hit && smashed == EL_PENGUIN)
6125       Bang(x, y + 1);
6126     else
6127     {
6128       Feld[x][y] = EL_AMOEBA_GROWING;
6129       Store[x][y] = EL_AMOEBA_WET;
6130
6131       ResetRandomAnimationValue(x, y);
6132     }
6133     return;
6134   }
6135
6136   if (object_hit)               /* check which object was hit */
6137   {
6138     if ((CAN_PASS_MAGIC_WALL(element) && 
6139          (smashed == EL_MAGIC_WALL ||
6140           smashed == EL_BD_MAGIC_WALL)) ||
6141         (CAN_PASS_DC_MAGIC_WALL(element) &&
6142          smashed == EL_DC_MAGIC_WALL))
6143     {
6144       int xx, yy;
6145       int activated_magic_wall =
6146         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6147          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6148          EL_DC_MAGIC_WALL_ACTIVE);
6149
6150       /* activate magic wall / mill */
6151       SCAN_PLAYFIELD(xx, yy)
6152       {
6153         if (Feld[xx][yy] == smashed)
6154           Feld[xx][yy] = activated_magic_wall;
6155       }
6156
6157       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6158       game.magic_wall_active = TRUE;
6159
6160       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6161                             SND_MAGIC_WALL_ACTIVATING :
6162                             smashed == EL_BD_MAGIC_WALL ?
6163                             SND_BD_MAGIC_WALL_ACTIVATING :
6164                             SND_DC_MAGIC_WALL_ACTIVATING));
6165     }
6166
6167     if (IS_PLAYER(x, y + 1))
6168     {
6169       if (CAN_SMASH_PLAYER(element))
6170       {
6171         KillPlayerUnlessEnemyProtected(x, y + 1);
6172         return;
6173       }
6174     }
6175     else if (smashed == EL_PENGUIN)
6176     {
6177       if (CAN_SMASH_PLAYER(element))
6178       {
6179         Bang(x, y + 1);
6180         return;
6181       }
6182     }
6183     else if (element == EL_BD_DIAMOND)
6184     {
6185       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6186       {
6187         Bang(x, y + 1);
6188         return;
6189       }
6190     }
6191     else if (((element == EL_SP_INFOTRON ||
6192                element == EL_SP_ZONK) &&
6193               (smashed == EL_SP_SNIKSNAK ||
6194                smashed == EL_SP_ELECTRON ||
6195                smashed == EL_SP_DISK_ORANGE)) ||
6196              (element == EL_SP_INFOTRON &&
6197               smashed == EL_SP_DISK_YELLOW))
6198     {
6199       Bang(x, y + 1);
6200       return;
6201     }
6202     else if (CAN_SMASH_EVERYTHING(element))
6203     {
6204       if (IS_CLASSIC_ENEMY(smashed) ||
6205           CAN_EXPLODE_SMASHED(smashed))
6206       {
6207         Bang(x, y + 1);
6208         return;
6209       }
6210       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6211       {
6212         if (smashed == EL_LAMP ||
6213             smashed == EL_LAMP_ACTIVE)
6214         {
6215           Bang(x, y + 1);
6216           return;
6217         }
6218         else if (smashed == EL_NUT)
6219         {
6220           Feld[x][y + 1] = EL_NUT_BREAKING;
6221           PlayLevelSound(x, y, SND_NUT_BREAKING);
6222           RaiseScoreElement(EL_NUT);
6223           return;
6224         }
6225         else if (smashed == EL_PEARL)
6226         {
6227           ResetGfxAnimation(x, y);
6228
6229           Feld[x][y + 1] = EL_PEARL_BREAKING;
6230           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6231           return;
6232         }
6233         else if (smashed == EL_DIAMOND)
6234         {
6235           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6236           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6237           return;
6238         }
6239         else if (IS_BELT_SWITCH(smashed))
6240         {
6241           ToggleBeltSwitch(x, y + 1);
6242         }
6243         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6244                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6245                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6246                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6247         {
6248           ToggleSwitchgateSwitch(x, y + 1);
6249         }
6250         else if (smashed == EL_LIGHT_SWITCH ||
6251                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6252         {
6253           ToggleLightSwitch(x, y + 1);
6254         }
6255         else
6256         {
6257           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6258
6259           CheckElementChangeBySide(x, y + 1, smashed, element,
6260                                    CE_SWITCHED, CH_SIDE_TOP);
6261           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6262                                             CH_SIDE_TOP);
6263         }
6264       }
6265       else
6266       {
6267         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6268       }
6269     }
6270   }
6271
6272   /* play sound of magic wall / mill */
6273   if (!last_line &&
6274       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6275        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6276        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6277   {
6278     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6279       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6280     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6281       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6282     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6283       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6284
6285     return;
6286   }
6287
6288   /* play sound of object that hits the ground */
6289   if (last_line || object_hit)
6290     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6291 }
6292
6293 inline static void TurnRoundExt(int x, int y)
6294 {
6295   static struct
6296   {
6297     int dx, dy;
6298   } move_xy[] =
6299   {
6300     {  0,  0 },
6301     { -1,  0 },
6302     { +1,  0 },
6303     {  0,  0 },
6304     {  0, -1 },
6305     {  0,  0 }, { 0, 0 }, { 0, 0 },
6306     {  0, +1 }
6307   };
6308   static struct
6309   {
6310     int left, right, back;
6311   } turn[] =
6312   {
6313     { 0,        0,              0        },
6314     { MV_DOWN,  MV_UP,          MV_RIGHT },
6315     { MV_UP,    MV_DOWN,        MV_LEFT  },
6316     { 0,        0,              0        },
6317     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6318     { 0,        0,              0        },
6319     { 0,        0,              0        },
6320     { 0,        0,              0        },
6321     { MV_RIGHT, MV_LEFT,        MV_UP    }
6322   };
6323
6324   int element = Feld[x][y];
6325   int move_pattern = element_info[element].move_pattern;
6326
6327   int old_move_dir = MovDir[x][y];
6328   int left_dir  = turn[old_move_dir].left;
6329   int right_dir = turn[old_move_dir].right;
6330   int back_dir  = turn[old_move_dir].back;
6331
6332   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6333   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6334   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6335   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6336
6337   int left_x  = x + left_dx,  left_y  = y + left_dy;
6338   int right_x = x + right_dx, right_y = y + right_dy;
6339   int move_x  = x + move_dx,  move_y  = y + move_dy;
6340
6341   int xx, yy;
6342
6343   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6344   {
6345     TestIfBadThingTouchesOtherBadThing(x, y);
6346
6347     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6348       MovDir[x][y] = right_dir;
6349     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6350       MovDir[x][y] = left_dir;
6351
6352     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6353       MovDelay[x][y] = 9;
6354     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6355       MovDelay[x][y] = 1;
6356   }
6357   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6358   {
6359     TestIfBadThingTouchesOtherBadThing(x, y);
6360
6361     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6362       MovDir[x][y] = left_dir;
6363     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6364       MovDir[x][y] = right_dir;
6365
6366     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6367       MovDelay[x][y] = 9;
6368     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6369       MovDelay[x][y] = 1;
6370   }
6371   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6372   {
6373     TestIfBadThingTouchesOtherBadThing(x, y);
6374
6375     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6376       MovDir[x][y] = left_dir;
6377     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6378       MovDir[x][y] = right_dir;
6379
6380     if (MovDir[x][y] != old_move_dir)
6381       MovDelay[x][y] = 9;
6382   }
6383   else if (element == EL_YAMYAM)
6384   {
6385     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6386     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6387
6388     if (can_turn_left && can_turn_right)
6389       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6390     else if (can_turn_left)
6391       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6392     else if (can_turn_right)
6393       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6394     else
6395       MovDir[x][y] = back_dir;
6396
6397     MovDelay[x][y] = 16 + 16 * RND(3);
6398   }
6399   else if (element == EL_DARK_YAMYAM)
6400   {
6401     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6402                                                          left_x, left_y);
6403     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6404                                                          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] = 16 + 16 * RND(3);
6416   }
6417   else if (element == EL_PACMAN)
6418   {
6419     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6420     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6421
6422     if (can_turn_left && can_turn_right)
6423       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6424     else if (can_turn_left)
6425       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6426     else if (can_turn_right)
6427       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6428     else
6429       MovDir[x][y] = back_dir;
6430
6431     MovDelay[x][y] = 6 + RND(40);
6432   }
6433   else if (element == EL_PIG)
6434   {
6435     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6436     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6437     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6438     boolean should_turn_left, should_turn_right, should_move_on;
6439     int rnd_value = 24;
6440     int rnd = RND(rnd_value);
6441
6442     should_turn_left = (can_turn_left &&
6443                         (!can_move_on ||
6444                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6445                                                    y + back_dy + left_dy)));
6446     should_turn_right = (can_turn_right &&
6447                          (!can_move_on ||
6448                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6449                                                     y + back_dy + right_dy)));
6450     should_move_on = (can_move_on &&
6451                       (!can_turn_left ||
6452                        !can_turn_right ||
6453                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6454                                                  y + move_dy + left_dy) ||
6455                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6456                                                  y + move_dy + right_dy)));
6457
6458     if (should_turn_left || should_turn_right || should_move_on)
6459     {
6460       if (should_turn_left && should_turn_right && should_move_on)
6461         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6462                         rnd < 2 * rnd_value / 3 ? right_dir :
6463                         old_move_dir);
6464       else if (should_turn_left && should_turn_right)
6465         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6466       else if (should_turn_left && should_move_on)
6467         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6468       else if (should_turn_right && should_move_on)
6469         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6470       else if (should_turn_left)
6471         MovDir[x][y] = left_dir;
6472       else if (should_turn_right)
6473         MovDir[x][y] = right_dir;
6474       else if (should_move_on)
6475         MovDir[x][y] = old_move_dir;
6476     }
6477     else if (can_move_on && rnd > rnd_value / 8)
6478       MovDir[x][y] = old_move_dir;
6479     else if (can_turn_left && can_turn_right)
6480       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6481     else if (can_turn_left && rnd > rnd_value / 8)
6482       MovDir[x][y] = left_dir;
6483     else if (can_turn_right && rnd > rnd_value/8)
6484       MovDir[x][y] = right_dir;
6485     else
6486       MovDir[x][y] = back_dir;
6487
6488     xx = x + move_xy[MovDir[x][y]].dx;
6489     yy = y + move_xy[MovDir[x][y]].dy;
6490
6491     if (!IN_LEV_FIELD(xx, yy) ||
6492         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6493       MovDir[x][y] = old_move_dir;
6494
6495     MovDelay[x][y] = 0;
6496   }
6497   else if (element == EL_DRAGON)
6498   {
6499     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6500     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6501     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6502     int rnd_value = 24;
6503     int rnd = RND(rnd_value);
6504
6505     if (can_move_on && rnd > rnd_value / 8)
6506       MovDir[x][y] = old_move_dir;
6507     else if (can_turn_left && can_turn_right)
6508       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6509     else if (can_turn_left && rnd > rnd_value / 8)
6510       MovDir[x][y] = left_dir;
6511     else if (can_turn_right && rnd > rnd_value / 8)
6512       MovDir[x][y] = right_dir;
6513     else
6514       MovDir[x][y] = back_dir;
6515
6516     xx = x + move_xy[MovDir[x][y]].dx;
6517     yy = y + move_xy[MovDir[x][y]].dy;
6518
6519     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6520       MovDir[x][y] = old_move_dir;
6521
6522     MovDelay[x][y] = 0;
6523   }
6524   else if (element == EL_MOLE)
6525   {
6526     boolean can_move_on =
6527       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6528                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6529                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6530     if (!can_move_on)
6531     {
6532       boolean can_turn_left =
6533         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6534                               IS_AMOEBOID(Feld[left_x][left_y])));
6535
6536       boolean can_turn_right =
6537         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6538                               IS_AMOEBOID(Feld[right_x][right_y])));
6539
6540       if (can_turn_left && can_turn_right)
6541         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6542       else if (can_turn_left)
6543         MovDir[x][y] = left_dir;
6544       else
6545         MovDir[x][y] = right_dir;
6546     }
6547
6548     if (MovDir[x][y] != old_move_dir)
6549       MovDelay[x][y] = 9;
6550   }
6551   else if (element == EL_BALLOON)
6552   {
6553     MovDir[x][y] = game.wind_direction;
6554     MovDelay[x][y] = 0;
6555   }
6556   else if (element == EL_SPRING)
6557   {
6558     if (MovDir[x][y] & MV_HORIZONTAL)
6559     {
6560       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6561           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6562       {
6563         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6564         ResetGfxAnimation(move_x, move_y);
6565         TEST_DrawLevelField(move_x, move_y);
6566
6567         MovDir[x][y] = back_dir;
6568       }
6569       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6570                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6571         MovDir[x][y] = MV_NONE;
6572     }
6573
6574     MovDelay[x][y] = 0;
6575   }
6576   else if (element == EL_ROBOT ||
6577            element == EL_SATELLITE ||
6578            element == EL_PENGUIN ||
6579            element == EL_EMC_ANDROID)
6580   {
6581     int attr_x = -1, attr_y = -1;
6582
6583     if (AllPlayersGone)
6584     {
6585       attr_x = ExitX;
6586       attr_y = ExitY;
6587     }
6588     else
6589     {
6590       int i;
6591
6592       for (i = 0; i < MAX_PLAYERS; i++)
6593       {
6594         struct PlayerInfo *player = &stored_player[i];
6595         int jx = player->jx, jy = player->jy;
6596
6597         if (!player->active)
6598           continue;
6599
6600         if (attr_x == -1 ||
6601             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6602         {
6603           attr_x = jx;
6604           attr_y = jy;
6605         }
6606       }
6607     }
6608
6609     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6610         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6611          game.engine_version < VERSION_IDENT(3,1,0,0)))
6612     {
6613       attr_x = ZX;
6614       attr_y = ZY;
6615     }
6616
6617     if (element == EL_PENGUIN)
6618     {
6619       int i;
6620       static int xy[4][2] =
6621       {
6622         { 0, -1 },
6623         { -1, 0 },
6624         { +1, 0 },
6625         { 0, +1 }
6626       };
6627
6628       for (i = 0; i < NUM_DIRECTIONS; i++)
6629       {
6630         int ex = x + xy[i][0];
6631         int ey = y + xy[i][1];
6632
6633         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6634                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6635                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6636                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6637         {
6638           attr_x = ex;
6639           attr_y = ey;
6640           break;
6641         }
6642       }
6643     }
6644
6645     MovDir[x][y] = MV_NONE;
6646     if (attr_x < x)
6647       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6648     else if (attr_x > x)
6649       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6650     if (attr_y < y)
6651       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6652     else if (attr_y > y)
6653       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6654
6655     if (element == EL_ROBOT)
6656     {
6657       int newx, newy;
6658
6659       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6660         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6661       Moving2Blocked(x, y, &newx, &newy);
6662
6663       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6664         MovDelay[x][y] = 8 + 8 * !RND(3);
6665       else
6666         MovDelay[x][y] = 16;
6667     }
6668     else if (element == EL_PENGUIN)
6669     {
6670       int newx, newy;
6671
6672       MovDelay[x][y] = 1;
6673
6674       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6675       {
6676         boolean first_horiz = RND(2);
6677         int new_move_dir = MovDir[x][y];
6678
6679         MovDir[x][y] =
6680           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6681         Moving2Blocked(x, y, &newx, &newy);
6682
6683         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6684           return;
6685
6686         MovDir[x][y] =
6687           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6688         Moving2Blocked(x, y, &newx, &newy);
6689
6690         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6691           return;
6692
6693         MovDir[x][y] = old_move_dir;
6694         return;
6695       }
6696     }
6697     else if (element == EL_SATELLITE)
6698     {
6699       int newx, newy;
6700
6701       MovDelay[x][y] = 1;
6702
6703       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6704       {
6705         boolean first_horiz = RND(2);
6706         int new_move_dir = MovDir[x][y];
6707
6708         MovDir[x][y] =
6709           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6710         Moving2Blocked(x, y, &newx, &newy);
6711
6712         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6713           return;
6714
6715         MovDir[x][y] =
6716           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6717         Moving2Blocked(x, y, &newx, &newy);
6718
6719         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6720           return;
6721
6722         MovDir[x][y] = old_move_dir;
6723         return;
6724       }
6725     }
6726     else if (element == EL_EMC_ANDROID)
6727     {
6728       static int check_pos[16] =
6729       {
6730         -1,             /*  0 => (invalid)          */
6731         7,              /*  1 => MV_LEFT            */
6732         3,              /*  2 => MV_RIGHT           */
6733         -1,             /*  3 => (invalid)          */
6734         1,              /*  4 =>            MV_UP   */
6735         0,              /*  5 => MV_LEFT  | MV_UP   */
6736         2,              /*  6 => MV_RIGHT | MV_UP   */
6737         -1,             /*  7 => (invalid)          */
6738         5,              /*  8 =>            MV_DOWN */
6739         6,              /*  9 => MV_LEFT  | MV_DOWN */
6740         4,              /* 10 => MV_RIGHT | MV_DOWN */
6741         -1,             /* 11 => (invalid)          */
6742         -1,             /* 12 => (invalid)          */
6743         -1,             /* 13 => (invalid)          */
6744         -1,             /* 14 => (invalid)          */
6745         -1,             /* 15 => (invalid)          */
6746       };
6747       static struct
6748       {
6749         int dx, dy;
6750         int dir;
6751       } check_xy[8] =
6752       {
6753         { -1, -1,       MV_LEFT  | MV_UP   },
6754         {  0, -1,                  MV_UP   },
6755         { +1, -1,       MV_RIGHT | MV_UP   },
6756         { +1,  0,       MV_RIGHT           },
6757         { +1, +1,       MV_RIGHT | MV_DOWN },
6758         {  0, +1,                  MV_DOWN },
6759         { -1, +1,       MV_LEFT  | MV_DOWN },
6760         { -1,  0,       MV_LEFT            },
6761       };
6762       int start_pos, check_order;
6763       boolean can_clone = FALSE;
6764       int i;
6765
6766       /* check if there is any free field around current position */
6767       for (i = 0; i < 8; i++)
6768       {
6769         int newx = x + check_xy[i].dx;
6770         int newy = y + check_xy[i].dy;
6771
6772         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6773         {
6774           can_clone = TRUE;
6775
6776           break;
6777         }
6778       }
6779
6780       if (can_clone)            /* randomly find an element to clone */
6781       {
6782         can_clone = FALSE;
6783
6784         start_pos = check_pos[RND(8)];
6785         check_order = (RND(2) ? -1 : +1);
6786
6787         for (i = 0; i < 8; i++)
6788         {
6789           int pos_raw = start_pos + i * check_order;
6790           int pos = (pos_raw + 8) % 8;
6791           int newx = x + check_xy[pos].dx;
6792           int newy = y + check_xy[pos].dy;
6793
6794           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6795           {
6796             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6797             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6798
6799             Store[x][y] = Feld[newx][newy];
6800
6801             can_clone = TRUE;
6802
6803             break;
6804           }
6805         }
6806       }
6807
6808       if (can_clone)            /* randomly find a direction to move */
6809       {
6810         can_clone = FALSE;
6811
6812         start_pos = check_pos[RND(8)];
6813         check_order = (RND(2) ? -1 : +1);
6814
6815         for (i = 0; i < 8; i++)
6816         {
6817           int pos_raw = start_pos + i * check_order;
6818           int pos = (pos_raw + 8) % 8;
6819           int newx = x + check_xy[pos].dx;
6820           int newy = y + check_xy[pos].dy;
6821           int new_move_dir = check_xy[pos].dir;
6822
6823           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6824           {
6825             MovDir[x][y] = new_move_dir;
6826             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6827
6828             can_clone = TRUE;
6829
6830             break;
6831           }
6832         }
6833       }
6834
6835       if (can_clone)            /* cloning and moving successful */
6836         return;
6837
6838       /* cannot clone -- try to move towards player */
6839
6840       start_pos = check_pos[MovDir[x][y] & 0x0f];
6841       check_order = (RND(2) ? -1 : +1);
6842
6843       for (i = 0; i < 3; i++)
6844       {
6845         /* first check start_pos, then previous/next or (next/previous) pos */
6846         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6847         int pos = (pos_raw + 8) % 8;
6848         int newx = x + check_xy[pos].dx;
6849         int newy = y + check_xy[pos].dy;
6850         int new_move_dir = check_xy[pos].dir;
6851
6852         if (IS_PLAYER(newx, newy))
6853           break;
6854
6855         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6856         {
6857           MovDir[x][y] = new_move_dir;
6858           MovDelay[x][y] = level.android_move_time * 8 + 1;
6859
6860           break;
6861         }
6862       }
6863     }
6864   }
6865   else if (move_pattern == MV_TURNING_LEFT ||
6866            move_pattern == MV_TURNING_RIGHT ||
6867            move_pattern == MV_TURNING_LEFT_RIGHT ||
6868            move_pattern == MV_TURNING_RIGHT_LEFT ||
6869            move_pattern == MV_TURNING_RANDOM ||
6870            move_pattern == MV_ALL_DIRECTIONS)
6871   {
6872     boolean can_turn_left =
6873       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6874     boolean can_turn_right =
6875       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6876
6877     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6878       return;
6879
6880     if (move_pattern == MV_TURNING_LEFT)
6881       MovDir[x][y] = left_dir;
6882     else if (move_pattern == MV_TURNING_RIGHT)
6883       MovDir[x][y] = right_dir;
6884     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6885       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6886     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6887       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6888     else if (move_pattern == MV_TURNING_RANDOM)
6889       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6890                       can_turn_right && !can_turn_left ? right_dir :
6891                       RND(2) ? left_dir : right_dir);
6892     else if (can_turn_left && can_turn_right)
6893       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6894     else if (can_turn_left)
6895       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6896     else if (can_turn_right)
6897       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6898     else
6899       MovDir[x][y] = back_dir;
6900
6901     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6902   }
6903   else if (move_pattern == MV_HORIZONTAL ||
6904            move_pattern == MV_VERTICAL)
6905   {
6906     if (move_pattern & old_move_dir)
6907       MovDir[x][y] = back_dir;
6908     else if (move_pattern == MV_HORIZONTAL)
6909       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6910     else if (move_pattern == MV_VERTICAL)
6911       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6912
6913     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914   }
6915   else if (move_pattern & MV_ANY_DIRECTION)
6916   {
6917     MovDir[x][y] = move_pattern;
6918     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6919   }
6920   else if (move_pattern & MV_WIND_DIRECTION)
6921   {
6922     MovDir[x][y] = game.wind_direction;
6923     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6924   }
6925   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6926   {
6927     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6928       MovDir[x][y] = left_dir;
6929     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6930       MovDir[x][y] = right_dir;
6931
6932     if (MovDir[x][y] != old_move_dir)
6933       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6934   }
6935   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6936   {
6937     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6938       MovDir[x][y] = right_dir;
6939     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6940       MovDir[x][y] = left_dir;
6941
6942     if (MovDir[x][y] != old_move_dir)
6943       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6944   }
6945   else if (move_pattern == MV_TOWARDS_PLAYER ||
6946            move_pattern == MV_AWAY_FROM_PLAYER)
6947   {
6948     int attr_x = -1, attr_y = -1;
6949     int newx, newy;
6950     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6951
6952     if (AllPlayersGone)
6953     {
6954       attr_x = ExitX;
6955       attr_y = ExitY;
6956     }
6957     else
6958     {
6959       int i;
6960
6961       for (i = 0; i < MAX_PLAYERS; i++)
6962       {
6963         struct PlayerInfo *player = &stored_player[i];
6964         int jx = player->jx, jy = player->jy;
6965
6966         if (!player->active)
6967           continue;
6968
6969         if (attr_x == -1 ||
6970             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6971         {
6972           attr_x = jx;
6973           attr_y = jy;
6974         }
6975       }
6976     }
6977
6978     MovDir[x][y] = MV_NONE;
6979     if (attr_x < x)
6980       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6981     else if (attr_x > x)
6982       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6983     if (attr_y < y)
6984       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6985     else if (attr_y > y)
6986       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6987
6988     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6989
6990     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6991     {
6992       boolean first_horiz = RND(2);
6993       int new_move_dir = MovDir[x][y];
6994
6995       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6996       {
6997         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6998         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6999
7000         return;
7001       }
7002
7003       MovDir[x][y] =
7004         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7005       Moving2Blocked(x, y, &newx, &newy);
7006
7007       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7008         return;
7009
7010       MovDir[x][y] =
7011         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7012       Moving2Blocked(x, y, &newx, &newy);
7013
7014       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7015         return;
7016
7017       MovDir[x][y] = old_move_dir;
7018     }
7019   }
7020   else if (move_pattern == MV_WHEN_PUSHED ||
7021            move_pattern == MV_WHEN_DROPPED)
7022   {
7023     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7024       MovDir[x][y] = MV_NONE;
7025
7026     MovDelay[x][y] = 0;
7027   }
7028   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7029   {
7030     static int test_xy[7][2] =
7031     {
7032       { 0, -1 },
7033       { -1, 0 },
7034       { +1, 0 },
7035       { 0, +1 },
7036       { 0, -1 },
7037       { -1, 0 },
7038       { +1, 0 },
7039     };
7040     static int test_dir[7] =
7041     {
7042       MV_UP,
7043       MV_LEFT,
7044       MV_RIGHT,
7045       MV_DOWN,
7046       MV_UP,
7047       MV_LEFT,
7048       MV_RIGHT,
7049     };
7050     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7051     int move_preference = -1000000;     /* start with very low preference */
7052     int new_move_dir = MV_NONE;
7053     int start_test = RND(4);
7054     int i;
7055
7056     for (i = 0; i < NUM_DIRECTIONS; i++)
7057     {
7058       int move_dir = test_dir[start_test + i];
7059       int move_dir_preference;
7060
7061       xx = x + test_xy[start_test + i][0];
7062       yy = y + test_xy[start_test + i][1];
7063
7064       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7065           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7066       {
7067         new_move_dir = move_dir;
7068
7069         break;
7070       }
7071
7072       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7073         continue;
7074
7075       move_dir_preference = -1 * RunnerVisit[xx][yy];
7076       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7077         move_dir_preference = PlayerVisit[xx][yy];
7078
7079       if (move_dir_preference > move_preference)
7080       {
7081         /* prefer field that has not been visited for the longest time */
7082         move_preference = move_dir_preference;
7083         new_move_dir = move_dir;
7084       }
7085       else if (move_dir_preference == move_preference &&
7086                move_dir == old_move_dir)
7087       {
7088         /* prefer last direction when all directions are preferred equally */
7089         move_preference = move_dir_preference;
7090         new_move_dir = move_dir;
7091       }
7092     }
7093
7094     MovDir[x][y] = new_move_dir;
7095     if (old_move_dir != new_move_dir)
7096       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7097   }
7098 }
7099
7100 static void TurnRound(int x, int y)
7101 {
7102   int direction = MovDir[x][y];
7103
7104   TurnRoundExt(x, y);
7105
7106   GfxDir[x][y] = MovDir[x][y];
7107
7108   if (direction != MovDir[x][y])
7109     GfxFrame[x][y] = 0;
7110
7111   if (MovDelay[x][y])
7112     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7113
7114   ResetGfxFrame(x, y, FALSE);
7115 }
7116
7117 static boolean JustBeingPushed(int x, int y)
7118 {
7119   int i;
7120
7121   for (i = 0; i < MAX_PLAYERS; i++)
7122   {
7123     struct PlayerInfo *player = &stored_player[i];
7124
7125     if (player->active && player->is_pushing && player->MovPos)
7126     {
7127       int next_jx = player->jx + (player->jx - player->last_jx);
7128       int next_jy = player->jy + (player->jy - player->last_jy);
7129
7130       if (x == next_jx && y == next_jy)
7131         return TRUE;
7132     }
7133   }
7134
7135   return FALSE;
7136 }
7137
7138 void StartMoving(int x, int y)
7139 {
7140   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7141   int element = Feld[x][y];
7142
7143   if (Stop[x][y])
7144     return;
7145
7146   if (MovDelay[x][y] == 0)
7147     GfxAction[x][y] = ACTION_DEFAULT;
7148
7149   if (CAN_FALL(element) && y < lev_fieldy - 1)
7150   {
7151     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7152         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7153       if (JustBeingPushed(x, y))
7154         return;
7155
7156     if (element == EL_QUICKSAND_FULL)
7157     {
7158       if (IS_FREE(x, y + 1))
7159       {
7160         InitMovingField(x, y, MV_DOWN);
7161         started_moving = TRUE;
7162
7163         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7164 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7165         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7166           Store[x][y] = EL_ROCK;
7167 #else
7168         Store[x][y] = EL_ROCK;
7169 #endif
7170
7171         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7172       }
7173       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7174       {
7175         if (!MovDelay[x][y])
7176         {
7177           MovDelay[x][y] = TILEY + 1;
7178
7179           ResetGfxAnimation(x, y);
7180           ResetGfxAnimation(x, y + 1);
7181         }
7182
7183         if (MovDelay[x][y])
7184         {
7185           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7186           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7187
7188           MovDelay[x][y]--;
7189           if (MovDelay[x][y])
7190             return;
7191         }
7192
7193         Feld[x][y] = EL_QUICKSAND_EMPTY;
7194         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7195         Store[x][y + 1] = Store[x][y];
7196         Store[x][y] = 0;
7197
7198         PlayLevelSoundAction(x, y, ACTION_FILLING);
7199       }
7200       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7201       {
7202         if (!MovDelay[x][y])
7203         {
7204           MovDelay[x][y] = TILEY + 1;
7205
7206           ResetGfxAnimation(x, y);
7207           ResetGfxAnimation(x, y + 1);
7208         }
7209
7210         if (MovDelay[x][y])
7211         {
7212           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7213           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7214
7215           MovDelay[x][y]--;
7216           if (MovDelay[x][y])
7217             return;
7218         }
7219
7220         Feld[x][y] = EL_QUICKSAND_EMPTY;
7221         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7222         Store[x][y + 1] = Store[x][y];
7223         Store[x][y] = 0;
7224
7225         PlayLevelSoundAction(x, y, ACTION_FILLING);
7226       }
7227     }
7228     else if (element == EL_QUICKSAND_FAST_FULL)
7229     {
7230       if (IS_FREE(x, y + 1))
7231       {
7232         InitMovingField(x, y, MV_DOWN);
7233         started_moving = TRUE;
7234
7235         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7236 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7237         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7238           Store[x][y] = EL_ROCK;
7239 #else
7240         Store[x][y] = EL_ROCK;
7241 #endif
7242
7243         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7244       }
7245       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7246       {
7247         if (!MovDelay[x][y])
7248         {
7249           MovDelay[x][y] = TILEY + 1;
7250
7251           ResetGfxAnimation(x, y);
7252           ResetGfxAnimation(x, y + 1);
7253         }
7254
7255         if (MovDelay[x][y])
7256         {
7257           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7258           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7259
7260           MovDelay[x][y]--;
7261           if (MovDelay[x][y])
7262             return;
7263         }
7264
7265         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7266         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7267         Store[x][y + 1] = Store[x][y];
7268         Store[x][y] = 0;
7269
7270         PlayLevelSoundAction(x, y, ACTION_FILLING);
7271       }
7272       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7273       {
7274         if (!MovDelay[x][y])
7275         {
7276           MovDelay[x][y] = TILEY + 1;
7277
7278           ResetGfxAnimation(x, y);
7279           ResetGfxAnimation(x, y + 1);
7280         }
7281
7282         if (MovDelay[x][y])
7283         {
7284           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7285           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7286
7287           MovDelay[x][y]--;
7288           if (MovDelay[x][y])
7289             return;
7290         }
7291
7292         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7293         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7294         Store[x][y + 1] = Store[x][y];
7295         Store[x][y] = 0;
7296
7297         PlayLevelSoundAction(x, y, ACTION_FILLING);
7298       }
7299     }
7300     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7301              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7302     {
7303       InitMovingField(x, y, MV_DOWN);
7304       started_moving = TRUE;
7305
7306       Feld[x][y] = EL_QUICKSAND_FILLING;
7307       Store[x][y] = element;
7308
7309       PlayLevelSoundAction(x, y, ACTION_FILLING);
7310     }
7311     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7312              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7313     {
7314       InitMovingField(x, y, MV_DOWN);
7315       started_moving = TRUE;
7316
7317       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7318       Store[x][y] = element;
7319
7320       PlayLevelSoundAction(x, y, ACTION_FILLING);
7321     }
7322     else if (element == EL_MAGIC_WALL_FULL)
7323     {
7324       if (IS_FREE(x, y + 1))
7325       {
7326         InitMovingField(x, y, MV_DOWN);
7327         started_moving = TRUE;
7328
7329         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7330         Store[x][y] = EL_CHANGED(Store[x][y]);
7331       }
7332       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7333       {
7334         if (!MovDelay[x][y])
7335           MovDelay[x][y] = TILEY / 4 + 1;
7336
7337         if (MovDelay[x][y])
7338         {
7339           MovDelay[x][y]--;
7340           if (MovDelay[x][y])
7341             return;
7342         }
7343
7344         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7345         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7346         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7347         Store[x][y] = 0;
7348       }
7349     }
7350     else if (element == EL_BD_MAGIC_WALL_FULL)
7351     {
7352       if (IS_FREE(x, y + 1))
7353       {
7354         InitMovingField(x, y, MV_DOWN);
7355         started_moving = TRUE;
7356
7357         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7358         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7359       }
7360       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7361       {
7362         if (!MovDelay[x][y])
7363           MovDelay[x][y] = TILEY / 4 + 1;
7364
7365         if (MovDelay[x][y])
7366         {
7367           MovDelay[x][y]--;
7368           if (MovDelay[x][y])
7369             return;
7370         }
7371
7372         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7373         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7374         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7375         Store[x][y] = 0;
7376       }
7377     }
7378     else if (element == EL_DC_MAGIC_WALL_FULL)
7379     {
7380       if (IS_FREE(x, y + 1))
7381       {
7382         InitMovingField(x, y, MV_DOWN);
7383         started_moving = TRUE;
7384
7385         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7386         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7387       }
7388       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7389       {
7390         if (!MovDelay[x][y])
7391           MovDelay[x][y] = TILEY / 4 + 1;
7392
7393         if (MovDelay[x][y])
7394         {
7395           MovDelay[x][y]--;
7396           if (MovDelay[x][y])
7397             return;
7398         }
7399
7400         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7401         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7402         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7403         Store[x][y] = 0;
7404       }
7405     }
7406     else if ((CAN_PASS_MAGIC_WALL(element) &&
7407               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7408                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7409              (CAN_PASS_DC_MAGIC_WALL(element) &&
7410               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7411
7412     {
7413       InitMovingField(x, y, MV_DOWN);
7414       started_moving = TRUE;
7415
7416       Feld[x][y] =
7417         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7418          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7419          EL_DC_MAGIC_WALL_FILLING);
7420       Store[x][y] = element;
7421     }
7422     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7423     {
7424       SplashAcid(x, y + 1);
7425
7426       InitMovingField(x, y, MV_DOWN);
7427       started_moving = TRUE;
7428
7429       Store[x][y] = EL_ACID;
7430     }
7431     else if (
7432              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7433               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7434              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7435               CAN_FALL(element) && WasJustFalling[x][y] &&
7436               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7437
7438              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7439               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7440               (Feld[x][y + 1] == EL_BLOCKED)))
7441     {
7442       /* this is needed for a special case not covered by calling "Impact()"
7443          from "ContinueMoving()": if an element moves to a tile directly below
7444          another element which was just falling on that tile (which was empty
7445          in the previous frame), the falling element above would just stop
7446          instead of smashing the element below (in previous version, the above
7447          element was just checked for "moving" instead of "falling", resulting
7448          in incorrect smashes caused by horizontal movement of the above
7449          element; also, the case of the player being the element to smash was
7450          simply not covered here... :-/ ) */
7451
7452       CheckCollision[x][y] = 0;
7453       CheckImpact[x][y] = 0;
7454
7455       Impact(x, y);
7456     }
7457     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7458     {
7459       if (MovDir[x][y] == MV_NONE)
7460       {
7461         InitMovingField(x, y, MV_DOWN);
7462         started_moving = TRUE;
7463       }
7464     }
7465     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7466     {
7467       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7468         MovDir[x][y] = MV_DOWN;
7469
7470       InitMovingField(x, y, MV_DOWN);
7471       started_moving = TRUE;
7472     }
7473     else if (element == EL_AMOEBA_DROP)
7474     {
7475       Feld[x][y] = EL_AMOEBA_GROWING;
7476       Store[x][y] = EL_AMOEBA_WET;
7477     }
7478     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7479               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7480              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7481              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7482     {
7483       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7484                                 (IS_FREE(x - 1, y + 1) ||
7485                                  Feld[x - 1][y + 1] == EL_ACID));
7486       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7487                                 (IS_FREE(x + 1, y + 1) ||
7488                                  Feld[x + 1][y + 1] == EL_ACID));
7489       boolean can_fall_any  = (can_fall_left || can_fall_right);
7490       boolean can_fall_both = (can_fall_left && can_fall_right);
7491       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7492
7493       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7494       {
7495         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7496           can_fall_right = FALSE;
7497         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7498           can_fall_left = FALSE;
7499         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7500           can_fall_right = FALSE;
7501         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7502           can_fall_left = FALSE;
7503
7504         can_fall_any  = (can_fall_left || can_fall_right);
7505         can_fall_both = FALSE;
7506       }
7507
7508       if (can_fall_both)
7509       {
7510         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7511           can_fall_right = FALSE;       /* slip down on left side */
7512         else
7513           can_fall_left = !(can_fall_right = RND(2));
7514
7515         can_fall_both = FALSE;
7516       }
7517
7518       if (can_fall_any)
7519       {
7520         /* if not determined otherwise, prefer left side for slipping down */
7521         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7522         started_moving = TRUE;
7523       }
7524     }
7525     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7526     {
7527       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7528       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7529       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7530       int belt_dir = game.belt_dir[belt_nr];
7531
7532       if ((belt_dir == MV_LEFT  && left_is_free) ||
7533           (belt_dir == MV_RIGHT && right_is_free))
7534       {
7535         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7536
7537         InitMovingField(x, y, belt_dir);
7538         started_moving = TRUE;
7539
7540         Pushed[x][y] = TRUE;
7541         Pushed[nextx][y] = TRUE;
7542
7543         GfxAction[x][y] = ACTION_DEFAULT;
7544       }
7545       else
7546       {
7547         MovDir[x][y] = 0;       /* if element was moving, stop it */
7548       }
7549     }
7550   }
7551
7552   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7553   if (CAN_MOVE(element) && !started_moving)
7554   {
7555     int move_pattern = element_info[element].move_pattern;
7556     int newx, newy;
7557
7558     Moving2Blocked(x, y, &newx, &newy);
7559
7560     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7561       return;
7562
7563     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7564         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7565     {
7566       WasJustMoving[x][y] = 0;
7567       CheckCollision[x][y] = 0;
7568
7569       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7570
7571       if (Feld[x][y] != element)        /* element has changed */
7572         return;
7573     }
7574
7575     if (!MovDelay[x][y])        /* start new movement phase */
7576     {
7577       /* all objects that can change their move direction after each step
7578          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7579
7580       if (element != EL_YAMYAM &&
7581           element != EL_DARK_YAMYAM &&
7582           element != EL_PACMAN &&
7583           !(move_pattern & MV_ANY_DIRECTION) &&
7584           move_pattern != MV_TURNING_LEFT &&
7585           move_pattern != MV_TURNING_RIGHT &&
7586           move_pattern != MV_TURNING_LEFT_RIGHT &&
7587           move_pattern != MV_TURNING_RIGHT_LEFT &&
7588           move_pattern != MV_TURNING_RANDOM)
7589       {
7590         TurnRound(x, y);
7591
7592         if (MovDelay[x][y] && (element == EL_BUG ||
7593                                element == EL_SPACESHIP ||
7594                                element == EL_SP_SNIKSNAK ||
7595                                element == EL_SP_ELECTRON ||
7596                                element == EL_MOLE))
7597           TEST_DrawLevelField(x, y);
7598       }
7599     }
7600
7601     if (MovDelay[x][y])         /* wait some time before next movement */
7602     {
7603       MovDelay[x][y]--;
7604
7605       if (element == EL_ROBOT ||
7606           element == EL_YAMYAM ||
7607           element == EL_DARK_YAMYAM)
7608       {
7609         DrawLevelElementAnimationIfNeeded(x, y, element);
7610         PlayLevelSoundAction(x, y, ACTION_WAITING);
7611       }
7612       else if (element == EL_SP_ELECTRON)
7613         DrawLevelElementAnimationIfNeeded(x, y, element);
7614       else if (element == EL_DRAGON)
7615       {
7616         int i;
7617         int dir = MovDir[x][y];
7618         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7619         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7620         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7621                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7622                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7623                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7624         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7625
7626         GfxAction[x][y] = ACTION_ATTACKING;
7627
7628         if (IS_PLAYER(x, y))
7629           DrawPlayerField(x, y);
7630         else
7631           TEST_DrawLevelField(x, y);
7632
7633         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7634
7635         for (i = 1; i <= 3; i++)
7636         {
7637           int xx = x + i * dx;
7638           int yy = y + i * dy;
7639           int sx = SCREENX(xx);
7640           int sy = SCREENY(yy);
7641           int flame_graphic = graphic + (i - 1);
7642
7643           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7644             break;
7645
7646           if (MovDelay[x][y])
7647           {
7648             int flamed = MovingOrBlocked2Element(xx, yy);
7649
7650             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7651               Bang(xx, yy);
7652             else
7653               RemoveMovingField(xx, yy);
7654
7655             ChangeDelay[xx][yy] = 0;
7656
7657             Feld[xx][yy] = EL_FLAMES;
7658
7659             if (IN_SCR_FIELD(sx, sy))
7660             {
7661               TEST_DrawLevelFieldCrumbled(xx, yy);
7662               DrawGraphic(sx, sy, flame_graphic, frame);
7663             }
7664           }
7665           else
7666           {
7667             if (Feld[xx][yy] == EL_FLAMES)
7668               Feld[xx][yy] = EL_EMPTY;
7669             TEST_DrawLevelField(xx, yy);
7670           }
7671         }
7672       }
7673
7674       if (MovDelay[x][y])       /* element still has to wait some time */
7675       {
7676         PlayLevelSoundAction(x, y, ACTION_WAITING);
7677
7678         return;
7679       }
7680     }
7681
7682     /* now make next step */
7683
7684     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7685
7686     if (DONT_COLLIDE_WITH(element) &&
7687         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7688         !PLAYER_ENEMY_PROTECTED(newx, newy))
7689     {
7690       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7691
7692       return;
7693     }
7694
7695     else if (CAN_MOVE_INTO_ACID(element) &&
7696              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7697              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7698              (MovDir[x][y] == MV_DOWN ||
7699               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7700     {
7701       SplashAcid(newx, newy);
7702       Store[x][y] = EL_ACID;
7703     }
7704     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7705     {
7706       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7707           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7708           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7709           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7710       {
7711         RemoveField(x, y);
7712         TEST_DrawLevelField(x, y);
7713
7714         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7715         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7716           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7717
7718         local_player->friends_still_needed--;
7719         if (!local_player->friends_still_needed &&
7720             !local_player->GameOver && AllPlayersGone)
7721           PlayerWins(local_player);
7722
7723         return;
7724       }
7725       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7726       {
7727         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7728           TEST_DrawLevelField(newx, newy);
7729         else
7730           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7731       }
7732       else if (!IS_FREE(newx, newy))
7733       {
7734         GfxAction[x][y] = ACTION_WAITING;
7735
7736         if (IS_PLAYER(x, y))
7737           DrawPlayerField(x, y);
7738         else
7739           TEST_DrawLevelField(x, y);
7740
7741         return;
7742       }
7743     }
7744     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7745     {
7746       if (IS_FOOD_PIG(Feld[newx][newy]))
7747       {
7748         if (IS_MOVING(newx, newy))
7749           RemoveMovingField(newx, newy);
7750         else
7751         {
7752           Feld[newx][newy] = EL_EMPTY;
7753           TEST_DrawLevelField(newx, newy);
7754         }
7755
7756         PlayLevelSound(x, y, SND_PIG_DIGGING);
7757       }
7758       else if (!IS_FREE(newx, newy))
7759       {
7760         if (IS_PLAYER(x, y))
7761           DrawPlayerField(x, y);
7762         else
7763           TEST_DrawLevelField(x, y);
7764
7765         return;
7766       }
7767     }
7768     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7769     {
7770       if (Store[x][y] != EL_EMPTY)
7771       {
7772         boolean can_clone = FALSE;
7773         int xx, yy;
7774
7775         /* check if element to clone is still there */
7776         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7777         {
7778           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7779           {
7780             can_clone = TRUE;
7781
7782             break;
7783           }
7784         }
7785
7786         /* cannot clone or target field not free anymore -- do not clone */
7787         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7788           Store[x][y] = EL_EMPTY;
7789       }
7790
7791       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7792       {
7793         if (IS_MV_DIAGONAL(MovDir[x][y]))
7794         {
7795           int diagonal_move_dir = MovDir[x][y];
7796           int stored = Store[x][y];
7797           int change_delay = 8;
7798           int graphic;
7799
7800           /* android is moving diagonally */
7801
7802           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7803
7804           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7805           GfxElement[x][y] = EL_EMC_ANDROID;
7806           GfxAction[x][y] = ACTION_SHRINKING;
7807           GfxDir[x][y] = diagonal_move_dir;
7808           ChangeDelay[x][y] = change_delay;
7809
7810           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7811                                    GfxDir[x][y]);
7812
7813           DrawLevelGraphicAnimation(x, y, graphic);
7814           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7815
7816           if (Feld[newx][newy] == EL_ACID)
7817           {
7818             SplashAcid(newx, newy);
7819
7820             return;
7821           }
7822
7823           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7824
7825           Store[newx][newy] = EL_EMC_ANDROID;
7826           GfxElement[newx][newy] = EL_EMC_ANDROID;
7827           GfxAction[newx][newy] = ACTION_GROWING;
7828           GfxDir[newx][newy] = diagonal_move_dir;
7829           ChangeDelay[newx][newy] = change_delay;
7830
7831           graphic = el_act_dir2img(GfxElement[newx][newy],
7832                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7833
7834           DrawLevelGraphicAnimation(newx, newy, graphic);
7835           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7836
7837           return;
7838         }
7839         else
7840         {
7841           Feld[newx][newy] = EL_EMPTY;
7842           TEST_DrawLevelField(newx, newy);
7843
7844           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7845         }
7846       }
7847       else if (!IS_FREE(newx, newy))
7848       {
7849         return;
7850       }
7851     }
7852     else if (IS_CUSTOM_ELEMENT(element) &&
7853              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7854     {
7855       if (!DigFieldByCE(newx, newy, element))
7856         return;
7857
7858       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7859       {
7860         RunnerVisit[x][y] = FrameCounter;
7861         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7862       }
7863     }
7864     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7865     {
7866       if (!IS_FREE(newx, newy))
7867       {
7868         if (IS_PLAYER(x, y))
7869           DrawPlayerField(x, y);
7870         else
7871           TEST_DrawLevelField(x, y);
7872
7873         return;
7874       }
7875       else
7876       {
7877         boolean wanna_flame = !RND(10);
7878         int dx = newx - x, dy = newy - y;
7879         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7880         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7881         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7882                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7883         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7884                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7885
7886         if ((wanna_flame ||
7887              IS_CLASSIC_ENEMY(element1) ||
7888              IS_CLASSIC_ENEMY(element2)) &&
7889             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7890             element1 != EL_FLAMES && element2 != EL_FLAMES)
7891         {
7892           ResetGfxAnimation(x, y);
7893           GfxAction[x][y] = ACTION_ATTACKING;
7894
7895           if (IS_PLAYER(x, y))
7896             DrawPlayerField(x, y);
7897           else
7898             TEST_DrawLevelField(x, y);
7899
7900           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7901
7902           MovDelay[x][y] = 50;
7903
7904           Feld[newx][newy] = EL_FLAMES;
7905           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7906             Feld[newx1][newy1] = EL_FLAMES;
7907           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7908             Feld[newx2][newy2] = EL_FLAMES;
7909
7910           return;
7911         }
7912       }
7913     }
7914     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7915              Feld[newx][newy] == EL_DIAMOND)
7916     {
7917       if (IS_MOVING(newx, newy))
7918         RemoveMovingField(newx, newy);
7919       else
7920       {
7921         Feld[newx][newy] = EL_EMPTY;
7922         TEST_DrawLevelField(newx, newy);
7923       }
7924
7925       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7926     }
7927     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7928              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7929     {
7930       if (AmoebaNr[newx][newy])
7931       {
7932         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7933         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7934             Feld[newx][newy] == EL_BD_AMOEBA)
7935           AmoebaCnt[AmoebaNr[newx][newy]]--;
7936       }
7937
7938       if (IS_MOVING(newx, newy))
7939       {
7940         RemoveMovingField(newx, newy);
7941       }
7942       else
7943       {
7944         Feld[newx][newy] = EL_EMPTY;
7945         TEST_DrawLevelField(newx, newy);
7946       }
7947
7948       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7949     }
7950     else if ((element == EL_PACMAN || element == EL_MOLE)
7951              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7952     {
7953       if (AmoebaNr[newx][newy])
7954       {
7955         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7956         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7957             Feld[newx][newy] == EL_BD_AMOEBA)
7958           AmoebaCnt[AmoebaNr[newx][newy]]--;
7959       }
7960
7961       if (element == EL_MOLE)
7962       {
7963         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7964         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7965
7966         ResetGfxAnimation(x, y);
7967         GfxAction[x][y] = ACTION_DIGGING;
7968         TEST_DrawLevelField(x, y);
7969
7970         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7971
7972         return;                         /* wait for shrinking amoeba */
7973       }
7974       else      /* element == EL_PACMAN */
7975       {
7976         Feld[newx][newy] = EL_EMPTY;
7977         TEST_DrawLevelField(newx, newy);
7978         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7979       }
7980     }
7981     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7982              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7983               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7984     {
7985       /* wait for shrinking amoeba to completely disappear */
7986       return;
7987     }
7988     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7989     {
7990       /* object was running against a wall */
7991
7992       TurnRound(x, y);
7993
7994       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7995         DrawLevelElementAnimation(x, y, element);
7996
7997       if (DONT_TOUCH(element))
7998         TestIfBadThingTouchesPlayer(x, y);
7999
8000       return;
8001     }
8002
8003     InitMovingField(x, y, MovDir[x][y]);
8004
8005     PlayLevelSoundAction(x, y, ACTION_MOVING);
8006   }
8007
8008   if (MovDir[x][y])
8009     ContinueMoving(x, y);
8010 }
8011
8012 void ContinueMoving(int x, int y)
8013 {
8014   int element = Feld[x][y];
8015   struct ElementInfo *ei = &element_info[element];
8016   int direction = MovDir[x][y];
8017   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8018   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8019   int newx = x + dx, newy = y + dy;
8020   int stored = Store[x][y];
8021   int stored_new = Store[newx][newy];
8022   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8023   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8024   boolean last_line = (newy == lev_fieldy - 1);
8025
8026   MovPos[x][y] += getElementMoveStepsize(x, y);
8027
8028   if (pushed_by_player) /* special case: moving object pushed by player */
8029     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8030
8031   if (ABS(MovPos[x][y]) < TILEX)
8032   {
8033     TEST_DrawLevelField(x, y);
8034
8035     return;     /* element is still moving */
8036   }
8037
8038   /* element reached destination field */
8039
8040   Feld[x][y] = EL_EMPTY;
8041   Feld[newx][newy] = element;
8042   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8043
8044   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8045   {
8046     element = Feld[newx][newy] = EL_ACID;
8047   }
8048   else if (element == EL_MOLE)
8049   {
8050     Feld[x][y] = EL_SAND;
8051
8052     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8053   }
8054   else if (element == EL_QUICKSAND_FILLING)
8055   {
8056     element = Feld[newx][newy] = get_next_element(element);
8057     Store[newx][newy] = Store[x][y];
8058   }
8059   else if (element == EL_QUICKSAND_EMPTYING)
8060   {
8061     Feld[x][y] = get_next_element(element);
8062     element = Feld[newx][newy] = Store[x][y];
8063   }
8064   else if (element == EL_QUICKSAND_FAST_FILLING)
8065   {
8066     element = Feld[newx][newy] = get_next_element(element);
8067     Store[newx][newy] = Store[x][y];
8068   }
8069   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8070   {
8071     Feld[x][y] = get_next_element(element);
8072     element = Feld[newx][newy] = Store[x][y];
8073   }
8074   else if (element == EL_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_MAGIC_WALL_DEAD;
8079     Store[newx][newy] = Store[x][y];
8080   }
8081   else if (element == EL_MAGIC_WALL_EMPTYING)
8082   {
8083     Feld[x][y] = get_next_element(element);
8084     if (!game.magic_wall_active)
8085       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8086     element = Feld[newx][newy] = Store[x][y];
8087
8088     InitField(newx, newy, FALSE);
8089   }
8090   else if (element == EL_BD_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_BD_MAGIC_WALL_DEAD;
8095     Store[newx][newy] = Store[x][y];
8096   }
8097   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8098   {
8099     Feld[x][y] = get_next_element(element);
8100     if (!game.magic_wall_active)
8101       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8102     element = Feld[newx][newy] = Store[x][y];
8103
8104     InitField(newx, newy, FALSE);
8105   }
8106   else if (element == EL_DC_MAGIC_WALL_FILLING)
8107   {
8108     element = Feld[newx][newy] = get_next_element(element);
8109     if (!game.magic_wall_active)
8110       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8111     Store[newx][newy] = Store[x][y];
8112   }
8113   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8114   {
8115     Feld[x][y] = get_next_element(element);
8116     if (!game.magic_wall_active)
8117       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8118     element = Feld[newx][newy] = Store[x][y];
8119
8120     InitField(newx, newy, FALSE);
8121   }
8122   else if (element == EL_AMOEBA_DROPPING)
8123   {
8124     Feld[x][y] = get_next_element(element);
8125     element = Feld[newx][newy] = Store[x][y];
8126   }
8127   else if (element == EL_SOKOBAN_OBJECT)
8128   {
8129     if (Back[x][y])
8130       Feld[x][y] = Back[x][y];
8131
8132     if (Back[newx][newy])
8133       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8134
8135     Back[x][y] = Back[newx][newy] = 0;
8136   }
8137
8138   Store[x][y] = EL_EMPTY;
8139   MovPos[x][y] = 0;
8140   MovDir[x][y] = 0;
8141   MovDelay[x][y] = 0;
8142
8143   MovDelay[newx][newy] = 0;
8144
8145   if (CAN_CHANGE_OR_HAS_ACTION(element))
8146   {
8147     /* copy element change control values to new field */
8148     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8149     ChangePage[newx][newy]  = ChangePage[x][y];
8150     ChangeCount[newx][newy] = ChangeCount[x][y];
8151     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8152   }
8153
8154   CustomValue[newx][newy] = CustomValue[x][y];
8155
8156   ChangeDelay[x][y] = 0;
8157   ChangePage[x][y] = -1;
8158   ChangeCount[x][y] = 0;
8159   ChangeEvent[x][y] = -1;
8160
8161   CustomValue[x][y] = 0;
8162
8163   /* copy animation control values to new field */
8164   GfxFrame[newx][newy]  = GfxFrame[x][y];
8165   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8166   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8167   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8168
8169   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8170
8171   /* some elements can leave other elements behind after moving */
8172   if (ei->move_leave_element != EL_EMPTY &&
8173       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8174       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8175   {
8176     int move_leave_element = ei->move_leave_element;
8177
8178     /* this makes it possible to leave the removed element again */
8179     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8180       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8181
8182     Feld[x][y] = move_leave_element;
8183
8184     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8185       MovDir[x][y] = direction;
8186
8187     InitField(x, y, FALSE);
8188
8189     if (GFX_CRUMBLED(Feld[x][y]))
8190       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8191
8192     if (ELEM_IS_PLAYER(move_leave_element))
8193       RelocatePlayer(x, y, move_leave_element);
8194   }
8195
8196   /* do this after checking for left-behind element */
8197   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8198
8199   if (!CAN_MOVE(element) ||
8200       (CAN_FALL(element) && direction == MV_DOWN &&
8201        (element == EL_SPRING ||
8202         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8203         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8204     GfxDir[x][y] = MovDir[newx][newy] = 0;
8205
8206   TEST_DrawLevelField(x, y);
8207   TEST_DrawLevelField(newx, newy);
8208
8209   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8210
8211   /* prevent pushed element from moving on in pushed direction */
8212   if (pushed_by_player && CAN_MOVE(element) &&
8213       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8214       !(element_info[element].move_pattern & direction))
8215     TurnRound(newx, newy);
8216
8217   /* prevent elements on conveyor belt from moving on in last direction */
8218   if (pushed_by_conveyor && CAN_FALL(element) &&
8219       direction & MV_HORIZONTAL)
8220     MovDir[newx][newy] = 0;
8221
8222   if (!pushed_by_player)
8223   {
8224     int nextx = newx + dx, nexty = newy + dy;
8225     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8226
8227     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8228
8229     if (CAN_FALL(element) && direction == MV_DOWN)
8230       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8231
8232     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8233       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8234
8235     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8236       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8237   }
8238
8239   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8240   {
8241     TestIfBadThingTouchesPlayer(newx, newy);
8242     TestIfBadThingTouchesFriend(newx, newy);
8243
8244     if (!IS_CUSTOM_ELEMENT(element))
8245       TestIfBadThingTouchesOtherBadThing(newx, newy);
8246   }
8247   else if (element == EL_PENGUIN)
8248     TestIfFriendTouchesBadThing(newx, newy);
8249
8250   if (DONT_GET_HIT_BY(element))
8251   {
8252     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8253   }
8254
8255   /* give the player one last chance (one more frame) to move away */
8256   if (CAN_FALL(element) && direction == MV_DOWN &&
8257       (last_line || (!IS_FREE(x, newy + 1) &&
8258                      (!IS_PLAYER(x, newy + 1) ||
8259                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8260     Impact(x, newy);
8261
8262   if (pushed_by_player && !game.use_change_when_pushing_bug)
8263   {
8264     int push_side = MV_DIR_OPPOSITE(direction);
8265     struct PlayerInfo *player = PLAYERINFO(x, y);
8266
8267     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8268                                player->index_bit, push_side);
8269     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8270                                         player->index_bit, push_side);
8271   }
8272
8273   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8274     MovDelay[newx][newy] = 1;
8275
8276   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8277
8278   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8279   TestIfElementHitsCustomElement(newx, newy, direction);
8280   TestIfPlayerTouchesCustomElement(newx, newy);
8281   TestIfElementTouchesCustomElement(newx, newy);
8282
8283   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8284       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8285     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8286                              MV_DIR_OPPOSITE(direction));
8287 }
8288
8289 int AmoebeNachbarNr(int ax, int ay)
8290 {
8291   int i;
8292   int element = Feld[ax][ay];
8293   int group_nr = 0;
8294   static int xy[4][2] =
8295   {
8296     { 0, -1 },
8297     { -1, 0 },
8298     { +1, 0 },
8299     { 0, +1 }
8300   };
8301
8302   for (i = 0; i < NUM_DIRECTIONS; i++)
8303   {
8304     int x = ax + xy[i][0];
8305     int y = ay + xy[i][1];
8306
8307     if (!IN_LEV_FIELD(x, y))
8308       continue;
8309
8310     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8311       group_nr = AmoebaNr[x][y];
8312   }
8313
8314   return group_nr;
8315 }
8316
8317 void AmoebenVereinigen(int ax, int ay)
8318 {
8319   int i, x, y, xx, yy;
8320   int new_group_nr = AmoebaNr[ax][ay];
8321   static int xy[4][2] =
8322   {
8323     { 0, -1 },
8324     { -1, 0 },
8325     { +1, 0 },
8326     { 0, +1 }
8327   };
8328
8329   if (new_group_nr == 0)
8330     return;
8331
8332   for (i = 0; i < NUM_DIRECTIONS; i++)
8333   {
8334     x = ax + xy[i][0];
8335     y = ay + xy[i][1];
8336
8337     if (!IN_LEV_FIELD(x, y))
8338       continue;
8339
8340     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8341          Feld[x][y] == EL_BD_AMOEBA ||
8342          Feld[x][y] == EL_AMOEBA_DEAD) &&
8343         AmoebaNr[x][y] != new_group_nr)
8344     {
8345       int old_group_nr = AmoebaNr[x][y];
8346
8347       if (old_group_nr == 0)
8348         return;
8349
8350       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8351       AmoebaCnt[old_group_nr] = 0;
8352       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8353       AmoebaCnt2[old_group_nr] = 0;
8354
8355       SCAN_PLAYFIELD(xx, yy)
8356       {
8357         if (AmoebaNr[xx][yy] == old_group_nr)
8358           AmoebaNr[xx][yy] = new_group_nr;
8359       }
8360     }
8361   }
8362 }
8363
8364 void AmoebeUmwandeln(int ax, int ay)
8365 {
8366   int i, x, y;
8367
8368   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8369   {
8370     int group_nr = AmoebaNr[ax][ay];
8371
8372 #ifdef DEBUG
8373     if (group_nr == 0)
8374     {
8375       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8376       printf("AmoebeUmwandeln(): This should never happen!\n");
8377       return;
8378     }
8379 #endif
8380
8381     SCAN_PLAYFIELD(x, y)
8382     {
8383       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8384       {
8385         AmoebaNr[x][y] = 0;
8386         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8387       }
8388     }
8389
8390     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8391                             SND_AMOEBA_TURNING_TO_GEM :
8392                             SND_AMOEBA_TURNING_TO_ROCK));
8393     Bang(ax, ay);
8394   }
8395   else
8396   {
8397     static int xy[4][2] =
8398     {
8399       { 0, -1 },
8400       { -1, 0 },
8401       { +1, 0 },
8402       { 0, +1 }
8403     };
8404
8405     for (i = 0; i < NUM_DIRECTIONS; i++)
8406     {
8407       x = ax + xy[i][0];
8408       y = ay + xy[i][1];
8409
8410       if (!IN_LEV_FIELD(x, y))
8411         continue;
8412
8413       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8414       {
8415         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8416                               SND_AMOEBA_TURNING_TO_GEM :
8417                               SND_AMOEBA_TURNING_TO_ROCK));
8418         Bang(x, y);
8419       }
8420     }
8421   }
8422 }
8423
8424 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8425 {
8426   int x, y;
8427   int group_nr = AmoebaNr[ax][ay];
8428   boolean done = FALSE;
8429
8430 #ifdef DEBUG
8431   if (group_nr == 0)
8432   {
8433     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8434     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8435     return;
8436   }
8437 #endif
8438
8439   SCAN_PLAYFIELD(x, y)
8440   {
8441     if (AmoebaNr[x][y] == group_nr &&
8442         (Feld[x][y] == EL_AMOEBA_DEAD ||
8443          Feld[x][y] == EL_BD_AMOEBA ||
8444          Feld[x][y] == EL_AMOEBA_GROWING))
8445     {
8446       AmoebaNr[x][y] = 0;
8447       Feld[x][y] = new_element;
8448       InitField(x, y, FALSE);
8449       TEST_DrawLevelField(x, y);
8450       done = TRUE;
8451     }
8452   }
8453
8454   if (done)
8455     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8456                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8457                             SND_BD_AMOEBA_TURNING_TO_GEM));
8458 }
8459
8460 void AmoebeWaechst(int x, int y)
8461 {
8462   static unsigned int sound_delay = 0;
8463   static unsigned int sound_delay_value = 0;
8464
8465   if (!MovDelay[x][y])          /* start new growing cycle */
8466   {
8467     MovDelay[x][y] = 7;
8468
8469     if (DelayReached(&sound_delay, sound_delay_value))
8470     {
8471       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8472       sound_delay_value = 30;
8473     }
8474   }
8475
8476   if (MovDelay[x][y])           /* wait some time before growing bigger */
8477   {
8478     MovDelay[x][y]--;
8479     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8480     {
8481       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8482                                            6 - MovDelay[x][y]);
8483
8484       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8485     }
8486
8487     if (!MovDelay[x][y])
8488     {
8489       Feld[x][y] = Store[x][y];
8490       Store[x][y] = 0;
8491       TEST_DrawLevelField(x, y);
8492     }
8493   }
8494 }
8495
8496 void AmoebaDisappearing(int x, int y)
8497 {
8498   static unsigned int sound_delay = 0;
8499   static unsigned int sound_delay_value = 0;
8500
8501   if (!MovDelay[x][y])          /* start new shrinking cycle */
8502   {
8503     MovDelay[x][y] = 7;
8504
8505     if (DelayReached(&sound_delay, sound_delay_value))
8506       sound_delay_value = 30;
8507   }
8508
8509   if (MovDelay[x][y])           /* wait some time before shrinking */
8510   {
8511     MovDelay[x][y]--;
8512     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8513     {
8514       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8515                                            6 - MovDelay[x][y]);
8516
8517       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8518     }
8519
8520     if (!MovDelay[x][y])
8521     {
8522       Feld[x][y] = EL_EMPTY;
8523       TEST_DrawLevelField(x, y);
8524
8525       /* don't let mole enter this field in this cycle;
8526          (give priority to objects falling to this field from above) */
8527       Stop[x][y] = TRUE;
8528     }
8529   }
8530 }
8531
8532 void AmoebeAbleger(int ax, int ay)
8533 {
8534   int i;
8535   int element = Feld[ax][ay];
8536   int graphic = el2img(element);
8537   int newax = ax, neway = ay;
8538   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8539   static int xy[4][2] =
8540   {
8541     { 0, -1 },
8542     { -1, 0 },
8543     { +1, 0 },
8544     { 0, +1 }
8545   };
8546
8547   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8548   {
8549     Feld[ax][ay] = EL_AMOEBA_DEAD;
8550     TEST_DrawLevelField(ax, ay);
8551     return;
8552   }
8553
8554   if (IS_ANIMATED(graphic))
8555     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8556
8557   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8558     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8559
8560   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8561   {
8562     MovDelay[ax][ay]--;
8563     if (MovDelay[ax][ay])
8564       return;
8565   }
8566
8567   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8568   {
8569     int start = RND(4);
8570     int x = ax + xy[start][0];
8571     int y = ay + xy[start][1];
8572
8573     if (!IN_LEV_FIELD(x, y))
8574       return;
8575
8576     if (IS_FREE(x, y) ||
8577         CAN_GROW_INTO(Feld[x][y]) ||
8578         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8579         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8580     {
8581       newax = x;
8582       neway = y;
8583     }
8584
8585     if (newax == ax && neway == ay)
8586       return;
8587   }
8588   else                          /* normal or "filled" (BD style) amoeba */
8589   {
8590     int start = RND(4);
8591     boolean waiting_for_player = FALSE;
8592
8593     for (i = 0; i < NUM_DIRECTIONS; i++)
8594     {
8595       int j = (start + i) % 4;
8596       int x = ax + xy[j][0];
8597       int y = ay + xy[j][1];
8598
8599       if (!IN_LEV_FIELD(x, y))
8600         continue;
8601
8602       if (IS_FREE(x, y) ||
8603           CAN_GROW_INTO(Feld[x][y]) ||
8604           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8605           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8606       {
8607         newax = x;
8608         neway = y;
8609         break;
8610       }
8611       else if (IS_PLAYER(x, y))
8612         waiting_for_player = TRUE;
8613     }
8614
8615     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8616     {
8617       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8618       {
8619         Feld[ax][ay] = EL_AMOEBA_DEAD;
8620         TEST_DrawLevelField(ax, ay);
8621         AmoebaCnt[AmoebaNr[ax][ay]]--;
8622
8623         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8624         {
8625           if (element == EL_AMOEBA_FULL)
8626             AmoebeUmwandeln(ax, ay);
8627           else if (element == EL_BD_AMOEBA)
8628             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8629         }
8630       }
8631       return;
8632     }
8633     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8634     {
8635       /* amoeba gets larger by growing in some direction */
8636
8637       int new_group_nr = AmoebaNr[ax][ay];
8638
8639 #ifdef DEBUG
8640   if (new_group_nr == 0)
8641   {
8642     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8643     printf("AmoebeAbleger(): This should never happen!\n");
8644     return;
8645   }
8646 #endif
8647
8648       AmoebaNr[newax][neway] = new_group_nr;
8649       AmoebaCnt[new_group_nr]++;
8650       AmoebaCnt2[new_group_nr]++;
8651
8652       /* if amoeba touches other amoeba(s) after growing, unify them */
8653       AmoebenVereinigen(newax, neway);
8654
8655       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8656       {
8657         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8658         return;
8659       }
8660     }
8661   }
8662
8663   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8664       (neway == lev_fieldy - 1 && newax != ax))
8665   {
8666     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8667     Store[newax][neway] = element;
8668   }
8669   else if (neway == ay || element == EL_EMC_DRIPPER)
8670   {
8671     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8672
8673     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8674   }
8675   else
8676   {
8677     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8678     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8679     Store[ax][ay] = EL_AMOEBA_DROP;
8680     ContinueMoving(ax, ay);
8681     return;
8682   }
8683
8684   TEST_DrawLevelField(newax, neway);
8685 }
8686
8687 void Life(int ax, int ay)
8688 {
8689   int x1, y1, x2, y2;
8690   int life_time = 40;
8691   int element = Feld[ax][ay];
8692   int graphic = el2img(element);
8693   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8694                          level.biomaze);
8695   boolean changed = FALSE;
8696
8697   if (IS_ANIMATED(graphic))
8698     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8699
8700   if (Stop[ax][ay])
8701     return;
8702
8703   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8704     MovDelay[ax][ay] = life_time;
8705
8706   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8707   {
8708     MovDelay[ax][ay]--;
8709     if (MovDelay[ax][ay])
8710       return;
8711   }
8712
8713   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8714   {
8715     int xx = ax+x1, yy = ay+y1;
8716     int nachbarn = 0;
8717
8718     if (!IN_LEV_FIELD(xx, yy))
8719       continue;
8720
8721     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8722     {
8723       int x = xx+x2, y = yy+y2;
8724
8725       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8726         continue;
8727
8728       if (((Feld[x][y] == element ||
8729             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8730            !Stop[x][y]) ||
8731           (IS_FREE(x, y) && Stop[x][y]))
8732         nachbarn++;
8733     }
8734
8735     if (xx == ax && yy == ay)           /* field in the middle */
8736     {
8737       if (nachbarn < life_parameter[0] ||
8738           nachbarn > life_parameter[1])
8739       {
8740         Feld[xx][yy] = EL_EMPTY;
8741         if (!Stop[xx][yy])
8742           TEST_DrawLevelField(xx, yy);
8743         Stop[xx][yy] = TRUE;
8744         changed = TRUE;
8745       }
8746     }
8747     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8748     {                                   /* free border field */
8749       if (nachbarn >= life_parameter[2] &&
8750           nachbarn <= life_parameter[3])
8751       {
8752         Feld[xx][yy] = element;
8753         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8754         if (!Stop[xx][yy])
8755           TEST_DrawLevelField(xx, yy);
8756         Stop[xx][yy] = TRUE;
8757         changed = TRUE;
8758       }
8759     }
8760   }
8761
8762   if (changed)
8763     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8764                    SND_GAME_OF_LIFE_GROWING);
8765 }
8766
8767 static void InitRobotWheel(int x, int y)
8768 {
8769   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8770 }
8771
8772 static void RunRobotWheel(int x, int y)
8773 {
8774   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8775 }
8776
8777 static void StopRobotWheel(int x, int y)
8778 {
8779   if (ZX == x && ZY == y)
8780   {
8781     ZX = ZY = -1;
8782
8783     game.robot_wheel_active = FALSE;
8784   }
8785 }
8786
8787 static void InitTimegateWheel(int x, int y)
8788 {
8789   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8790 }
8791
8792 static void RunTimegateWheel(int x, int y)
8793 {
8794   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8795 }
8796
8797 static void InitMagicBallDelay(int x, int y)
8798 {
8799   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8800 }
8801
8802 static void ActivateMagicBall(int bx, int by)
8803 {
8804   int x, y;
8805
8806   if (level.ball_random)
8807   {
8808     int pos_border = RND(8);    /* select one of the eight border elements */
8809     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8810     int xx = pos_content % 3;
8811     int yy = pos_content / 3;
8812
8813     x = bx - 1 + xx;
8814     y = by - 1 + yy;
8815
8816     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8817       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8818   }
8819   else
8820   {
8821     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8822     {
8823       int xx = x - bx + 1;
8824       int yy = y - by + 1;
8825
8826       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8827         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8828     }
8829   }
8830
8831   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8832 }
8833
8834 void CheckExit(int x, int y)
8835 {
8836   if (local_player->gems_still_needed > 0 ||
8837       local_player->sokobanfields_still_needed > 0 ||
8838       local_player->lights_still_needed > 0)
8839   {
8840     int element = Feld[x][y];
8841     int graphic = el2img(element);
8842
8843     if (IS_ANIMATED(graphic))
8844       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8845
8846     return;
8847   }
8848
8849   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8850     return;
8851
8852   Feld[x][y] = EL_EXIT_OPENING;
8853
8854   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8855 }
8856
8857 void CheckExitEM(int x, int y)
8858 {
8859   if (local_player->gems_still_needed > 0 ||
8860       local_player->sokobanfields_still_needed > 0 ||
8861       local_player->lights_still_needed > 0)
8862   {
8863     int element = Feld[x][y];
8864     int graphic = el2img(element);
8865
8866     if (IS_ANIMATED(graphic))
8867       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8868
8869     return;
8870   }
8871
8872   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8873     return;
8874
8875   Feld[x][y] = EL_EM_EXIT_OPENING;
8876
8877   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8878 }
8879
8880 void CheckExitSteel(int x, int y)
8881 {
8882   if (local_player->gems_still_needed > 0 ||
8883       local_player->sokobanfields_still_needed > 0 ||
8884       local_player->lights_still_needed > 0)
8885   {
8886     int element = Feld[x][y];
8887     int graphic = el2img(element);
8888
8889     if (IS_ANIMATED(graphic))
8890       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8891
8892     return;
8893   }
8894
8895   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8896     return;
8897
8898   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8899
8900   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8901 }
8902
8903 void CheckExitSteelEM(int x, int y)
8904 {
8905   if (local_player->gems_still_needed > 0 ||
8906       local_player->sokobanfields_still_needed > 0 ||
8907       local_player->lights_still_needed > 0)
8908   {
8909     int element = Feld[x][y];
8910     int graphic = el2img(element);
8911
8912     if (IS_ANIMATED(graphic))
8913       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8914
8915     return;
8916   }
8917
8918   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8919     return;
8920
8921   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8922
8923   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8924 }
8925
8926 void CheckExitSP(int x, int y)
8927 {
8928   if (local_player->gems_still_needed > 0)
8929   {
8930     int element = Feld[x][y];
8931     int graphic = el2img(element);
8932
8933     if (IS_ANIMATED(graphic))
8934       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8935
8936     return;
8937   }
8938
8939   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8940     return;
8941
8942   Feld[x][y] = EL_SP_EXIT_OPENING;
8943
8944   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8945 }
8946
8947 static void CloseAllOpenTimegates()
8948 {
8949   int x, y;
8950
8951   SCAN_PLAYFIELD(x, y)
8952   {
8953     int element = Feld[x][y];
8954
8955     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8956     {
8957       Feld[x][y] = EL_TIMEGATE_CLOSING;
8958
8959       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8960     }
8961   }
8962 }
8963
8964 void DrawTwinkleOnField(int x, int y)
8965 {
8966   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8967     return;
8968
8969   if (Feld[x][y] == EL_BD_DIAMOND)
8970     return;
8971
8972   if (MovDelay[x][y] == 0)      /* next animation frame */
8973     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8974
8975   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8976   {
8977     MovDelay[x][y]--;
8978
8979     DrawLevelElementAnimation(x, y, Feld[x][y]);
8980
8981     if (MovDelay[x][y] != 0)
8982     {
8983       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8984                                            10 - MovDelay[x][y]);
8985
8986       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8987     }
8988   }
8989 }
8990
8991 void MauerWaechst(int x, int y)
8992 {
8993   int delay = 6;
8994
8995   if (!MovDelay[x][y])          /* next animation frame */
8996     MovDelay[x][y] = 3 * delay;
8997
8998   if (MovDelay[x][y])           /* wait some time before next frame */
8999   {
9000     MovDelay[x][y]--;
9001
9002     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9003     {
9004       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9005       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9006
9007       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9008     }
9009
9010     if (!MovDelay[x][y])
9011     {
9012       if (MovDir[x][y] == MV_LEFT)
9013       {
9014         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9015           TEST_DrawLevelField(x - 1, y);
9016       }
9017       else if (MovDir[x][y] == MV_RIGHT)
9018       {
9019         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9020           TEST_DrawLevelField(x + 1, y);
9021       }
9022       else if (MovDir[x][y] == MV_UP)
9023       {
9024         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9025           TEST_DrawLevelField(x, y - 1);
9026       }
9027       else
9028       {
9029         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9030           TEST_DrawLevelField(x, y + 1);
9031       }
9032
9033       Feld[x][y] = Store[x][y];
9034       Store[x][y] = 0;
9035       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9036       TEST_DrawLevelField(x, y);
9037     }
9038   }
9039 }
9040
9041 void MauerAbleger(int ax, int ay)
9042 {
9043   int element = Feld[ax][ay];
9044   int graphic = el2img(element);
9045   boolean oben_frei = FALSE, unten_frei = FALSE;
9046   boolean links_frei = FALSE, rechts_frei = FALSE;
9047   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9048   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9049   boolean new_wall = FALSE;
9050
9051   if (IS_ANIMATED(graphic))
9052     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9053
9054   if (!MovDelay[ax][ay])        /* start building new wall */
9055     MovDelay[ax][ay] = 6;
9056
9057   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9058   {
9059     MovDelay[ax][ay]--;
9060     if (MovDelay[ax][ay])
9061       return;
9062   }
9063
9064   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9065     oben_frei = TRUE;
9066   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9067     unten_frei = TRUE;
9068   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9069     links_frei = TRUE;
9070   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9071     rechts_frei = TRUE;
9072
9073   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9074       element == EL_EXPANDABLE_WALL_ANY)
9075   {
9076     if (oben_frei)
9077     {
9078       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9079       Store[ax][ay-1] = element;
9080       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9081       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9082         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9083                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9084       new_wall = TRUE;
9085     }
9086     if (unten_frei)
9087     {
9088       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9089       Store[ax][ay+1] = element;
9090       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9091       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9092         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9093                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9094       new_wall = TRUE;
9095     }
9096   }
9097
9098   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9099       element == EL_EXPANDABLE_WALL_ANY ||
9100       element == EL_EXPANDABLE_WALL ||
9101       element == EL_BD_EXPANDABLE_WALL)
9102   {
9103     if (links_frei)
9104     {
9105       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9106       Store[ax-1][ay] = element;
9107       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9108       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9109         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9110                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9111       new_wall = TRUE;
9112     }
9113
9114     if (rechts_frei)
9115     {
9116       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9117       Store[ax+1][ay] = element;
9118       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9119       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9120         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9121                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9122       new_wall = TRUE;
9123     }
9124   }
9125
9126   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9127     TEST_DrawLevelField(ax, ay);
9128
9129   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9130     oben_massiv = TRUE;
9131   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9132     unten_massiv = TRUE;
9133   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9134     links_massiv = TRUE;
9135   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9136     rechts_massiv = TRUE;
9137
9138   if (((oben_massiv && unten_massiv) ||
9139        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9140        element == EL_EXPANDABLE_WALL) &&
9141       ((links_massiv && rechts_massiv) ||
9142        element == EL_EXPANDABLE_WALL_VERTICAL))
9143     Feld[ax][ay] = EL_WALL;
9144
9145   if (new_wall)
9146     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9147 }
9148
9149 void MauerAblegerStahl(int ax, int ay)
9150 {
9151   int element = Feld[ax][ay];
9152   int graphic = el2img(element);
9153   boolean oben_frei = FALSE, unten_frei = FALSE;
9154   boolean links_frei = FALSE, rechts_frei = FALSE;
9155   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9156   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9157   boolean new_wall = FALSE;
9158
9159   if (IS_ANIMATED(graphic))
9160     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9161
9162   if (!MovDelay[ax][ay])        /* start building new wall */
9163     MovDelay[ax][ay] = 6;
9164
9165   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9166   {
9167     MovDelay[ax][ay]--;
9168     if (MovDelay[ax][ay])
9169       return;
9170   }
9171
9172   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9173     oben_frei = TRUE;
9174   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9175     unten_frei = TRUE;
9176   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9177     links_frei = TRUE;
9178   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9179     rechts_frei = TRUE;
9180
9181   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9182       element == EL_EXPANDABLE_STEELWALL_ANY)
9183   {
9184     if (oben_frei)
9185     {
9186       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9187       Store[ax][ay-1] = element;
9188       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9189       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9190         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9191                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9192       new_wall = TRUE;
9193     }
9194     if (unten_frei)
9195     {
9196       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9197       Store[ax][ay+1] = element;
9198       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9199       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9200         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9201                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9202       new_wall = TRUE;
9203     }
9204   }
9205
9206   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9207       element == EL_EXPANDABLE_STEELWALL_ANY)
9208   {
9209     if (links_frei)
9210     {
9211       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9212       Store[ax-1][ay] = element;
9213       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9214       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9215         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9216                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9217       new_wall = TRUE;
9218     }
9219
9220     if (rechts_frei)
9221     {
9222       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9223       Store[ax+1][ay] = element;
9224       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9225       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9226         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9227                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9228       new_wall = TRUE;
9229     }
9230   }
9231
9232   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9233     oben_massiv = TRUE;
9234   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9235     unten_massiv = TRUE;
9236   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9237     links_massiv = TRUE;
9238   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9239     rechts_massiv = TRUE;
9240
9241   if (((oben_massiv && unten_massiv) ||
9242        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9243       ((links_massiv && rechts_massiv) ||
9244        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9245     Feld[ax][ay] = EL_STEELWALL;
9246
9247   if (new_wall)
9248     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9249 }
9250
9251 void CheckForDragon(int x, int y)
9252 {
9253   int i, j;
9254   boolean dragon_found = FALSE;
9255   static int xy[4][2] =
9256   {
9257     { 0, -1 },
9258     { -1, 0 },
9259     { +1, 0 },
9260     { 0, +1 }
9261   };
9262
9263   for (i = 0; i < NUM_DIRECTIONS; i++)
9264   {
9265     for (j = 0; j < 4; j++)
9266     {
9267       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9268
9269       if (IN_LEV_FIELD(xx, yy) &&
9270           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9271       {
9272         if (Feld[xx][yy] == EL_DRAGON)
9273           dragon_found = TRUE;
9274       }
9275       else
9276         break;
9277     }
9278   }
9279
9280   if (!dragon_found)
9281   {
9282     for (i = 0; i < NUM_DIRECTIONS; i++)
9283     {
9284       for (j = 0; j < 3; j++)
9285       {
9286         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9287   
9288         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9289         {
9290           Feld[xx][yy] = EL_EMPTY;
9291           TEST_DrawLevelField(xx, yy);
9292         }
9293         else
9294           break;
9295       }
9296     }
9297   }
9298 }
9299
9300 static void InitBuggyBase(int x, int y)
9301 {
9302   int element = Feld[x][y];
9303   int activating_delay = FRAMES_PER_SECOND / 4;
9304
9305   ChangeDelay[x][y] =
9306     (element == EL_SP_BUGGY_BASE ?
9307      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9308      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9309      activating_delay :
9310      element == EL_SP_BUGGY_BASE_ACTIVE ?
9311      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9312 }
9313
9314 static void WarnBuggyBase(int x, int y)
9315 {
9316   int i;
9317   static int xy[4][2] =
9318   {
9319     { 0, -1 },
9320     { -1, 0 },
9321     { +1, 0 },
9322     { 0, +1 }
9323   };
9324
9325   for (i = 0; i < NUM_DIRECTIONS; i++)
9326   {
9327     int xx = x + xy[i][0];
9328     int yy = y + xy[i][1];
9329
9330     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9331     {
9332       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9333
9334       break;
9335     }
9336   }
9337 }
9338
9339 static void InitTrap(int x, int y)
9340 {
9341   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9342 }
9343
9344 static void ActivateTrap(int x, int y)
9345 {
9346   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9347 }
9348
9349 static void ChangeActiveTrap(int x, int y)
9350 {
9351   int graphic = IMG_TRAP_ACTIVE;
9352
9353   /* if new animation frame was drawn, correct crumbled sand border */
9354   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9355     TEST_DrawLevelFieldCrumbled(x, y);
9356 }
9357
9358 static int getSpecialActionElement(int element, int number, int base_element)
9359 {
9360   return (element != EL_EMPTY ? element :
9361           number != -1 ? base_element + number - 1 :
9362           EL_EMPTY);
9363 }
9364
9365 static int getModifiedActionNumber(int value_old, int operator, int operand,
9366                                    int value_min, int value_max)
9367 {
9368   int value_new = (operator == CA_MODE_SET      ? operand :
9369                    operator == CA_MODE_ADD      ? value_old + operand :
9370                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9371                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9372                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9373                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9374                    value_old);
9375
9376   return (value_new < value_min ? value_min :
9377           value_new > value_max ? value_max :
9378           value_new);
9379 }
9380
9381 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9382 {
9383   struct ElementInfo *ei = &element_info[element];
9384   struct ElementChangeInfo *change = &ei->change_page[page];
9385   int target_element = change->target_element;
9386   int action_type = change->action_type;
9387   int action_mode = change->action_mode;
9388   int action_arg = change->action_arg;
9389   int action_element = change->action_element;
9390   int i;
9391
9392   if (!change->has_action)
9393     return;
9394
9395   /* ---------- determine action paramater values -------------------------- */
9396
9397   int level_time_value =
9398     (level.time > 0 ? TimeLeft :
9399      TimePlayed);
9400
9401   int action_arg_element_raw =
9402     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9403      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9404      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9405      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9406      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9407      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9408      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9409      EL_EMPTY);
9410   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9411
9412   int action_arg_direction =
9413     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9414      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9415      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9416      change->actual_trigger_side :
9417      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9418      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9419      MV_NONE);
9420
9421   int action_arg_number_min =
9422     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9423      CA_ARG_MIN);
9424
9425   int action_arg_number_max =
9426     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9427      action_type == CA_SET_LEVEL_GEMS ? 999 :
9428      action_type == CA_SET_LEVEL_TIME ? 9999 :
9429      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9430      action_type == CA_SET_CE_VALUE ? 9999 :
9431      action_type == CA_SET_CE_SCORE ? 9999 :
9432      CA_ARG_MAX);
9433
9434   int action_arg_number_reset =
9435     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9436      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9437      action_type == CA_SET_LEVEL_TIME ? level.time :
9438      action_type == CA_SET_LEVEL_SCORE ? 0 :
9439      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9440      action_type == CA_SET_CE_SCORE ? 0 :
9441      0);
9442
9443   int action_arg_number =
9444     (action_arg <= CA_ARG_MAX ? action_arg :
9445      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9446      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9447      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9448      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9449      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9450      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9451      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9452      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9453      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9454      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9455      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9456      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9457      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9458      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9459      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9460      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9461      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9462      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9463      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9464      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9465      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9466      -1);
9467
9468   int action_arg_number_old =
9469     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9470      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9471      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9472      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9473      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9474      0);
9475
9476   int action_arg_number_new =
9477     getModifiedActionNumber(action_arg_number_old,
9478                             action_mode, action_arg_number,
9479                             action_arg_number_min, action_arg_number_max);
9480
9481   int trigger_player_bits =
9482     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9483      change->actual_trigger_player_bits : change->trigger_player);
9484
9485   int action_arg_player_bits =
9486     (action_arg >= CA_ARG_PLAYER_1 &&
9487      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9488      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9489      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9490      PLAYER_BITS_ANY);
9491
9492   /* ---------- execute action  -------------------------------------------- */
9493
9494   switch (action_type)
9495   {
9496     case CA_NO_ACTION:
9497     {
9498       return;
9499     }
9500
9501     /* ---------- level actions  ------------------------------------------- */
9502
9503     case CA_RESTART_LEVEL:
9504     {
9505       game.restart_level = TRUE;
9506
9507       break;
9508     }
9509
9510     case CA_SHOW_ENVELOPE:
9511     {
9512       int element = getSpecialActionElement(action_arg_element,
9513                                             action_arg_number, EL_ENVELOPE_1);
9514
9515       if (IS_ENVELOPE(element))
9516         local_player->show_envelope = element;
9517
9518       break;
9519     }
9520
9521     case CA_SET_LEVEL_TIME:
9522     {
9523       if (level.time > 0)       /* only modify limited time value */
9524       {
9525         TimeLeft = action_arg_number_new;
9526
9527         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9528
9529         DisplayGameControlValues();
9530
9531         if (!TimeLeft && setup.time_limit)
9532           for (i = 0; i < MAX_PLAYERS; i++)
9533             KillPlayer(&stored_player[i]);
9534       }
9535
9536       break;
9537     }
9538
9539     case CA_SET_LEVEL_SCORE:
9540     {
9541       local_player->score = action_arg_number_new;
9542
9543       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9544
9545       DisplayGameControlValues();
9546
9547       break;
9548     }
9549
9550     case CA_SET_LEVEL_GEMS:
9551     {
9552       local_player->gems_still_needed = action_arg_number_new;
9553
9554       game_panel_controls[GAME_PANEL_GEMS].value =
9555         local_player->gems_still_needed;
9556
9557       DisplayGameControlValues();
9558
9559       break;
9560     }
9561
9562     case CA_SET_LEVEL_WIND:
9563     {
9564       game.wind_direction = action_arg_direction;
9565
9566       break;
9567     }
9568
9569     case CA_SET_LEVEL_RANDOM_SEED:
9570     {
9571       /* ensure that setting a new random seed while playing is predictable */
9572       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9573
9574       break;
9575     }
9576
9577     /* ---------- player actions  ------------------------------------------ */
9578
9579     case CA_MOVE_PLAYER:
9580     {
9581       /* automatically move to the next field in specified direction */
9582       for (i = 0; i < MAX_PLAYERS; i++)
9583         if (trigger_player_bits & (1 << i))
9584           stored_player[i].programmed_action = action_arg_direction;
9585
9586       break;
9587     }
9588
9589     case CA_EXIT_PLAYER:
9590     {
9591       for (i = 0; i < MAX_PLAYERS; i++)
9592         if (action_arg_player_bits & (1 << i))
9593           PlayerWins(&stored_player[i]);
9594
9595       break;
9596     }
9597
9598     case CA_KILL_PLAYER:
9599     {
9600       for (i = 0; i < MAX_PLAYERS; i++)
9601         if (action_arg_player_bits & (1 << i))
9602           KillPlayer(&stored_player[i]);
9603
9604       break;
9605     }
9606
9607     case CA_SET_PLAYER_KEYS:
9608     {
9609       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9610       int element = getSpecialActionElement(action_arg_element,
9611                                             action_arg_number, EL_KEY_1);
9612
9613       if (IS_KEY(element))
9614       {
9615         for (i = 0; i < MAX_PLAYERS; i++)
9616         {
9617           if (trigger_player_bits & (1 << i))
9618           {
9619             stored_player[i].key[KEY_NR(element)] = key_state;
9620
9621             DrawGameDoorValues();
9622           }
9623         }
9624       }
9625
9626       break;
9627     }
9628
9629     case CA_SET_PLAYER_SPEED:
9630     {
9631       for (i = 0; i < MAX_PLAYERS; i++)
9632       {
9633         if (trigger_player_bits & (1 << i))
9634         {
9635           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9636
9637           if (action_arg == CA_ARG_SPEED_FASTER &&
9638               stored_player[i].cannot_move)
9639           {
9640             action_arg_number = STEPSIZE_VERY_SLOW;
9641           }
9642           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9643                    action_arg == CA_ARG_SPEED_FASTER)
9644           {
9645             action_arg_number = 2;
9646             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9647                            CA_MODE_MULTIPLY);
9648           }
9649           else if (action_arg == CA_ARG_NUMBER_RESET)
9650           {
9651             action_arg_number = level.initial_player_stepsize[i];
9652           }
9653
9654           move_stepsize =
9655             getModifiedActionNumber(move_stepsize,
9656                                     action_mode,
9657                                     action_arg_number,
9658                                     action_arg_number_min,
9659                                     action_arg_number_max);
9660
9661           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9662         }
9663       }
9664
9665       break;
9666     }
9667
9668     case CA_SET_PLAYER_SHIELD:
9669     {
9670       for (i = 0; i < MAX_PLAYERS; i++)
9671       {
9672         if (trigger_player_bits & (1 << i))
9673         {
9674           if (action_arg == CA_ARG_SHIELD_OFF)
9675           {
9676             stored_player[i].shield_normal_time_left = 0;
9677             stored_player[i].shield_deadly_time_left = 0;
9678           }
9679           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9680           {
9681             stored_player[i].shield_normal_time_left = 999999;
9682           }
9683           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9684           {
9685             stored_player[i].shield_normal_time_left = 999999;
9686             stored_player[i].shield_deadly_time_left = 999999;
9687           }
9688         }
9689       }
9690
9691       break;
9692     }
9693
9694     case CA_SET_PLAYER_GRAVITY:
9695     {
9696       for (i = 0; i < MAX_PLAYERS; i++)
9697       {
9698         if (trigger_player_bits & (1 << i))
9699         {
9700           stored_player[i].gravity =
9701             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9702              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9703              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9704              stored_player[i].gravity);
9705         }
9706       }
9707
9708       break;
9709     }
9710
9711     case CA_SET_PLAYER_ARTWORK:
9712     {
9713       for (i = 0; i < MAX_PLAYERS; i++)
9714       {
9715         if (trigger_player_bits & (1 << i))
9716         {
9717           int artwork_element = action_arg_element;
9718
9719           if (action_arg == CA_ARG_ELEMENT_RESET)
9720             artwork_element =
9721               (level.use_artwork_element[i] ? level.artwork_element[i] :
9722                stored_player[i].element_nr);
9723
9724           if (stored_player[i].artwork_element != artwork_element)
9725             stored_player[i].Frame = 0;
9726
9727           stored_player[i].artwork_element = artwork_element;
9728
9729           SetPlayerWaiting(&stored_player[i], FALSE);
9730
9731           /* set number of special actions for bored and sleeping animation */
9732           stored_player[i].num_special_action_bored =
9733             get_num_special_action(artwork_element,
9734                                    ACTION_BORING_1, ACTION_BORING_LAST);
9735           stored_player[i].num_special_action_sleeping =
9736             get_num_special_action(artwork_element,
9737                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9738         }
9739       }
9740
9741       break;
9742     }
9743
9744     case CA_SET_PLAYER_INVENTORY:
9745     {
9746       for (i = 0; i < MAX_PLAYERS; i++)
9747       {
9748         struct PlayerInfo *player = &stored_player[i];
9749         int j, k;
9750
9751         if (trigger_player_bits & (1 << i))
9752         {
9753           int inventory_element = action_arg_element;
9754
9755           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9756               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9757               action_arg == CA_ARG_ELEMENT_ACTION)
9758           {
9759             int element = inventory_element;
9760             int collect_count = element_info[element].collect_count_initial;
9761
9762             if (!IS_CUSTOM_ELEMENT(element))
9763               collect_count = 1;
9764
9765             if (collect_count == 0)
9766               player->inventory_infinite_element = element;
9767             else
9768               for (k = 0; k < collect_count; k++)
9769                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9770                   player->inventory_element[player->inventory_size++] =
9771                     element;
9772           }
9773           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9774                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9775                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9776           {
9777             if (player->inventory_infinite_element != EL_UNDEFINED &&
9778                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9779                                      action_arg_element_raw))
9780               player->inventory_infinite_element = EL_UNDEFINED;
9781
9782             for (k = 0, j = 0; j < player->inventory_size; j++)
9783             {
9784               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9785                                         action_arg_element_raw))
9786                 player->inventory_element[k++] = player->inventory_element[j];
9787             }
9788
9789             player->inventory_size = k;
9790           }
9791           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9792           {
9793             if (player->inventory_size > 0)
9794             {
9795               for (j = 0; j < player->inventory_size - 1; j++)
9796                 player->inventory_element[j] = player->inventory_element[j + 1];
9797
9798               player->inventory_size--;
9799             }
9800           }
9801           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9802           {
9803             if (player->inventory_size > 0)
9804               player->inventory_size--;
9805           }
9806           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9807           {
9808             player->inventory_infinite_element = EL_UNDEFINED;
9809             player->inventory_size = 0;
9810           }
9811           else if (action_arg == CA_ARG_INVENTORY_RESET)
9812           {
9813             player->inventory_infinite_element = EL_UNDEFINED;
9814             player->inventory_size = 0;
9815
9816             if (level.use_initial_inventory[i])
9817             {
9818               for (j = 0; j < level.initial_inventory_size[i]; j++)
9819               {
9820                 int element = level.initial_inventory_content[i][j];
9821                 int collect_count = element_info[element].collect_count_initial;
9822
9823                 if (!IS_CUSTOM_ELEMENT(element))
9824                   collect_count = 1;
9825
9826                 if (collect_count == 0)
9827                   player->inventory_infinite_element = element;
9828                 else
9829                   for (k = 0; k < collect_count; k++)
9830                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9831                       player->inventory_element[player->inventory_size++] =
9832                         element;
9833               }
9834             }
9835           }
9836         }
9837       }
9838
9839       break;
9840     }
9841
9842     /* ---------- CE actions  ---------------------------------------------- */
9843
9844     case CA_SET_CE_VALUE:
9845     {
9846       int last_ce_value = CustomValue[x][y];
9847
9848       CustomValue[x][y] = action_arg_number_new;
9849
9850       if (CustomValue[x][y] != last_ce_value)
9851       {
9852         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9853         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9854
9855         if (CustomValue[x][y] == 0)
9856         {
9857           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9858           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9859         }
9860       }
9861
9862       break;
9863     }
9864
9865     case CA_SET_CE_SCORE:
9866     {
9867       int last_ce_score = ei->collect_score;
9868
9869       ei->collect_score = action_arg_number_new;
9870
9871       if (ei->collect_score != last_ce_score)
9872       {
9873         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9874         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9875
9876         if (ei->collect_score == 0)
9877         {
9878           int xx, yy;
9879
9880           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9881           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9882
9883           /*
9884             This is a very special case that seems to be a mixture between
9885             CheckElementChange() and CheckTriggeredElementChange(): while
9886             the first one only affects single elements that are triggered
9887             directly, the second one affects multiple elements in the playfield
9888             that are triggered indirectly by another element. This is a third
9889             case: Changing the CE score always affects multiple identical CEs,
9890             so every affected CE must be checked, not only the single CE for
9891             which the CE score was changed in the first place (as every instance
9892             of that CE shares the same CE score, and therefore also can change)!
9893           */
9894           SCAN_PLAYFIELD(xx, yy)
9895           {
9896             if (Feld[xx][yy] == element)
9897               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9898                                  CE_SCORE_GETS_ZERO);
9899           }
9900         }
9901       }
9902
9903       break;
9904     }
9905
9906     case CA_SET_CE_ARTWORK:
9907     {
9908       int artwork_element = action_arg_element;
9909       boolean reset_frame = FALSE;
9910       int xx, yy;
9911
9912       if (action_arg == CA_ARG_ELEMENT_RESET)
9913         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9914                            element);
9915
9916       if (ei->gfx_element != artwork_element)
9917         reset_frame = TRUE;
9918
9919       ei->gfx_element = artwork_element;
9920
9921       SCAN_PLAYFIELD(xx, yy)
9922       {
9923         if (Feld[xx][yy] == element)
9924         {
9925           if (reset_frame)
9926           {
9927             ResetGfxAnimation(xx, yy);
9928             ResetRandomAnimationValue(xx, yy);
9929           }
9930
9931           TEST_DrawLevelField(xx, yy);
9932         }
9933       }
9934
9935       break;
9936     }
9937
9938     /* ---------- engine actions  ------------------------------------------ */
9939
9940     case CA_SET_ENGINE_SCAN_MODE:
9941     {
9942       InitPlayfieldScanMode(action_arg);
9943
9944       break;
9945     }
9946
9947     default:
9948       break;
9949   }
9950 }
9951
9952 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9953 {
9954   int old_element = Feld[x][y];
9955   int new_element = GetElementFromGroupElement(element);
9956   int previous_move_direction = MovDir[x][y];
9957   int last_ce_value = CustomValue[x][y];
9958   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9959   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9960   boolean add_player_onto_element = (new_element_is_player &&
9961                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9962                                      IS_WALKABLE(old_element));
9963
9964   if (!add_player_onto_element)
9965   {
9966     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9967       RemoveMovingField(x, y);
9968     else
9969       RemoveField(x, y);
9970
9971     Feld[x][y] = new_element;
9972
9973     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9974       MovDir[x][y] = previous_move_direction;
9975
9976     if (element_info[new_element].use_last_ce_value)
9977       CustomValue[x][y] = last_ce_value;
9978
9979     InitField_WithBug1(x, y, FALSE);
9980
9981     new_element = Feld[x][y];   /* element may have changed */
9982
9983     ResetGfxAnimation(x, y);
9984     ResetRandomAnimationValue(x, y);
9985
9986     TEST_DrawLevelField(x, y);
9987
9988     if (GFX_CRUMBLED(new_element))
9989       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9990   }
9991
9992   /* check if element under the player changes from accessible to unaccessible
9993      (needed for special case of dropping element which then changes) */
9994   /* (must be checked after creating new element for walkable group elements) */
9995   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9996       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9997   {
9998     Bang(x, y);
9999
10000     return;
10001   }
10002
10003   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10004   if (new_element_is_player)
10005     RelocatePlayer(x, y, new_element);
10006
10007   if (is_change)
10008     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10009
10010   TestIfBadThingTouchesPlayer(x, y);
10011   TestIfPlayerTouchesCustomElement(x, y);
10012   TestIfElementTouchesCustomElement(x, y);
10013 }
10014
10015 static void CreateField(int x, int y, int element)
10016 {
10017   CreateFieldExt(x, y, element, FALSE);
10018 }
10019
10020 static void CreateElementFromChange(int x, int y, int element)
10021 {
10022   element = GET_VALID_RUNTIME_ELEMENT(element);
10023
10024   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10025   {
10026     int old_element = Feld[x][y];
10027
10028     /* prevent changed element from moving in same engine frame
10029        unless both old and new element can either fall or move */
10030     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10031         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10032       Stop[x][y] = TRUE;
10033   }
10034
10035   CreateFieldExt(x, y, element, TRUE);
10036 }
10037
10038 static boolean ChangeElement(int x, int y, int element, int page)
10039 {
10040   struct ElementInfo *ei = &element_info[element];
10041   struct ElementChangeInfo *change = &ei->change_page[page];
10042   int ce_value = CustomValue[x][y];
10043   int ce_score = ei->collect_score;
10044   int target_element;
10045   int old_element = Feld[x][y];
10046
10047   /* always use default change event to prevent running into a loop */
10048   if (ChangeEvent[x][y] == -1)
10049     ChangeEvent[x][y] = CE_DELAY;
10050
10051   if (ChangeEvent[x][y] == CE_DELAY)
10052   {
10053     /* reset actual trigger element, trigger player and action element */
10054     change->actual_trigger_element = EL_EMPTY;
10055     change->actual_trigger_player = EL_EMPTY;
10056     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10057     change->actual_trigger_side = CH_SIDE_NONE;
10058     change->actual_trigger_ce_value = 0;
10059     change->actual_trigger_ce_score = 0;
10060   }
10061
10062   /* do not change elements more than a specified maximum number of changes */
10063   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10064     return FALSE;
10065
10066   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10067
10068   if (change->explode)
10069   {
10070     Bang(x, y);
10071
10072     return TRUE;
10073   }
10074
10075   if (change->use_target_content)
10076   {
10077     boolean complete_replace = TRUE;
10078     boolean can_replace[3][3];
10079     int xx, yy;
10080
10081     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10082     {
10083       boolean is_empty;
10084       boolean is_walkable;
10085       boolean is_diggable;
10086       boolean is_collectible;
10087       boolean is_removable;
10088       boolean is_destructible;
10089       int ex = x + xx - 1;
10090       int ey = y + yy - 1;
10091       int content_element = change->target_content.e[xx][yy];
10092       int e;
10093
10094       can_replace[xx][yy] = TRUE;
10095
10096       if (ex == x && ey == y)   /* do not check changing element itself */
10097         continue;
10098
10099       if (content_element == EL_EMPTY_SPACE)
10100       {
10101         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10102
10103         continue;
10104       }
10105
10106       if (!IN_LEV_FIELD(ex, ey))
10107       {
10108         can_replace[xx][yy] = FALSE;
10109         complete_replace = FALSE;
10110
10111         continue;
10112       }
10113
10114       e = Feld[ex][ey];
10115
10116       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10117         e = MovingOrBlocked2Element(ex, ey);
10118
10119       is_empty = (IS_FREE(ex, ey) ||
10120                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10121
10122       is_walkable     = (is_empty || IS_WALKABLE(e));
10123       is_diggable     = (is_empty || IS_DIGGABLE(e));
10124       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10125       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10126       is_removable    = (is_diggable || is_collectible);
10127
10128       can_replace[xx][yy] =
10129         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10130           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10131           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10132           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10133           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10134           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10135          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10136
10137       if (!can_replace[xx][yy])
10138         complete_replace = FALSE;
10139     }
10140
10141     if (!change->only_if_complete || complete_replace)
10142     {
10143       boolean something_has_changed = FALSE;
10144
10145       if (change->only_if_complete && change->use_random_replace &&
10146           RND(100) < change->random_percentage)
10147         return FALSE;
10148
10149       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10150       {
10151         int ex = x + xx - 1;
10152         int ey = y + yy - 1;
10153         int content_element;
10154
10155         if (can_replace[xx][yy] && (!change->use_random_replace ||
10156                                     RND(100) < change->random_percentage))
10157         {
10158           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10159             RemoveMovingField(ex, ey);
10160
10161           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10162
10163           content_element = change->target_content.e[xx][yy];
10164           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10165                                               ce_value, ce_score);
10166
10167           CreateElementFromChange(ex, ey, target_element);
10168
10169           something_has_changed = TRUE;
10170
10171           /* for symmetry reasons, freeze newly created border elements */
10172           if (ex != x || ey != y)
10173             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10174         }
10175       }
10176
10177       if (something_has_changed)
10178       {
10179         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10180         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10181       }
10182     }
10183   }
10184   else
10185   {
10186     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10187                                         ce_value, ce_score);
10188
10189     if (element == EL_DIAGONAL_GROWING ||
10190         element == EL_DIAGONAL_SHRINKING)
10191     {
10192       target_element = Store[x][y];
10193
10194       Store[x][y] = EL_EMPTY;
10195     }
10196
10197     CreateElementFromChange(x, y, target_element);
10198
10199     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10200     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10201   }
10202
10203   /* this uses direct change before indirect change */
10204   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10205
10206   return TRUE;
10207 }
10208
10209 static void HandleElementChange(int x, int y, int page)
10210 {
10211   int element = MovingOrBlocked2Element(x, y);
10212   struct ElementInfo *ei = &element_info[element];
10213   struct ElementChangeInfo *change = &ei->change_page[page];
10214   boolean handle_action_before_change = FALSE;
10215
10216 #ifdef DEBUG
10217   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10218       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10219   {
10220     printf("\n\n");
10221     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10222            x, y, element, element_info[element].token_name);
10223     printf("HandleElementChange(): This should never happen!\n");
10224     printf("\n\n");
10225   }
10226 #endif
10227
10228   /* this can happen with classic bombs on walkable, changing elements */
10229   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10230   {
10231     return;
10232   }
10233
10234   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10235   {
10236     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10237
10238     if (change->can_change)
10239     {
10240       /* !!! not clear why graphic animation should be reset at all here !!! */
10241       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10242       /* when a custom element is about to change (for example by change delay),
10243          do not reset graphic animation when the custom element is moving */
10244       if (!IS_MOVING(x, y))
10245       {
10246         ResetGfxAnimation(x, y);
10247         ResetRandomAnimationValue(x, y);
10248       }
10249
10250       if (change->pre_change_function)
10251         change->pre_change_function(x, y);
10252     }
10253   }
10254
10255   ChangeDelay[x][y]--;
10256
10257   if (ChangeDelay[x][y] != 0)           /* continue element change */
10258   {
10259     if (change->can_change)
10260     {
10261       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10262
10263       if (IS_ANIMATED(graphic))
10264         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10265
10266       if (change->change_function)
10267         change->change_function(x, y);
10268     }
10269   }
10270   else                                  /* finish element change */
10271   {
10272     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10273     {
10274       page = ChangePage[x][y];
10275       ChangePage[x][y] = -1;
10276
10277       change = &ei->change_page[page];
10278     }
10279
10280     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10281     {
10282       ChangeDelay[x][y] = 1;            /* try change after next move step */
10283       ChangePage[x][y] = page;          /* remember page to use for change */
10284
10285       return;
10286     }
10287
10288     /* special case: set new level random seed before changing element */
10289     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10290       handle_action_before_change = TRUE;
10291
10292     if (change->has_action && handle_action_before_change)
10293       ExecuteCustomElementAction(x, y, element, page);
10294
10295     if (change->can_change)
10296     {
10297       if (ChangeElement(x, y, element, page))
10298       {
10299         if (change->post_change_function)
10300           change->post_change_function(x, y);
10301       }
10302     }
10303
10304     if (change->has_action && !handle_action_before_change)
10305       ExecuteCustomElementAction(x, y, element, page);
10306   }
10307 }
10308
10309 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10310                                               int trigger_element,
10311                                               int trigger_event,
10312                                               int trigger_player,
10313                                               int trigger_side,
10314                                               int trigger_page)
10315 {
10316   boolean change_done_any = FALSE;
10317   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10318   int i;
10319
10320   if (!(trigger_events[trigger_element][trigger_event]))
10321     return FALSE;
10322
10323   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10324
10325   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10326   {
10327     int element = EL_CUSTOM_START + i;
10328     boolean change_done = FALSE;
10329     int p;
10330
10331     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10332         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10333       continue;
10334
10335     for (p = 0; p < element_info[element].num_change_pages; p++)
10336     {
10337       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10338
10339       if (change->can_change_or_has_action &&
10340           change->has_event[trigger_event] &&
10341           change->trigger_side & trigger_side &&
10342           change->trigger_player & trigger_player &&
10343           change->trigger_page & trigger_page_bits &&
10344           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10345       {
10346         change->actual_trigger_element = trigger_element;
10347         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10348         change->actual_trigger_player_bits = trigger_player;
10349         change->actual_trigger_side = trigger_side;
10350         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10351         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10352
10353         if ((change->can_change && !change_done) || change->has_action)
10354         {
10355           int x, y;
10356
10357           SCAN_PLAYFIELD(x, y)
10358           {
10359             if (Feld[x][y] == element)
10360             {
10361               if (change->can_change && !change_done)
10362               {
10363                 /* if element already changed in this frame, not only prevent
10364                    another element change (checked in ChangeElement()), but
10365                    also prevent additional element actions for this element */
10366
10367                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10368                     !level.use_action_after_change_bug)
10369                   continue;
10370
10371                 ChangeDelay[x][y] = 1;
10372                 ChangeEvent[x][y] = trigger_event;
10373
10374                 HandleElementChange(x, y, p);
10375               }
10376               else if (change->has_action)
10377               {
10378                 /* if element already changed in this frame, not only prevent
10379                    another element change (checked in ChangeElement()), but
10380                    also prevent additional element actions for this element */
10381
10382                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10383                     !level.use_action_after_change_bug)
10384                   continue;
10385
10386                 ExecuteCustomElementAction(x, y, element, p);
10387                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10388               }
10389             }
10390           }
10391
10392           if (change->can_change)
10393           {
10394             change_done = TRUE;
10395             change_done_any = TRUE;
10396           }
10397         }
10398       }
10399     }
10400   }
10401
10402   RECURSION_LOOP_DETECTION_END();
10403
10404   return change_done_any;
10405 }
10406
10407 static boolean CheckElementChangeExt(int x, int y,
10408                                      int element,
10409                                      int trigger_element,
10410                                      int trigger_event,
10411                                      int trigger_player,
10412                                      int trigger_side)
10413 {
10414   boolean change_done = FALSE;
10415   int p;
10416
10417   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10418       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10419     return FALSE;
10420
10421   if (Feld[x][y] == EL_BLOCKED)
10422   {
10423     Blocked2Moving(x, y, &x, &y);
10424     element = Feld[x][y];
10425   }
10426
10427   /* check if element has already changed or is about to change after moving */
10428   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10429        Feld[x][y] != element) ||
10430
10431       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10432        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10433         ChangePage[x][y] != -1)))
10434     return FALSE;
10435
10436   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10437
10438   for (p = 0; p < element_info[element].num_change_pages; p++)
10439   {
10440     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10441
10442     /* check trigger element for all events where the element that is checked
10443        for changing interacts with a directly adjacent element -- this is
10444        different to element changes that affect other elements to change on the
10445        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10446     boolean check_trigger_element =
10447       (trigger_event == CE_TOUCHING_X ||
10448        trigger_event == CE_HITTING_X ||
10449        trigger_event == CE_HIT_BY_X ||
10450        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10451
10452     if (change->can_change_or_has_action &&
10453         change->has_event[trigger_event] &&
10454         change->trigger_side & trigger_side &&
10455         change->trigger_player & trigger_player &&
10456         (!check_trigger_element ||
10457          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10458     {
10459       change->actual_trigger_element = trigger_element;
10460       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10461       change->actual_trigger_player_bits = trigger_player;
10462       change->actual_trigger_side = trigger_side;
10463       change->actual_trigger_ce_value = CustomValue[x][y];
10464       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10465
10466       /* special case: trigger element not at (x,y) position for some events */
10467       if (check_trigger_element)
10468       {
10469         static struct
10470         {
10471           int dx, dy;
10472         } move_xy[] =
10473           {
10474             {  0,  0 },
10475             { -1,  0 },
10476             { +1,  0 },
10477             {  0,  0 },
10478             {  0, -1 },
10479             {  0,  0 }, { 0, 0 }, { 0, 0 },
10480             {  0, +1 }
10481           };
10482
10483         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10484         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10485
10486         change->actual_trigger_ce_value = CustomValue[xx][yy];
10487         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10488       }
10489
10490       if (change->can_change && !change_done)
10491       {
10492         ChangeDelay[x][y] = 1;
10493         ChangeEvent[x][y] = trigger_event;
10494
10495         HandleElementChange(x, y, p);
10496
10497         change_done = TRUE;
10498       }
10499       else if (change->has_action)
10500       {
10501         ExecuteCustomElementAction(x, y, element, p);
10502         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10503       }
10504     }
10505   }
10506
10507   RECURSION_LOOP_DETECTION_END();
10508
10509   return change_done;
10510 }
10511
10512 static void PlayPlayerSound(struct PlayerInfo *player)
10513 {
10514   int jx = player->jx, jy = player->jy;
10515   int sound_element = player->artwork_element;
10516   int last_action = player->last_action_waiting;
10517   int action = player->action_waiting;
10518
10519   if (player->is_waiting)
10520   {
10521     if (action != last_action)
10522       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10523     else
10524       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10525   }
10526   else
10527   {
10528     if (action != last_action)
10529       StopSound(element_info[sound_element].sound[last_action]);
10530
10531     if (last_action == ACTION_SLEEPING)
10532       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10533   }
10534 }
10535
10536 static void PlayAllPlayersSound()
10537 {
10538   int i;
10539
10540   for (i = 0; i < MAX_PLAYERS; i++)
10541     if (stored_player[i].active)
10542       PlayPlayerSound(&stored_player[i]);
10543 }
10544
10545 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10546 {
10547   boolean last_waiting = player->is_waiting;
10548   int move_dir = player->MovDir;
10549
10550   player->dir_waiting = move_dir;
10551   player->last_action_waiting = player->action_waiting;
10552
10553   if (is_waiting)
10554   {
10555     if (!last_waiting)          /* not waiting -> waiting */
10556     {
10557       player->is_waiting = TRUE;
10558
10559       player->frame_counter_bored =
10560         FrameCounter +
10561         game.player_boring_delay_fixed +
10562         GetSimpleRandom(game.player_boring_delay_random);
10563       player->frame_counter_sleeping =
10564         FrameCounter +
10565         game.player_sleeping_delay_fixed +
10566         GetSimpleRandom(game.player_sleeping_delay_random);
10567
10568       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10569     }
10570
10571     if (game.player_sleeping_delay_fixed +
10572         game.player_sleeping_delay_random > 0 &&
10573         player->anim_delay_counter == 0 &&
10574         player->post_delay_counter == 0 &&
10575         FrameCounter >= player->frame_counter_sleeping)
10576       player->is_sleeping = TRUE;
10577     else if (game.player_boring_delay_fixed +
10578              game.player_boring_delay_random > 0 &&
10579              FrameCounter >= player->frame_counter_bored)
10580       player->is_bored = TRUE;
10581
10582     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10583                               player->is_bored ? ACTION_BORING :
10584                               ACTION_WAITING);
10585
10586     if (player->is_sleeping && player->use_murphy)
10587     {
10588       /* special case for sleeping Murphy when leaning against non-free tile */
10589
10590       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10591           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10592            !IS_MOVING(player->jx - 1, player->jy)))
10593         move_dir = MV_LEFT;
10594       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10595                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10596                 !IS_MOVING(player->jx + 1, player->jy)))
10597         move_dir = MV_RIGHT;
10598       else
10599         player->is_sleeping = FALSE;
10600
10601       player->dir_waiting = move_dir;
10602     }
10603
10604     if (player->is_sleeping)
10605     {
10606       if (player->num_special_action_sleeping > 0)
10607       {
10608         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10609         {
10610           int last_special_action = player->special_action_sleeping;
10611           int num_special_action = player->num_special_action_sleeping;
10612           int special_action =
10613             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10614              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10615              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10616              last_special_action + 1 : ACTION_SLEEPING);
10617           int special_graphic =
10618             el_act_dir2img(player->artwork_element, special_action, move_dir);
10619
10620           player->anim_delay_counter =
10621             graphic_info[special_graphic].anim_delay_fixed +
10622             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10623           player->post_delay_counter =
10624             graphic_info[special_graphic].post_delay_fixed +
10625             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10626
10627           player->special_action_sleeping = special_action;
10628         }
10629
10630         if (player->anim_delay_counter > 0)
10631         {
10632           player->action_waiting = player->special_action_sleeping;
10633           player->anim_delay_counter--;
10634         }
10635         else if (player->post_delay_counter > 0)
10636         {
10637           player->post_delay_counter--;
10638         }
10639       }
10640     }
10641     else if (player->is_bored)
10642     {
10643       if (player->num_special_action_bored > 0)
10644       {
10645         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10646         {
10647           int special_action =
10648             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10649           int special_graphic =
10650             el_act_dir2img(player->artwork_element, special_action, move_dir);
10651
10652           player->anim_delay_counter =
10653             graphic_info[special_graphic].anim_delay_fixed +
10654             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10655           player->post_delay_counter =
10656             graphic_info[special_graphic].post_delay_fixed +
10657             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10658
10659           player->special_action_bored = special_action;
10660         }
10661
10662         if (player->anim_delay_counter > 0)
10663         {
10664           player->action_waiting = player->special_action_bored;
10665           player->anim_delay_counter--;
10666         }
10667         else if (player->post_delay_counter > 0)
10668         {
10669           player->post_delay_counter--;
10670         }
10671       }
10672     }
10673   }
10674   else if (last_waiting)        /* waiting -> not waiting */
10675   {
10676     player->is_waiting = FALSE;
10677     player->is_bored = FALSE;
10678     player->is_sleeping = FALSE;
10679
10680     player->frame_counter_bored = -1;
10681     player->frame_counter_sleeping = -1;
10682
10683     player->anim_delay_counter = 0;
10684     player->post_delay_counter = 0;
10685
10686     player->dir_waiting = player->MovDir;
10687     player->action_waiting = ACTION_DEFAULT;
10688
10689     player->special_action_bored = ACTION_DEFAULT;
10690     player->special_action_sleeping = ACTION_DEFAULT;
10691   }
10692 }
10693
10694 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10695 {
10696   static boolean player_was_moving = FALSE;
10697   static boolean player_was_snapping = FALSE;
10698   static boolean player_was_dropping = FALSE;
10699
10700   if ((!player->is_moving  && player_was_moving) ||
10701       (player->MovPos == 0 && player_was_moving) ||
10702       (player->is_snapping && !player_was_snapping) ||
10703       (player->is_dropping && !player_was_dropping))
10704   {
10705     if (!SaveEngineSnapshotToList())
10706       return;
10707
10708     player_was_moving = FALSE;
10709     player_was_snapping = TRUE;
10710     player_was_dropping = TRUE;
10711   }
10712   else
10713   {
10714     if (player->is_moving)
10715       player_was_moving = TRUE;
10716
10717     if (!player->is_snapping)
10718       player_was_snapping = FALSE;
10719
10720     if (!player->is_dropping)
10721       player_was_dropping = FALSE;
10722   }
10723 }
10724
10725 static void CheckSingleStepMode(struct PlayerInfo *player)
10726 {
10727   if (tape.single_step && tape.recording && !tape.pausing)
10728   {
10729     /* as it is called "single step mode", just return to pause mode when the
10730        player stopped moving after one tile (or never starts moving at all) */
10731     if (!player->is_moving && !player->is_pushing)
10732     {
10733       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10734       SnapField(player, 0, 0);                  /* stop snapping */
10735     }
10736   }
10737
10738   CheckSaveEngineSnapshot(player);
10739 }
10740
10741 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10742 {
10743   int left      = player_action & JOY_LEFT;
10744   int right     = player_action & JOY_RIGHT;
10745   int up        = player_action & JOY_UP;
10746   int down      = player_action & JOY_DOWN;
10747   int button1   = player_action & JOY_BUTTON_1;
10748   int button2   = player_action & JOY_BUTTON_2;
10749   int dx        = (left ? -1 : right ? 1 : 0);
10750   int dy        = (up   ? -1 : down  ? 1 : 0);
10751
10752   if (!player->active || tape.pausing)
10753     return 0;
10754
10755   if (player_action)
10756   {
10757     if (button1)
10758       SnapField(player, dx, dy);
10759     else
10760     {
10761       if (button2)
10762         DropElement(player);
10763
10764       MovePlayer(player, dx, dy);
10765     }
10766
10767     CheckSingleStepMode(player);
10768
10769     SetPlayerWaiting(player, FALSE);
10770
10771     return player_action;
10772   }
10773   else
10774   {
10775     /* no actions for this player (no input at player's configured device) */
10776
10777     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10778     SnapField(player, 0, 0);
10779     CheckGravityMovementWhenNotMoving(player);
10780
10781     if (player->MovPos == 0)
10782       SetPlayerWaiting(player, TRUE);
10783
10784     if (player->MovPos == 0)    /* needed for tape.playing */
10785       player->is_moving = FALSE;
10786
10787     player->is_dropping = FALSE;
10788     player->is_dropping_pressed = FALSE;
10789     player->drop_pressed_delay = 0;
10790
10791     CheckSingleStepMode(player);
10792
10793     return 0;
10794   }
10795 }
10796
10797 static void CheckLevelTime()
10798 {
10799   int i;
10800
10801   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10802   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10803   {
10804     if (level.native_em_level->lev->home == 0)  /* all players at home */
10805     {
10806       PlayerWins(local_player);
10807
10808       AllPlayersGone = TRUE;
10809
10810       level.native_em_level->lev->home = -1;
10811     }
10812
10813     if (level.native_em_level->ply[0]->alive == 0 &&
10814         level.native_em_level->ply[1]->alive == 0 &&
10815         level.native_em_level->ply[2]->alive == 0 &&
10816         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10817       AllPlayersGone = TRUE;
10818   }
10819   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10820   {
10821     if (game_sp.LevelSolved &&
10822         !game_sp.GameOver)                              /* game won */
10823     {
10824       PlayerWins(local_player);
10825
10826       game_sp.GameOver = TRUE;
10827
10828       AllPlayersGone = TRUE;
10829     }
10830
10831     if (game_sp.GameOver)                               /* game lost */
10832       AllPlayersGone = TRUE;
10833   }
10834
10835   if (TimeFrames >= FRAMES_PER_SECOND)
10836   {
10837     TimeFrames = 0;
10838     TapeTime++;
10839
10840     for (i = 0; i < MAX_PLAYERS; i++)
10841     {
10842       struct PlayerInfo *player = &stored_player[i];
10843
10844       if (SHIELD_ON(player))
10845       {
10846         player->shield_normal_time_left--;
10847
10848         if (player->shield_deadly_time_left > 0)
10849           player->shield_deadly_time_left--;
10850       }
10851     }
10852
10853     if (!local_player->LevelSolved && !level.use_step_counter)
10854     {
10855       TimePlayed++;
10856
10857       if (TimeLeft > 0)
10858       {
10859         TimeLeft--;
10860
10861         if (TimeLeft <= 10 && setup.time_limit)
10862           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10863
10864         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10865            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10866
10867         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10868
10869         if (!TimeLeft && setup.time_limit)
10870         {
10871           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10872             level.native_em_level->lev->killed_out_of_time = TRUE;
10873           else
10874             for (i = 0; i < MAX_PLAYERS; i++)
10875               KillPlayer(&stored_player[i]);
10876         }
10877       }
10878       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10879       {
10880         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10881       }
10882
10883       level.native_em_level->lev->time =
10884         (game.no_time_limit ? TimePlayed : TimeLeft);
10885     }
10886
10887     if (tape.recording || tape.playing)
10888       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10889   }
10890
10891   if (tape.recording || tape.playing)
10892     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10893
10894   UpdateAndDisplayGameControlValues();
10895 }
10896
10897 void AdvanceFrameAndPlayerCounters(int player_nr)
10898 {
10899   int i;
10900
10901   /* advance frame counters (global frame counter and time frame counter) */
10902   FrameCounter++;
10903   TimeFrames++;
10904
10905   /* advance player counters (counters for move delay, move animation etc.) */
10906   for (i = 0; i < MAX_PLAYERS; i++)
10907   {
10908     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10909     int move_delay_value = stored_player[i].move_delay_value;
10910     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10911
10912     if (!advance_player_counters)       /* not all players may be affected */
10913       continue;
10914
10915     if (move_frames == 0)       /* less than one move per game frame */
10916     {
10917       int stepsize = TILEX / move_delay_value;
10918       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10919       int count = (stored_player[i].is_moving ?
10920                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10921
10922       if (count % delay == 0)
10923         move_frames = 1;
10924     }
10925
10926     stored_player[i].Frame += move_frames;
10927
10928     if (stored_player[i].MovPos != 0)
10929       stored_player[i].StepFrame += move_frames;
10930
10931     if (stored_player[i].move_delay > 0)
10932       stored_player[i].move_delay--;
10933
10934     /* due to bugs in previous versions, counter must count up, not down */
10935     if (stored_player[i].push_delay != -1)
10936       stored_player[i].push_delay++;
10937
10938     if (stored_player[i].drop_delay > 0)
10939       stored_player[i].drop_delay--;
10940
10941     if (stored_player[i].is_dropping_pressed)
10942       stored_player[i].drop_pressed_delay++;
10943   }
10944 }
10945
10946 void StartGameActions(boolean init_network_game, boolean record_tape,
10947                       int random_seed)
10948 {
10949   unsigned int new_random_seed = InitRND(random_seed);
10950
10951   if (record_tape)
10952     TapeStartRecording(new_random_seed);
10953
10954 #if defined(NETWORK_AVALIABLE)
10955   if (init_network_game)
10956   {
10957     SendToServer_StartPlaying();
10958
10959     return;
10960   }
10961 #endif
10962
10963   InitGame();
10964 }
10965
10966 void GameActions()
10967 {
10968   static unsigned int game_frame_delay = 0;
10969   unsigned int game_frame_delay_value;
10970   byte *recorded_player_action;
10971   byte summarized_player_action = 0;
10972   byte tape_action[MAX_PLAYERS];
10973   int i;
10974
10975   for (i = 0; i < MAX_PLAYERS; i++)
10976   {
10977     struct PlayerInfo *player = &stored_player[i];
10978
10979     // allow engine snapshot if movement attempt was stopped
10980     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10981         (player->action & KEY_MOTION) == 0)
10982       game.snapshot.changed_action = TRUE;
10983
10984     // allow engine snapshot in case of snapping/dropping attempt
10985     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10986         (player->action & KEY_BUTTON) != 0)
10987       game.snapshot.changed_action = TRUE;
10988
10989     game.snapshot.last_action[i] = player->action;
10990   }
10991
10992   /* detect endless loops, caused by custom element programming */
10993   if (recursion_loop_detected && recursion_loop_depth == 0)
10994   {
10995     char *message = getStringCat3("Internal Error! Element ",
10996                                   EL_NAME(recursion_loop_element),
10997                                   " caused endless loop! Quit the game?");
10998
10999     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11000           EL_NAME(recursion_loop_element));
11001
11002     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11003
11004     recursion_loop_detected = FALSE;    /* if game should be continued */
11005
11006     free(message);
11007
11008     return;
11009   }
11010
11011   if (game.restart_level)
11012     StartGameActions(options.network, setup.autorecord, level.random_seed);
11013
11014   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11015   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11016   {
11017     if (level.native_em_level->lev->home == 0)  /* all players at home */
11018     {
11019       PlayerWins(local_player);
11020
11021       AllPlayersGone = TRUE;
11022
11023       level.native_em_level->lev->home = -1;
11024     }
11025
11026     if (level.native_em_level->ply[0]->alive == 0 &&
11027         level.native_em_level->ply[1]->alive == 0 &&
11028         level.native_em_level->ply[2]->alive == 0 &&
11029         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11030       AllPlayersGone = TRUE;
11031   }
11032   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11033   {
11034     if (game_sp.LevelSolved &&
11035         !game_sp.GameOver)                              /* game won */
11036     {
11037       PlayerWins(local_player);
11038
11039       game_sp.GameOver = TRUE;
11040
11041       AllPlayersGone = TRUE;
11042     }
11043
11044     if (game_sp.GameOver)                               /* game lost */
11045       AllPlayersGone = TRUE;
11046   }
11047
11048   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11049     GameWon();
11050
11051   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11052     TapeStop();
11053
11054   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11055     return;
11056
11057   game_frame_delay_value =
11058     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11059
11060   if (tape.playing && tape.warp_forward && !tape.pausing)
11061     game_frame_delay_value = 0;
11062
11063 #if 0
11064   /* ---------- main game synchronization point ---------- */
11065
11066   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11067
11068   printf("::: skip == %d\n", skip);
11069
11070 #else
11071   /* ---------- main game synchronization point ---------- */
11072
11073   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11074 #endif
11075
11076   if (network_playing && !network_player_action_received)
11077   {
11078     /* try to get network player actions in time */
11079
11080 #if defined(NETWORK_AVALIABLE)
11081     /* last chance to get network player actions without main loop delay */
11082     HandleNetworking();
11083 #endif
11084
11085     /* game was quit by network peer */
11086     if (game_status != GAME_MODE_PLAYING)
11087       return;
11088
11089     if (!network_player_action_received)
11090       return;           /* failed to get network player actions in time */
11091
11092     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11093   }
11094
11095   if (tape.pausing)
11096     return;
11097
11098   /* at this point we know that we really continue executing the game */
11099
11100   network_player_action_received = FALSE;
11101
11102   /* when playing tape, read previously recorded player input from tape data */
11103   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11104
11105   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11106   if (tape.pausing)
11107     return;
11108
11109   if (tape.set_centered_player)
11110   {
11111     game.centered_player_nr_next = tape.centered_player_nr_next;
11112     game.set_centered_player = TRUE;
11113   }
11114
11115   for (i = 0; i < MAX_PLAYERS; i++)
11116   {
11117     summarized_player_action |= stored_player[i].action;
11118
11119     if (!network_playing && (game.team_mode || tape.playing))
11120       stored_player[i].effective_action = stored_player[i].action;
11121   }
11122
11123 #if defined(NETWORK_AVALIABLE)
11124   if (network_playing)
11125     SendToServer_MovePlayer(summarized_player_action);
11126 #endif
11127
11128   if (!options.network && !game.team_mode)
11129     local_player->effective_action = summarized_player_action;
11130
11131   if (tape.recording &&
11132       setup.team_mode &&
11133       setup.input_on_focus &&
11134       game.centered_player_nr != -1)
11135   {
11136     for (i = 0; i < MAX_PLAYERS; i++)
11137       stored_player[i].effective_action =
11138         (i == game.centered_player_nr ? summarized_player_action : 0);
11139   }
11140
11141   if (recorded_player_action != NULL)
11142     for (i = 0; i < MAX_PLAYERS; i++)
11143       stored_player[i].effective_action = recorded_player_action[i];
11144
11145   for (i = 0; i < MAX_PLAYERS; i++)
11146   {
11147     tape_action[i] = stored_player[i].effective_action;
11148
11149     /* (this may happen in the RND game engine if a player was not present on
11150        the playfield on level start, but appeared later from a custom element */
11151     if (setup.team_mode &&
11152         tape.recording &&
11153         tape_action[i] &&
11154         !tape.player_participates[i])
11155       tape.player_participates[i] = TRUE;
11156   }
11157
11158   /* only record actions from input devices, but not programmed actions */
11159   if (tape.recording)
11160     TapeRecordAction(tape_action);
11161
11162 #if USE_NEW_PLAYER_ASSIGNMENTS
11163   // !!! also map player actions in single player mode !!!
11164   // if (game.team_mode)
11165   {
11166     byte mapped_action[MAX_PLAYERS];
11167
11168 #if DEBUG_PLAYER_ACTIONS
11169     printf(":::");
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       printf(" %d, ", stored_player[i].effective_action);
11172 #endif
11173
11174     for (i = 0; i < MAX_PLAYERS; i++)
11175       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11176
11177     for (i = 0; i < MAX_PLAYERS; i++)
11178       stored_player[i].effective_action = mapped_action[i];
11179
11180 #if DEBUG_PLAYER_ACTIONS
11181     printf(" =>");
11182     for (i = 0; i < MAX_PLAYERS; i++)
11183       printf(" %d, ", stored_player[i].effective_action);
11184     printf("\n");
11185 #endif
11186   }
11187 #if DEBUG_PLAYER_ACTIONS
11188   else
11189   {
11190     printf(":::");
11191     for (i = 0; i < MAX_PLAYERS; i++)
11192       printf(" %d, ", stored_player[i].effective_action);
11193     printf("\n");
11194   }
11195 #endif
11196 #endif
11197
11198   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11199   {
11200     GameActions_EM_Main();
11201   }
11202   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11203   {
11204     GameActions_SP_Main();
11205   }
11206   else
11207   {
11208     GameActions_RND_Main();
11209   }
11210
11211   BlitScreenToBitmap(backbuffer);
11212
11213   CheckLevelTime();
11214
11215   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11216
11217   if (options.debug)                    /* calculate frames per second */
11218   {
11219     static unsigned int fps_counter = 0;
11220     static int fps_frames = 0;
11221     unsigned int fps_delay_ms = Counter() - fps_counter;
11222
11223     fps_frames++;
11224
11225     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11226     {
11227       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11228
11229       fps_frames = 0;
11230       fps_counter = Counter();
11231     }
11232
11233     redraw_mask |= REDRAW_FPS;
11234   }
11235 }
11236
11237 void GameActions_EM_Main()
11238 {
11239   byte effective_action[MAX_PLAYERS];
11240   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11241   int i;
11242
11243   for (i = 0; i < MAX_PLAYERS; i++)
11244     effective_action[i] = stored_player[i].effective_action;
11245
11246   GameActions_EM(effective_action, warp_mode);
11247 }
11248
11249 void GameActions_SP_Main()
11250 {
11251   byte effective_action[MAX_PLAYERS];
11252   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11253   int i;
11254
11255   for (i = 0; i < MAX_PLAYERS; i++)
11256     effective_action[i] = stored_player[i].effective_action;
11257
11258   GameActions_SP(effective_action, warp_mode);
11259 }
11260
11261 void GameActions_RND_Main()
11262 {
11263   GameActions_RND();
11264 }
11265
11266 void GameActions_RND()
11267 {
11268   int magic_wall_x = 0, magic_wall_y = 0;
11269   int i, x, y, element, graphic;
11270
11271   InitPlayfieldScanModeVars();
11272
11273   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11274   {
11275     SCAN_PLAYFIELD(x, y)
11276     {
11277       ChangeCount[x][y] = 0;
11278       ChangeEvent[x][y] = -1;
11279     }
11280   }
11281
11282   if (game.set_centered_player)
11283   {
11284     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11285
11286     /* switching to "all players" only possible if all players fit to screen */
11287     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11288     {
11289       game.centered_player_nr_next = game.centered_player_nr;
11290       game.set_centered_player = FALSE;
11291     }
11292
11293     /* do not switch focus to non-existing (or non-active) player */
11294     if (game.centered_player_nr_next >= 0 &&
11295         !stored_player[game.centered_player_nr_next].active)
11296     {
11297       game.centered_player_nr_next = game.centered_player_nr;
11298       game.set_centered_player = FALSE;
11299     }
11300   }
11301
11302   if (game.set_centered_player &&
11303       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11304   {
11305     int sx, sy;
11306
11307     if (game.centered_player_nr_next == -1)
11308     {
11309       setScreenCenteredToAllPlayers(&sx, &sy);
11310     }
11311     else
11312     {
11313       sx = stored_player[game.centered_player_nr_next].jx;
11314       sy = stored_player[game.centered_player_nr_next].jy;
11315     }
11316
11317     game.centered_player_nr = game.centered_player_nr_next;
11318     game.set_centered_player = FALSE;
11319
11320     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11321     DrawGameDoorValues();
11322   }
11323
11324   for (i = 0; i < MAX_PLAYERS; i++)
11325   {
11326     int actual_player_action = stored_player[i].effective_action;
11327
11328 #if 1
11329     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11330        - rnd_equinox_tetrachloride 048
11331        - rnd_equinox_tetrachloride_ii 096
11332        - rnd_emanuel_schmieg 002
11333        - doctor_sloan_ww 001, 020
11334     */
11335     if (stored_player[i].MovPos == 0)
11336       CheckGravityMovement(&stored_player[i]);
11337 #endif
11338
11339     /* overwrite programmed action with tape action */
11340     if (stored_player[i].programmed_action)
11341       actual_player_action = stored_player[i].programmed_action;
11342
11343     PlayerActions(&stored_player[i], actual_player_action);
11344
11345     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11346   }
11347
11348   ScrollScreen(NULL, SCROLL_GO_ON);
11349
11350   /* for backwards compatibility, the following code emulates a fixed bug that
11351      occured when pushing elements (causing elements that just made their last
11352      pushing step to already (if possible) make their first falling step in the
11353      same game frame, which is bad); this code is also needed to use the famous
11354      "spring push bug" which is used in older levels and might be wanted to be
11355      used also in newer levels, but in this case the buggy pushing code is only
11356      affecting the "spring" element and no other elements */
11357
11358   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11359   {
11360     for (i = 0; i < MAX_PLAYERS; i++)
11361     {
11362       struct PlayerInfo *player = &stored_player[i];
11363       int x = player->jx;
11364       int y = player->jy;
11365
11366       if (player->active && player->is_pushing && player->is_moving &&
11367           IS_MOVING(x, y) &&
11368           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11369            Feld[x][y] == EL_SPRING))
11370       {
11371         ContinueMoving(x, y);
11372
11373         /* continue moving after pushing (this is actually a bug) */
11374         if (!IS_MOVING(x, y))
11375           Stop[x][y] = FALSE;
11376       }
11377     }
11378   }
11379
11380   SCAN_PLAYFIELD(x, y)
11381   {
11382     ChangeCount[x][y] = 0;
11383     ChangeEvent[x][y] = -1;
11384
11385     /* this must be handled before main playfield loop */
11386     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11387     {
11388       MovDelay[x][y]--;
11389       if (MovDelay[x][y] <= 0)
11390         RemoveField(x, y);
11391     }
11392
11393     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11394     {
11395       MovDelay[x][y]--;
11396       if (MovDelay[x][y] <= 0)
11397       {
11398         RemoveField(x, y);
11399         TEST_DrawLevelField(x, y);
11400
11401         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11402       }
11403     }
11404
11405 #if DEBUG
11406     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11407     {
11408       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11409       printf("GameActions(): This should never happen!\n");
11410
11411       ChangePage[x][y] = -1;
11412     }
11413 #endif
11414
11415     Stop[x][y] = FALSE;
11416     if (WasJustMoving[x][y] > 0)
11417       WasJustMoving[x][y]--;
11418     if (WasJustFalling[x][y] > 0)
11419       WasJustFalling[x][y]--;
11420     if (CheckCollision[x][y] > 0)
11421       CheckCollision[x][y]--;
11422     if (CheckImpact[x][y] > 0)
11423       CheckImpact[x][y]--;
11424
11425     GfxFrame[x][y]++;
11426
11427     /* reset finished pushing action (not done in ContinueMoving() to allow
11428        continuous pushing animation for elements with zero push delay) */
11429     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11430     {
11431       ResetGfxAnimation(x, y);
11432       TEST_DrawLevelField(x, y);
11433     }
11434
11435 #if DEBUG
11436     if (IS_BLOCKED(x, y))
11437     {
11438       int oldx, oldy;
11439
11440       Blocked2Moving(x, y, &oldx, &oldy);
11441       if (!IS_MOVING(oldx, oldy))
11442       {
11443         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11444         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11445         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11446         printf("GameActions(): This should never happen!\n");
11447       }
11448     }
11449 #endif
11450   }
11451
11452   SCAN_PLAYFIELD(x, y)
11453   {
11454     element = Feld[x][y];
11455     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11456
11457     ResetGfxFrame(x, y, TRUE);
11458
11459     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11460         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11461       ResetRandomAnimationValue(x, y);
11462
11463     SetRandomAnimationValue(x, y);
11464
11465     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11466
11467     if (IS_INACTIVE(element))
11468     {
11469       if (IS_ANIMATED(graphic))
11470         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11471
11472       continue;
11473     }
11474
11475     /* this may take place after moving, so 'element' may have changed */
11476     if (IS_CHANGING(x, y) &&
11477         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11478     {
11479       int page = element_info[element].event_page_nr[CE_DELAY];
11480
11481       HandleElementChange(x, y, page);
11482
11483       element = Feld[x][y];
11484       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11485     }
11486
11487     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11488     {
11489       StartMoving(x, y);
11490
11491       element = Feld[x][y];
11492       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11493
11494       if (IS_ANIMATED(graphic) &&
11495           !IS_MOVING(x, y) &&
11496           !Stop[x][y])
11497         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11498
11499       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11500         TEST_DrawTwinkleOnField(x, y);
11501     }
11502     else if ((element == EL_ACID ||
11503               element == EL_EXIT_OPEN ||
11504               element == EL_EM_EXIT_OPEN ||
11505               element == EL_SP_EXIT_OPEN ||
11506               element == EL_STEEL_EXIT_OPEN ||
11507               element == EL_EM_STEEL_EXIT_OPEN ||
11508               element == EL_SP_TERMINAL ||
11509               element == EL_SP_TERMINAL_ACTIVE ||
11510               element == EL_EXTRA_TIME ||
11511               element == EL_SHIELD_NORMAL ||
11512               element == EL_SHIELD_DEADLY) &&
11513              IS_ANIMATED(graphic))
11514       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11515     else if (IS_MOVING(x, y))
11516       ContinueMoving(x, y);
11517     else if (IS_ACTIVE_BOMB(element))
11518       CheckDynamite(x, y);
11519     else if (element == EL_AMOEBA_GROWING)
11520       AmoebeWaechst(x, y);
11521     else if (element == EL_AMOEBA_SHRINKING)
11522       AmoebaDisappearing(x, y);
11523
11524 #if !USE_NEW_AMOEBA_CODE
11525     else if (IS_AMOEBALIVE(element))
11526       AmoebeAbleger(x, y);
11527 #endif
11528
11529     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11530       Life(x, y);
11531     else if (element == EL_EXIT_CLOSED)
11532       CheckExit(x, y);
11533     else if (element == EL_EM_EXIT_CLOSED)
11534       CheckExitEM(x, y);
11535     else if (element == EL_STEEL_EXIT_CLOSED)
11536       CheckExitSteel(x, y);
11537     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11538       CheckExitSteelEM(x, y);
11539     else if (element == EL_SP_EXIT_CLOSED)
11540       CheckExitSP(x, y);
11541     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11542              element == EL_EXPANDABLE_STEELWALL_GROWING)
11543       MauerWaechst(x, y);
11544     else if (element == EL_EXPANDABLE_WALL ||
11545              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11546              element == EL_EXPANDABLE_WALL_VERTICAL ||
11547              element == EL_EXPANDABLE_WALL_ANY ||
11548              element == EL_BD_EXPANDABLE_WALL)
11549       MauerAbleger(x, y);
11550     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11551              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11552              element == EL_EXPANDABLE_STEELWALL_ANY)
11553       MauerAblegerStahl(x, y);
11554     else if (element == EL_FLAMES)
11555       CheckForDragon(x, y);
11556     else if (element == EL_EXPLOSION)
11557       ; /* drawing of correct explosion animation is handled separately */
11558     else if (element == EL_ELEMENT_SNAPPING ||
11559              element == EL_DIAGONAL_SHRINKING ||
11560              element == EL_DIAGONAL_GROWING)
11561     {
11562       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11563
11564       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11565     }
11566     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11567       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11568
11569     if (IS_BELT_ACTIVE(element))
11570       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11571
11572     if (game.magic_wall_active)
11573     {
11574       int jx = local_player->jx, jy = local_player->jy;
11575
11576       /* play the element sound at the position nearest to the player */
11577       if ((element == EL_MAGIC_WALL_FULL ||
11578            element == EL_MAGIC_WALL_ACTIVE ||
11579            element == EL_MAGIC_WALL_EMPTYING ||
11580            element == EL_BD_MAGIC_WALL_FULL ||
11581            element == EL_BD_MAGIC_WALL_ACTIVE ||
11582            element == EL_BD_MAGIC_WALL_EMPTYING ||
11583            element == EL_DC_MAGIC_WALL_FULL ||
11584            element == EL_DC_MAGIC_WALL_ACTIVE ||
11585            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11586           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11587       {
11588         magic_wall_x = x;
11589         magic_wall_y = y;
11590       }
11591     }
11592   }
11593
11594 #if USE_NEW_AMOEBA_CODE
11595   /* new experimental amoeba growth stuff */
11596   if (!(FrameCounter % 8))
11597   {
11598     static unsigned int random = 1684108901;
11599
11600     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11601     {
11602       x = RND(lev_fieldx);
11603       y = RND(lev_fieldy);
11604       element = Feld[x][y];
11605
11606       if (!IS_PLAYER(x,y) &&
11607           (element == EL_EMPTY ||
11608            CAN_GROW_INTO(element) ||
11609            element == EL_QUICKSAND_EMPTY ||
11610            element == EL_QUICKSAND_FAST_EMPTY ||
11611            element == EL_ACID_SPLASH_LEFT ||
11612            element == EL_ACID_SPLASH_RIGHT))
11613       {
11614         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11615             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11616             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11617             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11618           Feld[x][y] = EL_AMOEBA_DROP;
11619       }
11620
11621       random = random * 129 + 1;
11622     }
11623   }
11624 #endif
11625
11626   game.explosions_delayed = FALSE;
11627
11628   SCAN_PLAYFIELD(x, y)
11629   {
11630     element = Feld[x][y];
11631
11632     if (ExplodeField[x][y])
11633       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11634     else if (element == EL_EXPLOSION)
11635       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11636
11637     ExplodeField[x][y] = EX_TYPE_NONE;
11638   }
11639
11640   game.explosions_delayed = TRUE;
11641
11642   if (game.magic_wall_active)
11643   {
11644     if (!(game.magic_wall_time_left % 4))
11645     {
11646       int element = Feld[magic_wall_x][magic_wall_y];
11647
11648       if (element == EL_BD_MAGIC_WALL_FULL ||
11649           element == EL_BD_MAGIC_WALL_ACTIVE ||
11650           element == EL_BD_MAGIC_WALL_EMPTYING)
11651         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11652       else if (element == EL_DC_MAGIC_WALL_FULL ||
11653                element == EL_DC_MAGIC_WALL_ACTIVE ||
11654                element == EL_DC_MAGIC_WALL_EMPTYING)
11655         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11656       else
11657         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11658     }
11659
11660     if (game.magic_wall_time_left > 0)
11661     {
11662       game.magic_wall_time_left--;
11663
11664       if (!game.magic_wall_time_left)
11665       {
11666         SCAN_PLAYFIELD(x, y)
11667         {
11668           element = Feld[x][y];
11669
11670           if (element == EL_MAGIC_WALL_ACTIVE ||
11671               element == EL_MAGIC_WALL_FULL)
11672           {
11673             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11674             TEST_DrawLevelField(x, y);
11675           }
11676           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11677                    element == EL_BD_MAGIC_WALL_FULL)
11678           {
11679             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11680             TEST_DrawLevelField(x, y);
11681           }
11682           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11683                    element == EL_DC_MAGIC_WALL_FULL)
11684           {
11685             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11686             TEST_DrawLevelField(x, y);
11687           }
11688         }
11689
11690         game.magic_wall_active = FALSE;
11691       }
11692     }
11693   }
11694
11695   if (game.light_time_left > 0)
11696   {
11697     game.light_time_left--;
11698
11699     if (game.light_time_left == 0)
11700       RedrawAllLightSwitchesAndInvisibleElements();
11701   }
11702
11703   if (game.timegate_time_left > 0)
11704   {
11705     game.timegate_time_left--;
11706
11707     if (game.timegate_time_left == 0)
11708       CloseAllOpenTimegates();
11709   }
11710
11711   if (game.lenses_time_left > 0)
11712   {
11713     game.lenses_time_left--;
11714
11715     if (game.lenses_time_left == 0)
11716       RedrawAllInvisibleElementsForLenses();
11717   }
11718
11719   if (game.magnify_time_left > 0)
11720   {
11721     game.magnify_time_left--;
11722
11723     if (game.magnify_time_left == 0)
11724       RedrawAllInvisibleElementsForMagnifier();
11725   }
11726
11727   for (i = 0; i < MAX_PLAYERS; i++)
11728   {
11729     struct PlayerInfo *player = &stored_player[i];
11730
11731     if (SHIELD_ON(player))
11732     {
11733       if (player->shield_deadly_time_left)
11734         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11735       else if (player->shield_normal_time_left)
11736         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11737     }
11738   }
11739
11740 #if USE_DELAYED_GFX_REDRAW
11741   SCAN_PLAYFIELD(x, y)
11742   {
11743     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11744     {
11745       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11746          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11747
11748       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11749         DrawLevelField(x, y);
11750
11751       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11752         DrawLevelFieldCrumbled(x, y);
11753
11754       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11755         DrawLevelFieldCrumbledNeighbours(x, y);
11756
11757       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11758         DrawTwinkleOnField(x, y);
11759     }
11760
11761     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11762   }
11763 #endif
11764
11765   DrawAllPlayers();
11766   PlayAllPlayersSound();
11767
11768   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11769   {
11770     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11771
11772     local_player->show_envelope = 0;
11773   }
11774
11775   /* use random number generator in every frame to make it less predictable */
11776   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11777     RND(1);
11778 }
11779
11780 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11781 {
11782   int min_x = x, min_y = y, max_x = x, max_y = y;
11783   int i;
11784
11785   for (i = 0; i < MAX_PLAYERS; i++)
11786   {
11787     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11788
11789     if (!stored_player[i].active || &stored_player[i] == player)
11790       continue;
11791
11792     min_x = MIN(min_x, jx);
11793     min_y = MIN(min_y, jy);
11794     max_x = MAX(max_x, jx);
11795     max_y = MAX(max_y, jy);
11796   }
11797
11798   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11799 }
11800
11801 static boolean AllPlayersInVisibleScreen()
11802 {
11803   int i;
11804
11805   for (i = 0; i < MAX_PLAYERS; i++)
11806   {
11807     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11808
11809     if (!stored_player[i].active)
11810       continue;
11811
11812     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11813       return FALSE;
11814   }
11815
11816   return TRUE;
11817 }
11818
11819 void ScrollLevel(int dx, int dy)
11820 {
11821   int scroll_offset = 2 * TILEX_VAR;
11822   int x, y;
11823
11824   BlitBitmap(drawto_field, drawto_field,
11825              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11826              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11827              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11828              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11829              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11830              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11831
11832   if (dx != 0)
11833   {
11834     x = (dx == 1 ? BX1 : BX2);
11835     for (y = BY1; y <= BY2; y++)
11836       DrawScreenField(x, y);
11837   }
11838
11839   if (dy != 0)
11840   {
11841     y = (dy == 1 ? BY1 : BY2);
11842     for (x = BX1; x <= BX2; x++)
11843       DrawScreenField(x, y);
11844   }
11845
11846   redraw_mask |= REDRAW_FIELD;
11847 }
11848
11849 static boolean canFallDown(struct PlayerInfo *player)
11850 {
11851   int jx = player->jx, jy = player->jy;
11852
11853   return (IN_LEV_FIELD(jx, jy + 1) &&
11854           (IS_FREE(jx, jy + 1) ||
11855            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11856           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11857           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11858 }
11859
11860 static boolean canPassField(int x, int y, int move_dir)
11861 {
11862   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11863   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11864   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11865   int nextx = x + dx;
11866   int nexty = y + dy;
11867   int element = Feld[x][y];
11868
11869   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11870           !CAN_MOVE(element) &&
11871           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11872           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11873           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11874 }
11875
11876 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11877 {
11878   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11879   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11880   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11881   int newx = x + dx;
11882   int newy = y + dy;
11883
11884   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11885           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11886           (IS_DIGGABLE(Feld[newx][newy]) ||
11887            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11888            canPassField(newx, newy, move_dir)));
11889 }
11890
11891 static void CheckGravityMovement(struct PlayerInfo *player)
11892 {
11893   if (player->gravity && !player->programmed_action)
11894   {
11895     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11896     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11897     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11898     int jx = player->jx, jy = player->jy;
11899     boolean player_is_moving_to_valid_field =
11900       (!player_is_snapping &&
11901        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11902         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11903     boolean player_can_fall_down = canFallDown(player);
11904
11905     if (player_can_fall_down &&
11906         !player_is_moving_to_valid_field)
11907       player->programmed_action = MV_DOWN;
11908   }
11909 }
11910
11911 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11912 {
11913   return CheckGravityMovement(player);
11914
11915   if (player->gravity && !player->programmed_action)
11916   {
11917     int jx = player->jx, jy = player->jy;
11918     boolean field_under_player_is_free =
11919       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11920     boolean player_is_standing_on_valid_field =
11921       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11922        (IS_WALKABLE(Feld[jx][jy]) &&
11923         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11924
11925     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11926       player->programmed_action = MV_DOWN;
11927   }
11928 }
11929
11930 /*
11931   MovePlayerOneStep()
11932   -----------------------------------------------------------------------------
11933   dx, dy:               direction (non-diagonal) to try to move the player to
11934   real_dx, real_dy:     direction as read from input device (can be diagonal)
11935 */
11936
11937 boolean MovePlayerOneStep(struct PlayerInfo *player,
11938                           int dx, int dy, int real_dx, int real_dy)
11939 {
11940   int jx = player->jx, jy = player->jy;
11941   int new_jx = jx + dx, new_jy = jy + dy;
11942   int can_move;
11943   boolean player_can_move = !player->cannot_move;
11944
11945   if (!player->active || (!dx && !dy))
11946     return MP_NO_ACTION;
11947
11948   player->MovDir = (dx < 0 ? MV_LEFT :
11949                     dx > 0 ? MV_RIGHT :
11950                     dy < 0 ? MV_UP :
11951                     dy > 0 ? MV_DOWN :  MV_NONE);
11952
11953   if (!IN_LEV_FIELD(new_jx, new_jy))
11954     return MP_NO_ACTION;
11955
11956   if (!player_can_move)
11957   {
11958     if (player->MovPos == 0)
11959     {
11960       player->is_moving = FALSE;
11961       player->is_digging = FALSE;
11962       player->is_collecting = FALSE;
11963       player->is_snapping = FALSE;
11964       player->is_pushing = FALSE;
11965     }
11966   }
11967
11968   if (!options.network && game.centered_player_nr == -1 &&
11969       !AllPlayersInSight(player, new_jx, new_jy))
11970     return MP_NO_ACTION;
11971
11972   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11973   if (can_move != MP_MOVING)
11974     return can_move;
11975
11976   /* check if DigField() has caused relocation of the player */
11977   if (player->jx != jx || player->jy != jy)
11978     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11979
11980   StorePlayer[jx][jy] = 0;
11981   player->last_jx = jx;
11982   player->last_jy = jy;
11983   player->jx = new_jx;
11984   player->jy = new_jy;
11985   StorePlayer[new_jx][new_jy] = player->element_nr;
11986
11987   if (player->move_delay_value_next != -1)
11988   {
11989     player->move_delay_value = player->move_delay_value_next;
11990     player->move_delay_value_next = -1;
11991   }
11992
11993   player->MovPos =
11994     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11995
11996   player->step_counter++;
11997
11998   PlayerVisit[jx][jy] = FrameCounter;
11999
12000   player->is_moving = TRUE;
12001
12002 #if 1
12003   /* should better be called in MovePlayer(), but this breaks some tapes */
12004   ScrollPlayer(player, SCROLL_INIT);
12005 #endif
12006
12007   return MP_MOVING;
12008 }
12009
12010 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12011 {
12012   int jx = player->jx, jy = player->jy;
12013   int old_jx = jx, old_jy = jy;
12014   int moved = MP_NO_ACTION;
12015
12016   if (!player->active)
12017     return FALSE;
12018
12019   if (!dx && !dy)
12020   {
12021     if (player->MovPos == 0)
12022     {
12023       player->is_moving = FALSE;
12024       player->is_digging = FALSE;
12025       player->is_collecting = FALSE;
12026       player->is_snapping = FALSE;
12027       player->is_pushing = FALSE;
12028     }
12029
12030     return FALSE;
12031   }
12032
12033   if (player->move_delay > 0)
12034     return FALSE;
12035
12036   player->move_delay = -1;              /* set to "uninitialized" value */
12037
12038   /* store if player is automatically moved to next field */
12039   player->is_auto_moving = (player->programmed_action != MV_NONE);
12040
12041   /* remove the last programmed player action */
12042   player->programmed_action = 0;
12043
12044   if (player->MovPos)
12045   {
12046     /* should only happen if pre-1.2 tape recordings are played */
12047     /* this is only for backward compatibility */
12048
12049     int original_move_delay_value = player->move_delay_value;
12050
12051 #if DEBUG
12052     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12053            tape.counter);
12054 #endif
12055
12056     /* scroll remaining steps with finest movement resolution */
12057     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12058
12059     while (player->MovPos)
12060     {
12061       ScrollPlayer(player, SCROLL_GO_ON);
12062       ScrollScreen(NULL, SCROLL_GO_ON);
12063
12064       AdvanceFrameAndPlayerCounters(player->index_nr);
12065
12066       DrawAllPlayers();
12067       BackToFront();
12068     }
12069
12070     player->move_delay_value = original_move_delay_value;
12071   }
12072
12073   player->is_active = FALSE;
12074
12075   if (player->last_move_dir & MV_HORIZONTAL)
12076   {
12077     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12078       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12079   }
12080   else
12081   {
12082     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12083       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12084   }
12085
12086   if (!moved && !player->is_active)
12087   {
12088     player->is_moving = FALSE;
12089     player->is_digging = FALSE;
12090     player->is_collecting = FALSE;
12091     player->is_snapping = FALSE;
12092     player->is_pushing = FALSE;
12093   }
12094
12095   jx = player->jx;
12096   jy = player->jy;
12097
12098   if (moved & MP_MOVING && !ScreenMovPos &&
12099       (player->index_nr == game.centered_player_nr ||
12100        game.centered_player_nr == -1))
12101   {
12102     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12103     int offset = game.scroll_delay_value;
12104
12105     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12106     {
12107       /* actual player has left the screen -- scroll in that direction */
12108       if (jx != old_jx)         /* player has moved horizontally */
12109         scroll_x += (jx - old_jx);
12110       else                      /* player has moved vertically */
12111         scroll_y += (jy - old_jy);
12112     }
12113     else
12114     {
12115       if (jx != old_jx)         /* player has moved horizontally */
12116       {
12117         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12118             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12119           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12120
12121         /* don't scroll over playfield boundaries */
12122         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12123           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12124
12125         /* don't scroll more than one field at a time */
12126         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12127
12128         /* don't scroll against the player's moving direction */
12129         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12130             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12131           scroll_x = old_scroll_x;
12132       }
12133       else                      /* player has moved vertically */
12134       {
12135         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12136             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12137           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12138
12139         /* don't scroll over playfield boundaries */
12140         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12141           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12142
12143         /* don't scroll more than one field at a time */
12144         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12145
12146         /* don't scroll against the player's moving direction */
12147         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12148             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12149           scroll_y = old_scroll_y;
12150       }
12151     }
12152
12153     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12154     {
12155       if (!options.network && game.centered_player_nr == -1 &&
12156           !AllPlayersInVisibleScreen())
12157       {
12158         scroll_x = old_scroll_x;
12159         scroll_y = old_scroll_y;
12160       }
12161       else
12162       {
12163         ScrollScreen(player, SCROLL_INIT);
12164         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12165       }
12166     }
12167   }
12168
12169   player->StepFrame = 0;
12170
12171   if (moved & MP_MOVING)
12172   {
12173     if (old_jx != jx && old_jy == jy)
12174       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12175     else if (old_jx == jx && old_jy != jy)
12176       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12177
12178     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12179
12180     player->last_move_dir = player->MovDir;
12181     player->is_moving = TRUE;
12182     player->is_snapping = FALSE;
12183     player->is_switching = FALSE;
12184     player->is_dropping = FALSE;
12185     player->is_dropping_pressed = FALSE;
12186     player->drop_pressed_delay = 0;
12187
12188 #if 0
12189     /* should better be called here than above, but this breaks some tapes */
12190     ScrollPlayer(player, SCROLL_INIT);
12191 #endif
12192   }
12193   else
12194   {
12195     CheckGravityMovementWhenNotMoving(player);
12196
12197     player->is_moving = FALSE;
12198
12199     /* at this point, the player is allowed to move, but cannot move right now
12200        (e.g. because of something blocking the way) -- ensure that the player
12201        is also allowed to move in the next frame (in old versions before 3.1.1,
12202        the player was forced to wait again for eight frames before next try) */
12203
12204     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12205       player->move_delay = 0;   /* allow direct movement in the next frame */
12206   }
12207
12208   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12209     player->move_delay = player->move_delay_value;
12210
12211   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12212   {
12213     TestIfPlayerTouchesBadThing(jx, jy);
12214     TestIfPlayerTouchesCustomElement(jx, jy);
12215   }
12216
12217   if (!player->active)
12218     RemovePlayer(player);
12219
12220   return moved;
12221 }
12222
12223 void ScrollPlayer(struct PlayerInfo *player, int mode)
12224 {
12225   int jx = player->jx, jy = player->jy;
12226   int last_jx = player->last_jx, last_jy = player->last_jy;
12227   int move_stepsize = TILEX / player->move_delay_value;
12228
12229   if (!player->active)
12230     return;
12231
12232   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12233     return;
12234
12235   if (mode == SCROLL_INIT)
12236   {
12237     player->actual_frame_counter = FrameCounter;
12238     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12239
12240     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12241         Feld[last_jx][last_jy] == EL_EMPTY)
12242     {
12243       int last_field_block_delay = 0;   /* start with no blocking at all */
12244       int block_delay_adjustment = player->block_delay_adjustment;
12245
12246       /* if player blocks last field, add delay for exactly one move */
12247       if (player->block_last_field)
12248       {
12249         last_field_block_delay += player->move_delay_value;
12250
12251         /* when blocking enabled, prevent moving up despite gravity */
12252         if (player->gravity && player->MovDir == MV_UP)
12253           block_delay_adjustment = -1;
12254       }
12255
12256       /* add block delay adjustment (also possible when not blocking) */
12257       last_field_block_delay += block_delay_adjustment;
12258
12259       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12260       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12261     }
12262
12263     if (player->MovPos != 0)    /* player has not yet reached destination */
12264       return;
12265   }
12266   else if (!FrameReached(&player->actual_frame_counter, 1))
12267     return;
12268
12269   if (player->MovPos != 0)
12270   {
12271     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12272     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12273
12274     /* before DrawPlayer() to draw correct player graphic for this case */
12275     if (player->MovPos == 0)
12276       CheckGravityMovement(player);
12277   }
12278
12279   if (player->MovPos == 0)      /* player reached destination field */
12280   {
12281     if (player->move_delay_reset_counter > 0)
12282     {
12283       player->move_delay_reset_counter--;
12284
12285       if (player->move_delay_reset_counter == 0)
12286       {
12287         /* continue with normal speed after quickly moving through gate */
12288         HALVE_PLAYER_SPEED(player);
12289
12290         /* be able to make the next move without delay */
12291         player->move_delay = 0;
12292       }
12293     }
12294
12295     player->last_jx = jx;
12296     player->last_jy = jy;
12297
12298     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12299         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12300         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12301         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12302         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12303         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12304         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12305         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12306     {
12307       DrawPlayer(player);       /* needed here only to cleanup last field */
12308       RemovePlayer(player);
12309
12310       if (local_player->friends_still_needed == 0 ||
12311           IS_SP_ELEMENT(Feld[jx][jy]))
12312         PlayerWins(player);
12313     }
12314
12315     /* this breaks one level: "machine", level 000 */
12316     {
12317       int move_direction = player->MovDir;
12318       int enter_side = MV_DIR_OPPOSITE(move_direction);
12319       int leave_side = move_direction;
12320       int old_jx = last_jx;
12321       int old_jy = last_jy;
12322       int old_element = Feld[old_jx][old_jy];
12323       int new_element = Feld[jx][jy];
12324
12325       if (IS_CUSTOM_ELEMENT(old_element))
12326         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12327                                    CE_LEFT_BY_PLAYER,
12328                                    player->index_bit, leave_side);
12329
12330       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12331                                           CE_PLAYER_LEAVES_X,
12332                                           player->index_bit, leave_side);
12333
12334       if (IS_CUSTOM_ELEMENT(new_element))
12335         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12336                                    player->index_bit, enter_side);
12337
12338       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12339                                           CE_PLAYER_ENTERS_X,
12340                                           player->index_bit, enter_side);
12341
12342       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12343                                         CE_MOVE_OF_X, move_direction);
12344     }
12345
12346     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12347     {
12348       TestIfPlayerTouchesBadThing(jx, jy);
12349       TestIfPlayerTouchesCustomElement(jx, jy);
12350
12351       /* needed because pushed element has not yet reached its destination,
12352          so it would trigger a change event at its previous field location */
12353       if (!player->is_pushing)
12354         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12355
12356       if (!player->active)
12357         RemovePlayer(player);
12358     }
12359
12360     if (!local_player->LevelSolved && level.use_step_counter)
12361     {
12362       int i;
12363
12364       TimePlayed++;
12365
12366       if (TimeLeft > 0)
12367       {
12368         TimeLeft--;
12369
12370         if (TimeLeft <= 10 && setup.time_limit)
12371           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12372
12373         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12374
12375         DisplayGameControlValues();
12376
12377         if (!TimeLeft && setup.time_limit)
12378           for (i = 0; i < MAX_PLAYERS; i++)
12379             KillPlayer(&stored_player[i]);
12380       }
12381       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12382       {
12383         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12384
12385         DisplayGameControlValues();
12386       }
12387     }
12388
12389     if (tape.single_step && tape.recording && !tape.pausing &&
12390         !player->programmed_action)
12391       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12392
12393     if (!player->programmed_action)
12394       CheckSaveEngineSnapshot(player);
12395   }
12396 }
12397
12398 void ScrollScreen(struct PlayerInfo *player, int mode)
12399 {
12400   static unsigned int screen_frame_counter = 0;
12401
12402   if (mode == SCROLL_INIT)
12403   {
12404     /* set scrolling step size according to actual player's moving speed */
12405     ScrollStepSize = TILEX / player->move_delay_value;
12406
12407     screen_frame_counter = FrameCounter;
12408     ScreenMovDir = player->MovDir;
12409     ScreenMovPos = player->MovPos;
12410     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12411     return;
12412   }
12413   else if (!FrameReached(&screen_frame_counter, 1))
12414     return;
12415
12416   if (ScreenMovPos)
12417   {
12418     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12419     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12420     redraw_mask |= REDRAW_FIELD;
12421   }
12422   else
12423     ScreenMovDir = MV_NONE;
12424 }
12425
12426 void TestIfPlayerTouchesCustomElement(int x, int y)
12427 {
12428   static int xy[4][2] =
12429   {
12430     { 0, -1 },
12431     { -1, 0 },
12432     { +1, 0 },
12433     { 0, +1 }
12434   };
12435   static int trigger_sides[4][2] =
12436   {
12437     /* center side       border side */
12438     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12439     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12440     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12441     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12442   };
12443   static int touch_dir[4] =
12444   {
12445     MV_LEFT | MV_RIGHT,
12446     MV_UP   | MV_DOWN,
12447     MV_UP   | MV_DOWN,
12448     MV_LEFT | MV_RIGHT
12449   };
12450   int center_element = Feld[x][y];      /* should always be non-moving! */
12451   int i;
12452
12453   for (i = 0; i < NUM_DIRECTIONS; i++)
12454   {
12455     int xx = x + xy[i][0];
12456     int yy = y + xy[i][1];
12457     int center_side = trigger_sides[i][0];
12458     int border_side = trigger_sides[i][1];
12459     int border_element;
12460
12461     if (!IN_LEV_FIELD(xx, yy))
12462       continue;
12463
12464     if (IS_PLAYER(x, y))                /* player found at center element */
12465     {
12466       struct PlayerInfo *player = PLAYERINFO(x, y);
12467
12468       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12469         border_element = Feld[xx][yy];          /* may be moving! */
12470       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12471         border_element = Feld[xx][yy];
12472       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12473         border_element = MovingOrBlocked2Element(xx, yy);
12474       else
12475         continue;               /* center and border element do not touch */
12476
12477       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12478                                  player->index_bit, border_side);
12479       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12480                                           CE_PLAYER_TOUCHES_X,
12481                                           player->index_bit, border_side);
12482
12483       {
12484         /* use player element that is initially defined in the level playfield,
12485            not the player element that corresponds to the runtime player number
12486            (example: a level that contains EL_PLAYER_3 as the only player would
12487            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12488         int player_element = PLAYERINFO(x, y)->initial_element;
12489
12490         CheckElementChangeBySide(xx, yy, border_element, player_element,
12491                                  CE_TOUCHING_X, border_side);
12492       }
12493     }
12494     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12495     {
12496       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12497
12498       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12499       {
12500         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12501           continue;             /* center and border element do not touch */
12502       }
12503
12504       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12505                                  player->index_bit, center_side);
12506       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12507                                           CE_PLAYER_TOUCHES_X,
12508                                           player->index_bit, center_side);
12509
12510       {
12511         /* use player element that is initially defined in the level playfield,
12512            not the player element that corresponds to the runtime player number
12513            (example: a level that contains EL_PLAYER_3 as the only player would
12514            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12515         int player_element = PLAYERINFO(xx, yy)->initial_element;
12516
12517         CheckElementChangeBySide(x, y, center_element, player_element,
12518                                  CE_TOUCHING_X, center_side);
12519       }
12520
12521       break;
12522     }
12523   }
12524 }
12525
12526 void TestIfElementTouchesCustomElement(int x, int y)
12527 {
12528   static int xy[4][2] =
12529   {
12530     { 0, -1 },
12531     { -1, 0 },
12532     { +1, 0 },
12533     { 0, +1 }
12534   };
12535   static int trigger_sides[4][2] =
12536   {
12537     /* center side      border side */
12538     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12539     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12540     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12541     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12542   };
12543   static int touch_dir[4] =
12544   {
12545     MV_LEFT | MV_RIGHT,
12546     MV_UP   | MV_DOWN,
12547     MV_UP   | MV_DOWN,
12548     MV_LEFT | MV_RIGHT
12549   };
12550   boolean change_center_element = FALSE;
12551   int center_element = Feld[x][y];      /* should always be non-moving! */
12552   int border_element_old[NUM_DIRECTIONS];
12553   int i;
12554
12555   for (i = 0; i < NUM_DIRECTIONS; i++)
12556   {
12557     int xx = x + xy[i][0];
12558     int yy = y + xy[i][1];
12559     int border_element;
12560
12561     border_element_old[i] = -1;
12562
12563     if (!IN_LEV_FIELD(xx, yy))
12564       continue;
12565
12566     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12567       border_element = Feld[xx][yy];    /* may be moving! */
12568     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12569       border_element = Feld[xx][yy];
12570     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12571       border_element = MovingOrBlocked2Element(xx, yy);
12572     else
12573       continue;                 /* center and border element do not touch */
12574
12575     border_element_old[i] = border_element;
12576   }
12577
12578   for (i = 0; i < NUM_DIRECTIONS; i++)
12579   {
12580     int xx = x + xy[i][0];
12581     int yy = y + xy[i][1];
12582     int center_side = trigger_sides[i][0];
12583     int border_element = border_element_old[i];
12584
12585     if (border_element == -1)
12586       continue;
12587
12588     /* check for change of border element */
12589     CheckElementChangeBySide(xx, yy, border_element, center_element,
12590                              CE_TOUCHING_X, center_side);
12591
12592     /* (center element cannot be player, so we dont have to check this here) */
12593   }
12594
12595   for (i = 0; i < NUM_DIRECTIONS; i++)
12596   {
12597     int xx = x + xy[i][0];
12598     int yy = y + xy[i][1];
12599     int border_side = trigger_sides[i][1];
12600     int border_element = border_element_old[i];
12601
12602     if (border_element == -1)
12603       continue;
12604
12605     /* check for change of center element (but change it only once) */
12606     if (!change_center_element)
12607       change_center_element =
12608         CheckElementChangeBySide(x, y, center_element, border_element,
12609                                  CE_TOUCHING_X, border_side);
12610
12611     if (IS_PLAYER(xx, yy))
12612     {
12613       /* use player element that is initially defined in the level playfield,
12614          not the player element that corresponds to the runtime player number
12615          (example: a level that contains EL_PLAYER_3 as the only player would
12616          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12617       int player_element = PLAYERINFO(xx, yy)->initial_element;
12618
12619       CheckElementChangeBySide(x, y, center_element, player_element,
12620                                CE_TOUCHING_X, border_side);
12621     }
12622   }
12623 }
12624
12625 void TestIfElementHitsCustomElement(int x, int y, int direction)
12626 {
12627   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12628   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12629   int hitx = x + dx, hity = y + dy;
12630   int hitting_element = Feld[x][y];
12631   int touched_element;
12632
12633   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12634     return;
12635
12636   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12637                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12638
12639   if (IN_LEV_FIELD(hitx, hity))
12640   {
12641     int opposite_direction = MV_DIR_OPPOSITE(direction);
12642     int hitting_side = direction;
12643     int touched_side = opposite_direction;
12644     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12645                           MovDir[hitx][hity] != direction ||
12646                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12647
12648     object_hit = TRUE;
12649
12650     if (object_hit)
12651     {
12652       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12653                                CE_HITTING_X, touched_side);
12654
12655       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12656                                CE_HIT_BY_X, hitting_side);
12657
12658       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12659                                CE_HIT_BY_SOMETHING, opposite_direction);
12660
12661       if (IS_PLAYER(hitx, hity))
12662       {
12663         /* use player element that is initially defined in the level playfield,
12664            not the player element that corresponds to the runtime player number
12665            (example: a level that contains EL_PLAYER_3 as the only player would
12666            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12667         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12668
12669         CheckElementChangeBySide(x, y, hitting_element, player_element,
12670                                  CE_HITTING_X, touched_side);
12671       }
12672     }
12673   }
12674
12675   /* "hitting something" is also true when hitting the playfield border */
12676   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12677                            CE_HITTING_SOMETHING, direction);
12678 }
12679
12680 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12681 {
12682   int i, kill_x = -1, kill_y = -1;
12683
12684   int bad_element = -1;
12685   static int test_xy[4][2] =
12686   {
12687     { 0, -1 },
12688     { -1, 0 },
12689     { +1, 0 },
12690     { 0, +1 }
12691   };
12692   static int test_dir[4] =
12693   {
12694     MV_UP,
12695     MV_LEFT,
12696     MV_RIGHT,
12697     MV_DOWN
12698   };
12699
12700   for (i = 0; i < NUM_DIRECTIONS; i++)
12701   {
12702     int test_x, test_y, test_move_dir, test_element;
12703
12704     test_x = good_x + test_xy[i][0];
12705     test_y = good_y + test_xy[i][1];
12706
12707     if (!IN_LEV_FIELD(test_x, test_y))
12708       continue;
12709
12710     test_move_dir =
12711       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12712
12713     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12714
12715     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12716        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12717     */
12718     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12719         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12720     {
12721       kill_x = test_x;
12722       kill_y = test_y;
12723       bad_element = test_element;
12724
12725       break;
12726     }
12727   }
12728
12729   if (kill_x != -1 || kill_y != -1)
12730   {
12731     if (IS_PLAYER(good_x, good_y))
12732     {
12733       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12734
12735       if (player->shield_deadly_time_left > 0 &&
12736           !IS_INDESTRUCTIBLE(bad_element))
12737         Bang(kill_x, kill_y);
12738       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12739         KillPlayer(player);
12740     }
12741     else
12742       Bang(good_x, good_y);
12743   }
12744 }
12745
12746 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12747 {
12748   int i, kill_x = -1, kill_y = -1;
12749   int bad_element = Feld[bad_x][bad_y];
12750   static int test_xy[4][2] =
12751   {
12752     { 0, -1 },
12753     { -1, 0 },
12754     { +1, 0 },
12755     { 0, +1 }
12756   };
12757   static int touch_dir[4] =
12758   {
12759     MV_LEFT | MV_RIGHT,
12760     MV_UP   | MV_DOWN,
12761     MV_UP   | MV_DOWN,
12762     MV_LEFT | MV_RIGHT
12763   };
12764   static int test_dir[4] =
12765   {
12766     MV_UP,
12767     MV_LEFT,
12768     MV_RIGHT,
12769     MV_DOWN
12770   };
12771
12772   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12773     return;
12774
12775   for (i = 0; i < NUM_DIRECTIONS; i++)
12776   {
12777     int test_x, test_y, test_move_dir, test_element;
12778
12779     test_x = bad_x + test_xy[i][0];
12780     test_y = bad_y + test_xy[i][1];
12781
12782     if (!IN_LEV_FIELD(test_x, test_y))
12783       continue;
12784
12785     test_move_dir =
12786       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12787
12788     test_element = Feld[test_x][test_y];
12789
12790     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12791        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12792     */
12793     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12794         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12795     {
12796       /* good thing is player or penguin that does not move away */
12797       if (IS_PLAYER(test_x, test_y))
12798       {
12799         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12800
12801         if (bad_element == EL_ROBOT && player->is_moving)
12802           continue;     /* robot does not kill player if he is moving */
12803
12804         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12805         {
12806           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12807             continue;           /* center and border element do not touch */
12808         }
12809
12810         kill_x = test_x;
12811         kill_y = test_y;
12812
12813         break;
12814       }
12815       else if (test_element == EL_PENGUIN)
12816       {
12817         kill_x = test_x;
12818         kill_y = test_y;
12819
12820         break;
12821       }
12822     }
12823   }
12824
12825   if (kill_x != -1 || kill_y != -1)
12826   {
12827     if (IS_PLAYER(kill_x, kill_y))
12828     {
12829       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12830
12831       if (player->shield_deadly_time_left > 0 &&
12832           !IS_INDESTRUCTIBLE(bad_element))
12833         Bang(bad_x, bad_y);
12834       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12835         KillPlayer(player);
12836     }
12837     else
12838       Bang(kill_x, kill_y);
12839   }
12840 }
12841
12842 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12843 {
12844   int bad_element = Feld[bad_x][bad_y];
12845   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12846   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12847   int test_x = bad_x + dx, test_y = bad_y + dy;
12848   int test_move_dir, test_element;
12849   int kill_x = -1, kill_y = -1;
12850
12851   if (!IN_LEV_FIELD(test_x, test_y))
12852     return;
12853
12854   test_move_dir =
12855     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12856
12857   test_element = Feld[test_x][test_y];
12858
12859   if (test_move_dir != bad_move_dir)
12860   {
12861     /* good thing can be player or penguin that does not move away */
12862     if (IS_PLAYER(test_x, test_y))
12863     {
12864       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12865
12866       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12867          player as being hit when he is moving towards the bad thing, because
12868          the "get hit by" condition would be lost after the player stops) */
12869       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12870         return;         /* player moves away from bad thing */
12871
12872       kill_x = test_x;
12873       kill_y = test_y;
12874     }
12875     else if (test_element == EL_PENGUIN)
12876     {
12877       kill_x = test_x;
12878       kill_y = test_y;
12879     }
12880   }
12881
12882   if (kill_x != -1 || kill_y != -1)
12883   {
12884     if (IS_PLAYER(kill_x, kill_y))
12885     {
12886       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12887
12888       if (player->shield_deadly_time_left > 0 &&
12889           !IS_INDESTRUCTIBLE(bad_element))
12890         Bang(bad_x, bad_y);
12891       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12892         KillPlayer(player);
12893     }
12894     else
12895       Bang(kill_x, kill_y);
12896   }
12897 }
12898
12899 void TestIfPlayerTouchesBadThing(int x, int y)
12900 {
12901   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12902 }
12903
12904 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12905 {
12906   TestIfGoodThingHitsBadThing(x, y, move_dir);
12907 }
12908
12909 void TestIfBadThingTouchesPlayer(int x, int y)
12910 {
12911   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12912 }
12913
12914 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12915 {
12916   TestIfBadThingHitsGoodThing(x, y, move_dir);
12917 }
12918
12919 void TestIfFriendTouchesBadThing(int x, int y)
12920 {
12921   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12922 }
12923
12924 void TestIfBadThingTouchesFriend(int x, int y)
12925 {
12926   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12927 }
12928
12929 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12930 {
12931   int i, kill_x = bad_x, kill_y = bad_y;
12932   static int xy[4][2] =
12933   {
12934     { 0, -1 },
12935     { -1, 0 },
12936     { +1, 0 },
12937     { 0, +1 }
12938   };
12939
12940   for (i = 0; i < NUM_DIRECTIONS; i++)
12941   {
12942     int x, y, element;
12943
12944     x = bad_x + xy[i][0];
12945     y = bad_y + xy[i][1];
12946     if (!IN_LEV_FIELD(x, y))
12947       continue;
12948
12949     element = Feld[x][y];
12950     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12951         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12952     {
12953       kill_x = x;
12954       kill_y = y;
12955       break;
12956     }
12957   }
12958
12959   if (kill_x != bad_x || kill_y != bad_y)
12960     Bang(bad_x, bad_y);
12961 }
12962
12963 void KillPlayer(struct PlayerInfo *player)
12964 {
12965   int jx = player->jx, jy = player->jy;
12966
12967   if (!player->active)
12968     return;
12969
12970 #if 0
12971   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12972          player->killed, player->active, player->reanimated);
12973 #endif
12974
12975   /* the following code was introduced to prevent an infinite loop when calling
12976      -> Bang()
12977      -> CheckTriggeredElementChangeExt()
12978      -> ExecuteCustomElementAction()
12979      -> KillPlayer()
12980      -> (infinitely repeating the above sequence of function calls)
12981      which occurs when killing the player while having a CE with the setting
12982      "kill player X when explosion of <player X>"; the solution using a new
12983      field "player->killed" was chosen for backwards compatibility, although
12984      clever use of the fields "player->active" etc. would probably also work */
12985 #if 1
12986   if (player->killed)
12987     return;
12988 #endif
12989
12990   player->killed = TRUE;
12991
12992   /* remove accessible field at the player's position */
12993   Feld[jx][jy] = EL_EMPTY;
12994
12995   /* deactivate shield (else Bang()/Explode() would not work right) */
12996   player->shield_normal_time_left = 0;
12997   player->shield_deadly_time_left = 0;
12998
12999 #if 0
13000   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13001          player->killed, player->active, player->reanimated);
13002 #endif
13003
13004   Bang(jx, jy);
13005
13006 #if 0
13007   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13008          player->killed, player->active, player->reanimated);
13009 #endif
13010
13011   if (player->reanimated)       /* killed player may have been reanimated */
13012     player->killed = player->reanimated = FALSE;
13013   else
13014     BuryPlayer(player);
13015 }
13016
13017 static void KillPlayerUnlessEnemyProtected(int x, int y)
13018 {
13019   if (!PLAYER_ENEMY_PROTECTED(x, y))
13020     KillPlayer(PLAYERINFO(x, y));
13021 }
13022
13023 static void KillPlayerUnlessExplosionProtected(int x, int y)
13024 {
13025   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13026     KillPlayer(PLAYERINFO(x, y));
13027 }
13028
13029 void BuryPlayer(struct PlayerInfo *player)
13030 {
13031   int jx = player->jx, jy = player->jy;
13032
13033   if (!player->active)
13034     return;
13035
13036   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13037   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13038
13039   player->GameOver = TRUE;
13040   RemovePlayer(player);
13041 }
13042
13043 void RemovePlayer(struct PlayerInfo *player)
13044 {
13045   int jx = player->jx, jy = player->jy;
13046   int i, found = FALSE;
13047
13048   player->present = FALSE;
13049   player->active = FALSE;
13050
13051   if (!ExplodeField[jx][jy])
13052     StorePlayer[jx][jy] = 0;
13053
13054   if (player->is_moving)
13055     TEST_DrawLevelField(player->last_jx, player->last_jy);
13056
13057   for (i = 0; i < MAX_PLAYERS; i++)
13058     if (stored_player[i].active)
13059       found = TRUE;
13060
13061   if (!found)
13062     AllPlayersGone = TRUE;
13063
13064   ExitX = ZX = jx;
13065   ExitY = ZY = jy;
13066 }
13067
13068 static void setFieldForSnapping(int x, int y, int element, int direction)
13069 {
13070   struct ElementInfo *ei = &element_info[element];
13071   int direction_bit = MV_DIR_TO_BIT(direction);
13072   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13073   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13074                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13075
13076   Feld[x][y] = EL_ELEMENT_SNAPPING;
13077   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13078
13079   ResetGfxAnimation(x, y);
13080
13081   GfxElement[x][y] = element;
13082   GfxAction[x][y] = action;
13083   GfxDir[x][y] = direction;
13084   GfxFrame[x][y] = -1;
13085 }
13086
13087 /*
13088   =============================================================================
13089   checkDiagonalPushing()
13090   -----------------------------------------------------------------------------
13091   check if diagonal input device direction results in pushing of object
13092   (by checking if the alternative direction is walkable, diggable, ...)
13093   =============================================================================
13094 */
13095
13096 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13097                                     int x, int y, int real_dx, int real_dy)
13098 {
13099   int jx, jy, dx, dy, xx, yy;
13100
13101   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13102     return TRUE;
13103
13104   /* diagonal direction: check alternative direction */
13105   jx = player->jx;
13106   jy = player->jy;
13107   dx = x - jx;
13108   dy = y - jy;
13109   xx = jx + (dx == 0 ? real_dx : 0);
13110   yy = jy + (dy == 0 ? real_dy : 0);
13111
13112   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13113 }
13114
13115 /*
13116   =============================================================================
13117   DigField()
13118   -----------------------------------------------------------------------------
13119   x, y:                 field next to player (non-diagonal) to try to dig to
13120   real_dx, real_dy:     direction as read from input device (can be diagonal)
13121   =============================================================================
13122 */
13123
13124 static int DigField(struct PlayerInfo *player,
13125                     int oldx, int oldy, int x, int y,
13126                     int real_dx, int real_dy, int mode)
13127 {
13128   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13129   boolean player_was_pushing = player->is_pushing;
13130   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13131   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13132   int jx = oldx, jy = oldy;
13133   int dx = x - jx, dy = y - jy;
13134   int nextx = x + dx, nexty = y + dy;
13135   int move_direction = (dx == -1 ? MV_LEFT  :
13136                         dx == +1 ? MV_RIGHT :
13137                         dy == -1 ? MV_UP    :
13138                         dy == +1 ? MV_DOWN  : MV_NONE);
13139   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13140   int dig_side = MV_DIR_OPPOSITE(move_direction);
13141   int old_element = Feld[jx][jy];
13142   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13143   int collect_count;
13144
13145   if (is_player)                /* function can also be called by EL_PENGUIN */
13146   {
13147     if (player->MovPos == 0)
13148     {
13149       player->is_digging = FALSE;
13150       player->is_collecting = FALSE;
13151     }
13152
13153     if (player->MovPos == 0)    /* last pushing move finished */
13154       player->is_pushing = FALSE;
13155
13156     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13157     {
13158       player->is_switching = FALSE;
13159       player->push_delay = -1;
13160
13161       return MP_NO_ACTION;
13162     }
13163   }
13164
13165   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13166     old_element = Back[jx][jy];
13167
13168   /* in case of element dropped at player position, check background */
13169   else if (Back[jx][jy] != EL_EMPTY &&
13170            game.engine_version >= VERSION_IDENT(2,2,0,0))
13171     old_element = Back[jx][jy];
13172
13173   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13174     return MP_NO_ACTION;        /* field has no opening in this direction */
13175
13176   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13177     return MP_NO_ACTION;        /* field has no opening in this direction */
13178
13179   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13180   {
13181     SplashAcid(x, y);
13182
13183     Feld[jx][jy] = player->artwork_element;
13184     InitMovingField(jx, jy, MV_DOWN);
13185     Store[jx][jy] = EL_ACID;
13186     ContinueMoving(jx, jy);
13187     BuryPlayer(player);
13188
13189     return MP_DONT_RUN_INTO;
13190   }
13191
13192   if (player_can_move && DONT_RUN_INTO(element))
13193   {
13194     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13195
13196     return MP_DONT_RUN_INTO;
13197   }
13198
13199   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13200     return MP_NO_ACTION;
13201
13202   collect_count = element_info[element].collect_count_initial;
13203
13204   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13205     return MP_NO_ACTION;
13206
13207   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13208     player_can_move = player_can_move_or_snap;
13209
13210   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13211       game.engine_version >= VERSION_IDENT(2,2,0,0))
13212   {
13213     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13214                                player->index_bit, dig_side);
13215     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13216                                         player->index_bit, dig_side);
13217
13218     if (element == EL_DC_LANDMINE)
13219       Bang(x, y);
13220
13221     if (Feld[x][y] != element)          /* field changed by snapping */
13222       return MP_ACTION;
13223
13224     return MP_NO_ACTION;
13225   }
13226
13227   if (player->gravity && is_player && !player->is_auto_moving &&
13228       canFallDown(player) && move_direction != MV_DOWN &&
13229       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13230     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13231
13232   if (player_can_move &&
13233       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13234   {
13235     int sound_element = SND_ELEMENT(element);
13236     int sound_action = ACTION_WALKING;
13237
13238     if (IS_RND_GATE(element))
13239     {
13240       if (!player->key[RND_GATE_NR(element)])
13241         return MP_NO_ACTION;
13242     }
13243     else if (IS_RND_GATE_GRAY(element))
13244     {
13245       if (!player->key[RND_GATE_GRAY_NR(element)])
13246         return MP_NO_ACTION;
13247     }
13248     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13249     {
13250       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13251         return MP_NO_ACTION;
13252     }
13253     else if (element == EL_EXIT_OPEN ||
13254              element == EL_EM_EXIT_OPEN ||
13255              element == EL_EM_EXIT_OPENING ||
13256              element == EL_STEEL_EXIT_OPEN ||
13257              element == EL_EM_STEEL_EXIT_OPEN ||
13258              element == EL_EM_STEEL_EXIT_OPENING ||
13259              element == EL_SP_EXIT_OPEN ||
13260              element == EL_SP_EXIT_OPENING)
13261     {
13262       sound_action = ACTION_PASSING;    /* player is passing exit */
13263     }
13264     else if (element == EL_EMPTY)
13265     {
13266       sound_action = ACTION_MOVING;             /* nothing to walk on */
13267     }
13268
13269     /* play sound from background or player, whatever is available */
13270     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13271       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13272     else
13273       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13274   }
13275   else if (player_can_move &&
13276            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13277   {
13278     if (!ACCESS_FROM(element, opposite_direction))
13279       return MP_NO_ACTION;      /* field not accessible from this direction */
13280
13281     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13282       return MP_NO_ACTION;
13283
13284     if (IS_EM_GATE(element))
13285     {
13286       if (!player->key[EM_GATE_NR(element)])
13287         return MP_NO_ACTION;
13288     }
13289     else if (IS_EM_GATE_GRAY(element))
13290     {
13291       if (!player->key[EM_GATE_GRAY_NR(element)])
13292         return MP_NO_ACTION;
13293     }
13294     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13295     {
13296       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13297         return MP_NO_ACTION;
13298     }
13299     else if (IS_EMC_GATE(element))
13300     {
13301       if (!player->key[EMC_GATE_NR(element)])
13302         return MP_NO_ACTION;
13303     }
13304     else if (IS_EMC_GATE_GRAY(element))
13305     {
13306       if (!player->key[EMC_GATE_GRAY_NR(element)])
13307         return MP_NO_ACTION;
13308     }
13309     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13310     {
13311       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13312         return MP_NO_ACTION;
13313     }
13314     else if (element == EL_DC_GATE_WHITE ||
13315              element == EL_DC_GATE_WHITE_GRAY ||
13316              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13317     {
13318       if (player->num_white_keys == 0)
13319         return MP_NO_ACTION;
13320
13321       player->num_white_keys--;
13322     }
13323     else if (IS_SP_PORT(element))
13324     {
13325       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13326           element == EL_SP_GRAVITY_PORT_RIGHT ||
13327           element == EL_SP_GRAVITY_PORT_UP ||
13328           element == EL_SP_GRAVITY_PORT_DOWN)
13329         player->gravity = !player->gravity;
13330       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13331                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13332                element == EL_SP_GRAVITY_ON_PORT_UP ||
13333                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13334         player->gravity = TRUE;
13335       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13336                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13337                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13338                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13339         player->gravity = FALSE;
13340     }
13341
13342     /* automatically move to the next field with double speed */
13343     player->programmed_action = move_direction;
13344
13345     if (player->move_delay_reset_counter == 0)
13346     {
13347       player->move_delay_reset_counter = 2;     /* two double speed steps */
13348
13349       DOUBLE_PLAYER_SPEED(player);
13350     }
13351
13352     PlayLevelSoundAction(x, y, ACTION_PASSING);
13353   }
13354   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13355   {
13356     RemoveField(x, y);
13357
13358     if (mode != DF_SNAP)
13359     {
13360       GfxElement[x][y] = GFX_ELEMENT(element);
13361       player->is_digging = TRUE;
13362     }
13363
13364     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13365
13366     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13367                                         player->index_bit, dig_side);
13368
13369     if (mode == DF_SNAP)
13370     {
13371       if (level.block_snap_field)
13372         setFieldForSnapping(x, y, element, move_direction);
13373       else
13374         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13375
13376       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13377                                           player->index_bit, dig_side);
13378     }
13379   }
13380   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13381   {
13382     RemoveField(x, y);
13383
13384     if (is_player && mode != DF_SNAP)
13385     {
13386       GfxElement[x][y] = element;
13387       player->is_collecting = TRUE;
13388     }
13389
13390     if (element == EL_SPEED_PILL)
13391     {
13392       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13393     }
13394     else if (element == EL_EXTRA_TIME && level.time > 0)
13395     {
13396       TimeLeft += level.extra_time;
13397
13398       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13399
13400       DisplayGameControlValues();
13401     }
13402     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13403     {
13404       player->shield_normal_time_left += level.shield_normal_time;
13405       if (element == EL_SHIELD_DEADLY)
13406         player->shield_deadly_time_left += level.shield_deadly_time;
13407     }
13408     else if (element == EL_DYNAMITE ||
13409              element == EL_EM_DYNAMITE ||
13410              element == EL_SP_DISK_RED)
13411     {
13412       if (player->inventory_size < MAX_INVENTORY_SIZE)
13413         player->inventory_element[player->inventory_size++] = element;
13414
13415       DrawGameDoorValues();
13416     }
13417     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13418     {
13419       player->dynabomb_count++;
13420       player->dynabombs_left++;
13421     }
13422     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13423     {
13424       player->dynabomb_size++;
13425     }
13426     else if (element == EL_DYNABOMB_INCREASE_POWER)
13427     {
13428       player->dynabomb_xl = TRUE;
13429     }
13430     else if (IS_KEY(element))
13431     {
13432       player->key[KEY_NR(element)] = TRUE;
13433
13434       DrawGameDoorValues();
13435     }
13436     else if (element == EL_DC_KEY_WHITE)
13437     {
13438       player->num_white_keys++;
13439
13440       /* display white keys? */
13441       /* DrawGameDoorValues(); */
13442     }
13443     else if (IS_ENVELOPE(element))
13444     {
13445       player->show_envelope = element;
13446     }
13447     else if (element == EL_EMC_LENSES)
13448     {
13449       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13450
13451       RedrawAllInvisibleElementsForLenses();
13452     }
13453     else if (element == EL_EMC_MAGNIFIER)
13454     {
13455       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13456
13457       RedrawAllInvisibleElementsForMagnifier();
13458     }
13459     else if (IS_DROPPABLE(element) ||
13460              IS_THROWABLE(element))     /* can be collected and dropped */
13461     {
13462       int i;
13463
13464       if (collect_count == 0)
13465         player->inventory_infinite_element = element;
13466       else
13467         for (i = 0; i < collect_count; i++)
13468           if (player->inventory_size < MAX_INVENTORY_SIZE)
13469             player->inventory_element[player->inventory_size++] = element;
13470
13471       DrawGameDoorValues();
13472     }
13473     else if (collect_count > 0)
13474     {
13475       local_player->gems_still_needed -= collect_count;
13476       if (local_player->gems_still_needed < 0)
13477         local_player->gems_still_needed = 0;
13478
13479       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13480
13481       DisplayGameControlValues();
13482     }
13483
13484     RaiseScoreElement(element);
13485     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13486
13487     if (is_player)
13488       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13489                                           player->index_bit, dig_side);
13490
13491     if (mode == DF_SNAP)
13492     {
13493       if (level.block_snap_field)
13494         setFieldForSnapping(x, y, element, move_direction);
13495       else
13496         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13497
13498       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13499                                           player->index_bit, dig_side);
13500     }
13501   }
13502   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13503   {
13504     if (mode == DF_SNAP && element != EL_BD_ROCK)
13505       return MP_NO_ACTION;
13506
13507     if (CAN_FALL(element) && dy)
13508       return MP_NO_ACTION;
13509
13510     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13511         !(element == EL_SPRING && level.use_spring_bug))
13512       return MP_NO_ACTION;
13513
13514     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13515         ((move_direction & MV_VERTICAL &&
13516           ((element_info[element].move_pattern & MV_LEFT &&
13517             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13518            (element_info[element].move_pattern & MV_RIGHT &&
13519             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13520          (move_direction & MV_HORIZONTAL &&
13521           ((element_info[element].move_pattern & MV_UP &&
13522             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13523            (element_info[element].move_pattern & MV_DOWN &&
13524             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13525       return MP_NO_ACTION;
13526
13527     /* do not push elements already moving away faster than player */
13528     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13529         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13530       return MP_NO_ACTION;
13531
13532     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13533     {
13534       if (player->push_delay_value == -1 || !player_was_pushing)
13535         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13536     }
13537     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13538     {
13539       if (player->push_delay_value == -1)
13540         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13541     }
13542     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13543     {
13544       if (!player->is_pushing)
13545         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13546     }
13547
13548     player->is_pushing = TRUE;
13549     player->is_active = TRUE;
13550
13551     if (!(IN_LEV_FIELD(nextx, nexty) &&
13552           (IS_FREE(nextx, nexty) ||
13553            (IS_SB_ELEMENT(element) &&
13554             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13555            (IS_CUSTOM_ELEMENT(element) &&
13556             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13557       return MP_NO_ACTION;
13558
13559     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13560       return MP_NO_ACTION;
13561
13562     if (player->push_delay == -1)       /* new pushing; restart delay */
13563       player->push_delay = 0;
13564
13565     if (player->push_delay < player->push_delay_value &&
13566         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13567         element != EL_SPRING && element != EL_BALLOON)
13568     {
13569       /* make sure that there is no move delay before next try to push */
13570       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13571         player->move_delay = 0;
13572
13573       return MP_NO_ACTION;
13574     }
13575
13576     if (IS_CUSTOM_ELEMENT(element) &&
13577         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13578     {
13579       if (!DigFieldByCE(nextx, nexty, element))
13580         return MP_NO_ACTION;
13581     }
13582
13583     if (IS_SB_ELEMENT(element))
13584     {
13585       if (element == EL_SOKOBAN_FIELD_FULL)
13586       {
13587         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13588         local_player->sokobanfields_still_needed++;
13589       }
13590
13591       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13592       {
13593         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13594         local_player->sokobanfields_still_needed--;
13595       }
13596
13597       Feld[x][y] = EL_SOKOBAN_OBJECT;
13598
13599       if (Back[x][y] == Back[nextx][nexty])
13600         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13601       else if (Back[x][y] != 0)
13602         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13603                                     ACTION_EMPTYING);
13604       else
13605         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13606                                     ACTION_FILLING);
13607
13608       if (local_player->sokobanfields_still_needed == 0 &&
13609           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13610       {
13611         PlayerWins(player);
13612
13613         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13614       }
13615     }
13616     else
13617       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13618
13619     InitMovingField(x, y, move_direction);
13620     GfxAction[x][y] = ACTION_PUSHING;
13621
13622     if (mode == DF_SNAP)
13623       ContinueMoving(x, y);
13624     else
13625       MovPos[x][y] = (dx != 0 ? dx : dy);
13626
13627     Pushed[x][y] = TRUE;
13628     Pushed[nextx][nexty] = TRUE;
13629
13630     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13631       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13632     else
13633       player->push_delay_value = -1;    /* get new value later */
13634
13635     /* check for element change _after_ element has been pushed */
13636     if (game.use_change_when_pushing_bug)
13637     {
13638       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13639                                  player->index_bit, dig_side);
13640       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13641                                           player->index_bit, dig_side);
13642     }
13643   }
13644   else if (IS_SWITCHABLE(element))
13645   {
13646     if (PLAYER_SWITCHING(player, x, y))
13647     {
13648       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13649                                           player->index_bit, dig_side);
13650
13651       return MP_ACTION;
13652     }
13653
13654     player->is_switching = TRUE;
13655     player->switch_x = x;
13656     player->switch_y = y;
13657
13658     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13659
13660     if (element == EL_ROBOT_WHEEL)
13661     {
13662       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13663       ZX = x;
13664       ZY = y;
13665
13666       game.robot_wheel_active = TRUE;
13667
13668       TEST_DrawLevelField(x, y);
13669     }
13670     else if (element == EL_SP_TERMINAL)
13671     {
13672       int xx, yy;
13673
13674       SCAN_PLAYFIELD(xx, yy)
13675       {
13676         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13677           Bang(xx, yy);
13678         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13679           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13680       }
13681     }
13682     else if (IS_BELT_SWITCH(element))
13683     {
13684       ToggleBeltSwitch(x, y);
13685     }
13686     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13687              element == EL_SWITCHGATE_SWITCH_DOWN ||
13688              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13689              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13690     {
13691       ToggleSwitchgateSwitch(x, y);
13692     }
13693     else if (element == EL_LIGHT_SWITCH ||
13694              element == EL_LIGHT_SWITCH_ACTIVE)
13695     {
13696       ToggleLightSwitch(x, y);
13697     }
13698     else if (element == EL_TIMEGATE_SWITCH ||
13699              element == EL_DC_TIMEGATE_SWITCH)
13700     {
13701       ActivateTimegateSwitch(x, y);
13702     }
13703     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13704              element == EL_BALLOON_SWITCH_RIGHT ||
13705              element == EL_BALLOON_SWITCH_UP    ||
13706              element == EL_BALLOON_SWITCH_DOWN  ||
13707              element == EL_BALLOON_SWITCH_NONE  ||
13708              element == EL_BALLOON_SWITCH_ANY)
13709     {
13710       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13711                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13712                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13713                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13714                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13715                              move_direction);
13716     }
13717     else if (element == EL_LAMP)
13718     {
13719       Feld[x][y] = EL_LAMP_ACTIVE;
13720       local_player->lights_still_needed--;
13721
13722       ResetGfxAnimation(x, y);
13723       TEST_DrawLevelField(x, y);
13724     }
13725     else if (element == EL_TIME_ORB_FULL)
13726     {
13727       Feld[x][y] = EL_TIME_ORB_EMPTY;
13728
13729       if (level.time > 0 || level.use_time_orb_bug)
13730       {
13731         TimeLeft += level.time_orb_time;
13732         game.no_time_limit = FALSE;
13733
13734         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13735
13736         DisplayGameControlValues();
13737       }
13738
13739       ResetGfxAnimation(x, y);
13740       TEST_DrawLevelField(x, y);
13741     }
13742     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13743              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13744     {
13745       int xx, yy;
13746
13747       game.ball_state = !game.ball_state;
13748
13749       SCAN_PLAYFIELD(xx, yy)
13750       {
13751         int e = Feld[xx][yy];
13752
13753         if (game.ball_state)
13754         {
13755           if (e == EL_EMC_MAGIC_BALL)
13756             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13757           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13758             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13759         }
13760         else
13761         {
13762           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13763             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13764           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13765             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13766         }
13767       }
13768     }
13769
13770     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13771                                         player->index_bit, dig_side);
13772
13773     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13774                                         player->index_bit, dig_side);
13775
13776     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13777                                         player->index_bit, dig_side);
13778
13779     return MP_ACTION;
13780   }
13781   else
13782   {
13783     if (!PLAYER_SWITCHING(player, x, y))
13784     {
13785       player->is_switching = TRUE;
13786       player->switch_x = x;
13787       player->switch_y = y;
13788
13789       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13790                                  player->index_bit, dig_side);
13791       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13792                                           player->index_bit, dig_side);
13793
13794       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13795                                  player->index_bit, dig_side);
13796       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13797                                           player->index_bit, dig_side);
13798     }
13799
13800     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13801                                player->index_bit, dig_side);
13802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13803                                         player->index_bit, dig_side);
13804
13805     return MP_NO_ACTION;
13806   }
13807
13808   player->push_delay = -1;
13809
13810   if (is_player)                /* function can also be called by EL_PENGUIN */
13811   {
13812     if (Feld[x][y] != element)          /* really digged/collected something */
13813     {
13814       player->is_collecting = !player->is_digging;
13815       player->is_active = TRUE;
13816     }
13817   }
13818
13819   return MP_MOVING;
13820 }
13821
13822 static boolean DigFieldByCE(int x, int y, int digging_element)
13823 {
13824   int element = Feld[x][y];
13825
13826   if (!IS_FREE(x, y))
13827   {
13828     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13829                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13830                   ACTION_BREAKING);
13831
13832     /* no element can dig solid indestructible elements */
13833     if (IS_INDESTRUCTIBLE(element) &&
13834         !IS_DIGGABLE(element) &&
13835         !IS_COLLECTIBLE(element))
13836       return FALSE;
13837
13838     if (AmoebaNr[x][y] &&
13839         (element == EL_AMOEBA_FULL ||
13840          element == EL_BD_AMOEBA ||
13841          element == EL_AMOEBA_GROWING))
13842     {
13843       AmoebaCnt[AmoebaNr[x][y]]--;
13844       AmoebaCnt2[AmoebaNr[x][y]]--;
13845     }
13846
13847     if (IS_MOVING(x, y))
13848       RemoveMovingField(x, y);
13849     else
13850     {
13851       RemoveField(x, y);
13852       TEST_DrawLevelField(x, y);
13853     }
13854
13855     /* if digged element was about to explode, prevent the explosion */
13856     ExplodeField[x][y] = EX_TYPE_NONE;
13857
13858     PlayLevelSoundAction(x, y, action);
13859   }
13860
13861   Store[x][y] = EL_EMPTY;
13862
13863   /* this makes it possible to leave the removed element again */
13864   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13865     Store[x][y] = element;
13866
13867   return TRUE;
13868 }
13869
13870 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13871 {
13872   int jx = player->jx, jy = player->jy;
13873   int x = jx + dx, y = jy + dy;
13874   int snap_direction = (dx == -1 ? MV_LEFT  :
13875                         dx == +1 ? MV_RIGHT :
13876                         dy == -1 ? MV_UP    :
13877                         dy == +1 ? MV_DOWN  : MV_NONE);
13878   boolean can_continue_snapping = (level.continuous_snapping &&
13879                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13880
13881   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13882     return FALSE;
13883
13884   if (!player->active || !IN_LEV_FIELD(x, y))
13885     return FALSE;
13886
13887   if (dx && dy)
13888     return FALSE;
13889
13890   if (!dx && !dy)
13891   {
13892     if (player->MovPos == 0)
13893       player->is_pushing = FALSE;
13894
13895     player->is_snapping = FALSE;
13896
13897     if (player->MovPos == 0)
13898     {
13899       player->is_moving = FALSE;
13900       player->is_digging = FALSE;
13901       player->is_collecting = FALSE;
13902     }
13903
13904     return FALSE;
13905   }
13906
13907   /* prevent snapping with already pressed snap key when not allowed */
13908   if (player->is_snapping && !can_continue_snapping)
13909     return FALSE;
13910
13911   player->MovDir = snap_direction;
13912
13913   if (player->MovPos == 0)
13914   {
13915     player->is_moving = FALSE;
13916     player->is_digging = FALSE;
13917     player->is_collecting = FALSE;
13918   }
13919
13920   player->is_dropping = FALSE;
13921   player->is_dropping_pressed = FALSE;
13922   player->drop_pressed_delay = 0;
13923
13924   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13925     return FALSE;
13926
13927   player->is_snapping = TRUE;
13928   player->is_active = TRUE;
13929
13930   if (player->MovPos == 0)
13931   {
13932     player->is_moving = FALSE;
13933     player->is_digging = FALSE;
13934     player->is_collecting = FALSE;
13935   }
13936
13937   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13938     TEST_DrawLevelField(player->last_jx, player->last_jy);
13939
13940   TEST_DrawLevelField(x, y);
13941
13942   return TRUE;
13943 }
13944
13945 static boolean DropElement(struct PlayerInfo *player)
13946 {
13947   int old_element, new_element;
13948   int dropx = player->jx, dropy = player->jy;
13949   int drop_direction = player->MovDir;
13950   int drop_side = drop_direction;
13951   int drop_element = get_next_dropped_element(player);
13952
13953   player->is_dropping_pressed = TRUE;
13954
13955   /* do not drop an element on top of another element; when holding drop key
13956      pressed without moving, dropped element must move away before the next
13957      element can be dropped (this is especially important if the next element
13958      is dynamite, which can be placed on background for historical reasons) */
13959   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13960     return MP_ACTION;
13961
13962   if (IS_THROWABLE(drop_element))
13963   {
13964     dropx += GET_DX_FROM_DIR(drop_direction);
13965     dropy += GET_DY_FROM_DIR(drop_direction);
13966
13967     if (!IN_LEV_FIELD(dropx, dropy))
13968       return FALSE;
13969   }
13970
13971   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13972   new_element = drop_element;           /* default: no change when dropping */
13973
13974   /* check if player is active, not moving and ready to drop */
13975   if (!player->active || player->MovPos || player->drop_delay > 0)
13976     return FALSE;
13977
13978   /* check if player has anything that can be dropped */
13979   if (new_element == EL_UNDEFINED)
13980     return FALSE;
13981
13982   /* check if drop key was pressed long enough for EM style dynamite */
13983   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13984     return FALSE;
13985
13986   /* check if anything can be dropped at the current position */
13987   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13988     return FALSE;
13989
13990   /* collected custom elements can only be dropped on empty fields */
13991   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13992     return FALSE;
13993
13994   if (old_element != EL_EMPTY)
13995     Back[dropx][dropy] = old_element;   /* store old element on this field */
13996
13997   ResetGfxAnimation(dropx, dropy);
13998   ResetRandomAnimationValue(dropx, dropy);
13999
14000   if (player->inventory_size > 0 ||
14001       player->inventory_infinite_element != EL_UNDEFINED)
14002   {
14003     if (player->inventory_size > 0)
14004     {
14005       player->inventory_size--;
14006
14007       DrawGameDoorValues();
14008
14009       if (new_element == EL_DYNAMITE)
14010         new_element = EL_DYNAMITE_ACTIVE;
14011       else if (new_element == EL_EM_DYNAMITE)
14012         new_element = EL_EM_DYNAMITE_ACTIVE;
14013       else if (new_element == EL_SP_DISK_RED)
14014         new_element = EL_SP_DISK_RED_ACTIVE;
14015     }
14016
14017     Feld[dropx][dropy] = new_element;
14018
14019     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14020       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14021                           el2img(Feld[dropx][dropy]), 0);
14022
14023     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14024
14025     /* needed if previous element just changed to "empty" in the last frame */
14026     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14027
14028     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14029                                player->index_bit, drop_side);
14030     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14031                                         CE_PLAYER_DROPS_X,
14032                                         player->index_bit, drop_side);
14033
14034     TestIfElementTouchesCustomElement(dropx, dropy);
14035   }
14036   else          /* player is dropping a dyna bomb */
14037   {
14038     player->dynabombs_left--;
14039
14040     Feld[dropx][dropy] = new_element;
14041
14042     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14043       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14044                           el2img(Feld[dropx][dropy]), 0);
14045
14046     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14047   }
14048
14049   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14050     InitField_WithBug1(dropx, dropy, FALSE);
14051
14052   new_element = Feld[dropx][dropy];     /* element might have changed */
14053
14054   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14055       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14056   {
14057     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14058       MovDir[dropx][dropy] = drop_direction;
14059
14060     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14061
14062     /* do not cause impact style collision by dropping elements that can fall */
14063     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14064   }
14065
14066   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14067   player->is_dropping = TRUE;
14068
14069   player->drop_pressed_delay = 0;
14070   player->is_dropping_pressed = FALSE;
14071
14072   player->drop_x = dropx;
14073   player->drop_y = dropy;
14074
14075   return TRUE;
14076 }
14077
14078 /* ------------------------------------------------------------------------- */
14079 /* game sound playing functions                                              */
14080 /* ------------------------------------------------------------------------- */
14081
14082 static int *loop_sound_frame = NULL;
14083 static int *loop_sound_volume = NULL;
14084
14085 void InitPlayLevelSound()
14086 {
14087   int num_sounds = getSoundListSize();
14088
14089   checked_free(loop_sound_frame);
14090   checked_free(loop_sound_volume);
14091
14092   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14093   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14094 }
14095
14096 static void PlayLevelSound(int x, int y, int nr)
14097 {
14098   int sx = SCREENX(x), sy = SCREENY(y);
14099   int volume, stereo_position;
14100   int max_distance = 8;
14101   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14102
14103   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14104       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14105     return;
14106
14107   if (!IN_LEV_FIELD(x, y) ||
14108       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14109       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14110     return;
14111
14112   volume = SOUND_MAX_VOLUME;
14113
14114   if (!IN_SCR_FIELD(sx, sy))
14115   {
14116     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14117     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14118
14119     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14120   }
14121
14122   stereo_position = (SOUND_MAX_LEFT +
14123                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14124                      (SCR_FIELDX + 2 * max_distance));
14125
14126   if (IS_LOOP_SOUND(nr))
14127   {
14128     /* This assures that quieter loop sounds do not overwrite louder ones,
14129        while restarting sound volume comparison with each new game frame. */
14130
14131     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14132       return;
14133
14134     loop_sound_volume[nr] = volume;
14135     loop_sound_frame[nr] = FrameCounter;
14136   }
14137
14138   PlaySoundExt(nr, volume, stereo_position, type);
14139 }
14140
14141 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14142 {
14143   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14144                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14145                  y < LEVELY(BY1) ? LEVELY(BY1) :
14146                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14147                  sound_action);
14148 }
14149
14150 static void PlayLevelSoundAction(int x, int y, int action)
14151 {
14152   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14153 }
14154
14155 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14156 {
14157   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14158
14159   if (sound_effect != SND_UNDEFINED)
14160     PlayLevelSound(x, y, sound_effect);
14161 }
14162
14163 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14164                                               int action)
14165 {
14166   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14167
14168   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14169     PlayLevelSound(x, y, sound_effect);
14170 }
14171
14172 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14173 {
14174   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14175
14176   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14177     PlayLevelSound(x, y, sound_effect);
14178 }
14179
14180 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14181 {
14182   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14183
14184   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14185     StopSound(sound_effect);
14186 }
14187
14188 static void PlayLevelMusic()
14189 {
14190   if (levelset.music[level_nr] != MUS_UNDEFINED)
14191     PlayMusic(levelset.music[level_nr]);        /* from config file */
14192   else
14193     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14194 }
14195
14196 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14197 {
14198   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14199   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14200   int x = xx - 1 - offset;
14201   int y = yy - 1 - offset;
14202
14203   switch (sample)
14204   {
14205     case SAMPLE_blank:
14206       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14207       break;
14208
14209     case SAMPLE_roll:
14210       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14211       break;
14212
14213     case SAMPLE_stone:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14215       break;
14216
14217     case SAMPLE_nut:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14219       break;
14220
14221     case SAMPLE_crack:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14223       break;
14224
14225     case SAMPLE_bug:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14227       break;
14228
14229     case SAMPLE_tank:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14231       break;
14232
14233     case SAMPLE_android_clone:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14235       break;
14236
14237     case SAMPLE_android_move:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14239       break;
14240
14241     case SAMPLE_spring:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14243       break;
14244
14245     case SAMPLE_slurp:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14247       break;
14248
14249     case SAMPLE_eater:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14251       break;
14252
14253     case SAMPLE_eater_eat:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14255       break;
14256
14257     case SAMPLE_alien:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14259       break;
14260
14261     case SAMPLE_collect:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14263       break;
14264
14265     case SAMPLE_diamond:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14267       break;
14268
14269     case SAMPLE_squash:
14270       /* !!! CHECK THIS !!! */
14271 #if 1
14272       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14273 #else
14274       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14275 #endif
14276       break;
14277
14278     case SAMPLE_wonderfall:
14279       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14280       break;
14281
14282     case SAMPLE_drip:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14284       break;
14285
14286     case SAMPLE_push:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14288       break;
14289
14290     case SAMPLE_dirt:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14292       break;
14293
14294     case SAMPLE_acid:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14296       break;
14297
14298     case SAMPLE_ball:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14300       break;
14301
14302     case SAMPLE_grow:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14304       break;
14305
14306     case SAMPLE_wonder:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14308       break;
14309
14310     case SAMPLE_door:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14312       break;
14313
14314     case SAMPLE_exit_open:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14316       break;
14317
14318     case SAMPLE_exit_leave:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14320       break;
14321
14322     case SAMPLE_dynamite:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14324       break;
14325
14326     case SAMPLE_tick:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14328       break;
14329
14330     case SAMPLE_press:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14332       break;
14333
14334     case SAMPLE_wheel:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14336       break;
14337
14338     case SAMPLE_boom:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14340       break;
14341
14342     case SAMPLE_die:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14344       break;
14345
14346     case SAMPLE_time:
14347       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14348       break;
14349
14350     default:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14352       break;
14353   }
14354 }
14355
14356 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14357 {
14358   int element = map_element_SP_to_RND(element_sp);
14359   int action = map_action_SP_to_RND(action_sp);
14360   int offset = (setup.sp_show_border_elements ? 0 : 1);
14361   int x = xx - offset;
14362   int y = yy - offset;
14363
14364   PlayLevelSoundElementAction(x, y, element, action);
14365 }
14366
14367 void RaiseScore(int value)
14368 {
14369   local_player->score += value;
14370
14371   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14372
14373   DisplayGameControlValues();
14374 }
14375
14376 void RaiseScoreElement(int element)
14377 {
14378   switch (element)
14379   {
14380     case EL_EMERALD:
14381     case EL_BD_DIAMOND:
14382     case EL_EMERALD_YELLOW:
14383     case EL_EMERALD_RED:
14384     case EL_EMERALD_PURPLE:
14385     case EL_SP_INFOTRON:
14386       RaiseScore(level.score[SC_EMERALD]);
14387       break;
14388     case EL_DIAMOND:
14389       RaiseScore(level.score[SC_DIAMOND]);
14390       break;
14391     case EL_CRYSTAL:
14392       RaiseScore(level.score[SC_CRYSTAL]);
14393       break;
14394     case EL_PEARL:
14395       RaiseScore(level.score[SC_PEARL]);
14396       break;
14397     case EL_BUG:
14398     case EL_BD_BUTTERFLY:
14399     case EL_SP_ELECTRON:
14400       RaiseScore(level.score[SC_BUG]);
14401       break;
14402     case EL_SPACESHIP:
14403     case EL_BD_FIREFLY:
14404     case EL_SP_SNIKSNAK:
14405       RaiseScore(level.score[SC_SPACESHIP]);
14406       break;
14407     case EL_YAMYAM:
14408     case EL_DARK_YAMYAM:
14409       RaiseScore(level.score[SC_YAMYAM]);
14410       break;
14411     case EL_ROBOT:
14412       RaiseScore(level.score[SC_ROBOT]);
14413       break;
14414     case EL_PACMAN:
14415       RaiseScore(level.score[SC_PACMAN]);
14416       break;
14417     case EL_NUT:
14418       RaiseScore(level.score[SC_NUT]);
14419       break;
14420     case EL_DYNAMITE:
14421     case EL_EM_DYNAMITE:
14422     case EL_SP_DISK_RED:
14423     case EL_DYNABOMB_INCREASE_NUMBER:
14424     case EL_DYNABOMB_INCREASE_SIZE:
14425     case EL_DYNABOMB_INCREASE_POWER:
14426       RaiseScore(level.score[SC_DYNAMITE]);
14427       break;
14428     case EL_SHIELD_NORMAL:
14429     case EL_SHIELD_DEADLY:
14430       RaiseScore(level.score[SC_SHIELD]);
14431       break;
14432     case EL_EXTRA_TIME:
14433       RaiseScore(level.extra_time_score);
14434       break;
14435     case EL_KEY_1:
14436     case EL_KEY_2:
14437     case EL_KEY_3:
14438     case EL_KEY_4:
14439     case EL_EM_KEY_1:
14440     case EL_EM_KEY_2:
14441     case EL_EM_KEY_3:
14442     case EL_EM_KEY_4:
14443     case EL_EMC_KEY_5:
14444     case EL_EMC_KEY_6:
14445     case EL_EMC_KEY_7:
14446     case EL_EMC_KEY_8:
14447     case EL_DC_KEY_WHITE:
14448       RaiseScore(level.score[SC_KEY]);
14449       break;
14450     default:
14451       RaiseScore(element_info[element].collect_score);
14452       break;
14453   }
14454 }
14455
14456 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14457 {
14458   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14459   {
14460     /* closing door required in case of envelope style request dialogs */
14461     if (!skip_request)
14462       CloseDoor(DOOR_CLOSE_1);
14463
14464 #if defined(NETWORK_AVALIABLE)
14465     if (options.network)
14466       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14467     else
14468 #endif
14469     {
14470       if (quick_quit)
14471       {
14472         FadeSkipNextFadeIn();
14473
14474         game_status = GAME_MODE_MAIN;
14475
14476         DrawMainMenu();
14477       }
14478       else
14479       {
14480         game_status = GAME_MODE_MAIN;
14481
14482         DrawMainMenu();
14483       }
14484     }
14485   }
14486   else          /* continue playing the game */
14487   {
14488     if (tape.playing && tape.deactivate_display)
14489       TapeDeactivateDisplayOff(TRUE);
14490
14491     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14492
14493     if (tape.playing && tape.deactivate_display)
14494       TapeDeactivateDisplayOn();
14495   }
14496 }
14497
14498 void RequestQuitGame(boolean ask_if_really_quit)
14499 {
14500   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14501   boolean skip_request = AllPlayersGone || quick_quit;
14502
14503   RequestQuitGameExt(skip_request, quick_quit,
14504                      "Do you really want to quit the game?");
14505 }
14506
14507
14508 /* ------------------------------------------------------------------------- */
14509 /* random generator functions                                                */
14510 /* ------------------------------------------------------------------------- */
14511
14512 unsigned int InitEngineRandom_RND(int seed)
14513 {
14514   game.num_random_calls = 0;
14515
14516   return InitEngineRandom(seed);
14517 }
14518
14519 unsigned int RND(int max)
14520 {
14521   if (max > 0)
14522   {
14523     game.num_random_calls++;
14524
14525     return GetEngineRandom(max);
14526   }
14527
14528   return 0;
14529 }
14530
14531
14532 /* ------------------------------------------------------------------------- */
14533 /* game engine snapshot handling functions                                   */
14534 /* ------------------------------------------------------------------------- */
14535
14536 struct EngineSnapshotInfo
14537 {
14538   /* runtime values for custom element collect score */
14539   int collect_score[NUM_CUSTOM_ELEMENTS];
14540
14541   /* runtime values for group element choice position */
14542   int choice_pos[NUM_GROUP_ELEMENTS];
14543
14544   /* runtime values for belt position animations */
14545   int belt_graphic[4][NUM_BELT_PARTS];
14546   int belt_anim_mode[4][NUM_BELT_PARTS];
14547 };
14548
14549 static struct EngineSnapshotInfo engine_snapshot_rnd;
14550 static char *snapshot_level_identifier = NULL;
14551 static int snapshot_level_nr = -1;
14552
14553 static void SaveEngineSnapshotValues_RND()
14554 {
14555   static int belt_base_active_element[4] =
14556   {
14557     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14558     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14559     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14560     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14561   };
14562   int i, j;
14563
14564   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14565   {
14566     int element = EL_CUSTOM_START + i;
14567
14568     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14569   }
14570
14571   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14572   {
14573     int element = EL_GROUP_START + i;
14574
14575     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14576   }
14577
14578   for (i = 0; i < 4; i++)
14579   {
14580     for (j = 0; j < NUM_BELT_PARTS; j++)
14581     {
14582       int element = belt_base_active_element[i] + j;
14583       int graphic = el2img(element);
14584       int anim_mode = graphic_info[graphic].anim_mode;
14585
14586       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14587       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14588     }
14589   }
14590 }
14591
14592 static void LoadEngineSnapshotValues_RND()
14593 {
14594   unsigned int num_random_calls = game.num_random_calls;
14595   int i, j;
14596
14597   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14598   {
14599     int element = EL_CUSTOM_START + i;
14600
14601     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14602   }
14603
14604   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14605   {
14606     int element = EL_GROUP_START + i;
14607
14608     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14609   }
14610
14611   for (i = 0; i < 4; i++)
14612   {
14613     for (j = 0; j < NUM_BELT_PARTS; j++)
14614     {
14615       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14616       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14617
14618       graphic_info[graphic].anim_mode = anim_mode;
14619     }
14620   }
14621
14622   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14623   {
14624     InitRND(tape.random_seed);
14625     for (i = 0; i < num_random_calls; i++)
14626       RND(1);
14627   }
14628
14629   if (game.num_random_calls != num_random_calls)
14630   {
14631     Error(ERR_INFO, "number of random calls out of sync");
14632     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14633     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14634     Error(ERR_EXIT, "this should not happen -- please debug");
14635   }
14636 }
14637
14638 void FreeEngineSnapshotSingle()
14639 {
14640   FreeSnapshotSingle();
14641
14642   setString(&snapshot_level_identifier, NULL);
14643   snapshot_level_nr = -1;
14644 }
14645
14646 void FreeEngineSnapshotList()
14647 {
14648   FreeSnapshotList();
14649 }
14650
14651 ListNode *SaveEngineSnapshotBuffers()
14652 {
14653   ListNode *buffers = NULL;
14654
14655   /* copy some special values to a structure better suited for the snapshot */
14656
14657   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14658     SaveEngineSnapshotValues_RND();
14659   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14660     SaveEngineSnapshotValues_EM();
14661   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14662     SaveEngineSnapshotValues_SP(&buffers);
14663
14664   /* save values stored in special snapshot structure */
14665
14666   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14667     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14668   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14669     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14670   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14671     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14672
14673   /* save further RND engine values */
14674
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14678
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14683
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14689
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14693
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14695
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14697
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14700
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14719
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14722
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14726
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14729
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14735
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14738
14739 #if 0
14740   ListNode *node = engine_snapshot_list_rnd;
14741   int num_bytes = 0;
14742
14743   while (node != NULL)
14744   {
14745     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14746
14747     node = node->next;
14748   }
14749
14750   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14751 #endif
14752
14753   return buffers;
14754 }
14755
14756 void SaveEngineSnapshotSingle()
14757 {
14758   ListNode *buffers = SaveEngineSnapshotBuffers();
14759
14760   /* finally save all snapshot buffers to single snapshot */
14761   SaveSnapshotSingle(buffers);
14762
14763   /* save level identification information */
14764   setString(&snapshot_level_identifier, leveldir_current->identifier);
14765   snapshot_level_nr = level_nr;
14766 }
14767
14768 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14769 {
14770   boolean save_snapshot =
14771     (initial_snapshot ||
14772      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14773      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14774       game.snapshot.changed_action));
14775
14776   game.snapshot.changed_action = FALSE;
14777
14778   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14779       tape.quick_resume ||
14780       !save_snapshot)
14781     return FALSE;
14782
14783   ListNode *buffers = SaveEngineSnapshotBuffers();
14784
14785   /* finally save all snapshot buffers to snapshot list */
14786   SaveSnapshotToList(buffers);
14787
14788   return TRUE;
14789 }
14790
14791 boolean SaveEngineSnapshotToList()
14792 {
14793   return SaveEngineSnapshotToListExt(FALSE);
14794 }
14795
14796 void SaveEngineSnapshotToListInitial()
14797 {
14798   FreeEngineSnapshotList();
14799
14800   SaveEngineSnapshotToListExt(TRUE);
14801 }
14802
14803 void LoadEngineSnapshotValues()
14804 {
14805   /* restore special values from snapshot structure */
14806
14807   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14808     LoadEngineSnapshotValues_RND();
14809   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14810     LoadEngineSnapshotValues_EM();
14811   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14812     LoadEngineSnapshotValues_SP();
14813 }
14814
14815 void LoadEngineSnapshotSingle()
14816 {
14817   LoadSnapshotSingle();
14818
14819   LoadEngineSnapshotValues();
14820 }
14821
14822 void LoadEngineSnapshot_Undo(int steps)
14823 {
14824   LoadSnapshotFromList_Older(steps);
14825
14826   LoadEngineSnapshotValues();
14827 }
14828
14829 void LoadEngineSnapshot_Redo(int steps)
14830 {
14831   LoadSnapshotFromList_Newer(steps);
14832
14833   LoadEngineSnapshotValues();
14834 }
14835
14836 boolean CheckEngineSnapshotSingle()
14837 {
14838   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14839           snapshot_level_nr == level_nr);
14840 }
14841
14842 boolean CheckEngineSnapshotList()
14843 {
14844   return CheckSnapshotList();
14845 }
14846
14847
14848 /* ---------- new game button stuff ---------------------------------------- */
14849
14850 static struct
14851 {
14852   int graphic;
14853   struct XY *pos;
14854   int gadget_id;
14855   char *infotext;
14856 } gamebutton_info[NUM_GAME_BUTTONS] =
14857 {
14858   {
14859     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14860     GAME_CTRL_ID_STOP,                  "stop game"
14861   },
14862   {
14863     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14864     GAME_CTRL_ID_PAUSE,                 "pause game"
14865   },
14866   {
14867     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14868     GAME_CTRL_ID_PLAY,                  "play game"
14869   },
14870   {
14871     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14872     GAME_CTRL_ID_UNDO,                  "undo step"
14873   },
14874   {
14875     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14876     GAME_CTRL_ID_REDO,                  "redo step"
14877   },
14878   {
14879     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14880     GAME_CTRL_ID_SAVE,                  "save game"
14881   },
14882   {
14883     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14884     GAME_CTRL_ID_PAUSE2,                "pause game"
14885   },
14886   {
14887     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14888     GAME_CTRL_ID_LOAD,                  "load game"
14889   },
14890   {
14891     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14892     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14893   },
14894   {
14895     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14896     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14897   },
14898   {
14899     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14900     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14901   }
14902 };
14903
14904 void CreateGameButtons()
14905 {
14906   int i;
14907
14908   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14909   {
14910     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14911     struct XY *pos = gamebutton_info[i].pos;
14912     struct GadgetInfo *gi;
14913     int button_type;
14914     boolean checked;
14915     unsigned int event_mask;
14916     int base_x = (tape.show_game_buttons ? VX : DX);
14917     int base_y = (tape.show_game_buttons ? VY : DY);
14918     int gd_x   = gfx->src_x;
14919     int gd_y   = gfx->src_y;
14920     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14921     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14922     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14923     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14924     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14925     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14926     int id = i;
14927
14928     if (gfx->bitmap == NULL)
14929     {
14930       game_gadget[id] = NULL;
14931
14932       continue;
14933     }
14934
14935     if (id == GAME_CTRL_ID_STOP ||
14936         id == GAME_CTRL_ID_PLAY ||
14937         id == GAME_CTRL_ID_SAVE ||
14938         id == GAME_CTRL_ID_LOAD)
14939     {
14940       button_type = GD_TYPE_NORMAL_BUTTON;
14941       checked = FALSE;
14942       event_mask = GD_EVENT_RELEASED;
14943     }
14944     else if (id == GAME_CTRL_ID_UNDO ||
14945              id == GAME_CTRL_ID_REDO)
14946     {
14947       button_type = GD_TYPE_NORMAL_BUTTON;
14948       checked = FALSE;
14949       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14950     }
14951     else
14952     {
14953       button_type = GD_TYPE_CHECK_BUTTON;
14954       checked =
14955         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14956          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14957          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14958       event_mask = GD_EVENT_PRESSED;
14959     }
14960
14961     gi = CreateGadget(GDI_CUSTOM_ID, id,
14962                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14963                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14964                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14965                       GDI_WIDTH, gfx->width,
14966                       GDI_HEIGHT, gfx->height,
14967                       GDI_TYPE, button_type,
14968                       GDI_STATE, GD_BUTTON_UNPRESSED,
14969                       GDI_CHECKED, checked,
14970                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14971                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14972                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14973                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14974                       GDI_DIRECT_DRAW, FALSE,
14975                       GDI_EVENT_MASK, event_mask,
14976                       GDI_CALLBACK_ACTION, HandleGameButtons,
14977                       GDI_END);
14978
14979     if (gi == NULL)
14980       Error(ERR_EXIT, "cannot create gadget");
14981
14982     game_gadget[id] = gi;
14983   }
14984 }
14985
14986 void FreeGameButtons()
14987 {
14988   int i;
14989
14990   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14991     FreeGadget(game_gadget[i]);
14992 }
14993
14994 static void MapGameButtonsAtSamePosition(int id)
14995 {
14996   int i;
14997
14998   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14999     if (i != id &&
15000         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15001         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15002       MapGadget(game_gadget[i]);
15003 }
15004
15005 static void UnmapGameButtonsAtSamePosition(int id)
15006 {
15007   int i;
15008
15009   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15010     if (i != id &&
15011         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15012         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15013       UnmapGadget(game_gadget[i]);
15014 }
15015
15016 void MapUndoRedoButtons()
15017 {
15018   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15019   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15020
15021   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15022   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15023 }
15024
15025 void UnmapUndoRedoButtons()
15026 {
15027   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15028   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15029
15030   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15031   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15032 }
15033
15034 void MapGameButtons()
15035 {
15036   int i;
15037
15038   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15039     if (i != GAME_CTRL_ID_UNDO &&
15040         i != GAME_CTRL_ID_REDO)
15041       MapGadget(game_gadget[i]);
15042
15043   if (setup.show_snapshot_buttons)
15044   {
15045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15047     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15048   }
15049   else
15050   {
15051     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15052     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15053     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15054   }
15055
15056   RedrawGameButtons();
15057 }
15058
15059 void UnmapGameButtons()
15060 {
15061   int i;
15062
15063   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15064     UnmapGadget(game_gadget[i]);
15065 }
15066
15067 void RedrawGameButtons()
15068 {
15069   int i;
15070
15071   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15072     RedrawGadget(game_gadget[i]);
15073
15074   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15075   redraw_mask &= ~REDRAW_ALL;
15076 }
15077
15078 void GameUndoRedoExt()
15079 {
15080   ClearPlayerAction();
15081
15082   tape.pausing = TRUE;
15083
15084   RedrawPlayfield();
15085   UpdateAndDisplayGameControlValues();
15086
15087   DrawCompleteVideoDisplay();
15088   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15089   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15090   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15091                     VIDEO_STATE_1STEP_OFF), 0);
15092
15093   BackToFront();
15094 }
15095
15096 void GameUndo(int steps)
15097 {
15098   if (!CheckEngineSnapshotList())
15099     return;
15100
15101   LoadEngineSnapshot_Undo(steps);
15102
15103   GameUndoRedoExt();
15104 }
15105
15106 void GameRedo(int steps)
15107 {
15108   if (!CheckEngineSnapshotList())
15109     return;
15110
15111   LoadEngineSnapshot_Redo(steps);
15112
15113   GameUndoRedoExt();
15114 }
15115
15116 static void HandleGameButtonsExt(int id, int button)
15117 {
15118   int steps = BUTTON_STEPSIZE(button);
15119   boolean handle_game_buttons =
15120     (game_status == GAME_MODE_PLAYING ||
15121      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15122
15123   if (!handle_game_buttons)
15124     return;
15125
15126   switch (id)
15127   {
15128     case GAME_CTRL_ID_STOP:
15129       if (game_status == GAME_MODE_MAIN)
15130         break;
15131
15132       if (tape.playing)
15133         TapeStop();
15134       else
15135         RequestQuitGame(TRUE);
15136
15137       break;
15138
15139     case GAME_CTRL_ID_PAUSE:
15140     case GAME_CTRL_ID_PAUSE2:
15141       if (options.network && game_status == GAME_MODE_PLAYING)
15142       {
15143 #if defined(NETWORK_AVALIABLE)
15144         if (tape.pausing)
15145           SendToServer_ContinuePlaying();
15146         else
15147           SendToServer_PausePlaying();
15148 #endif
15149       }
15150       else
15151         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15152       break;
15153
15154     case GAME_CTRL_ID_PLAY:
15155       if (game_status == GAME_MODE_MAIN)
15156       {
15157         StartGameActions(options.network, setup.autorecord, level.random_seed);
15158       }
15159       else if (tape.pausing)
15160       {
15161 #if defined(NETWORK_AVALIABLE)
15162         if (options.network)
15163           SendToServer_ContinuePlaying();
15164         else
15165 #endif
15166           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15167       }
15168       break;
15169
15170     case GAME_CTRL_ID_UNDO:
15171       GameUndo(steps);
15172       break;
15173
15174     case GAME_CTRL_ID_REDO:
15175       GameRedo(steps);
15176       break;
15177
15178     case GAME_CTRL_ID_SAVE:
15179       TapeQuickSave();
15180       break;
15181
15182     case GAME_CTRL_ID_LOAD:
15183       TapeQuickLoad();
15184       break;
15185
15186     case SOUND_CTRL_ID_MUSIC:
15187       if (setup.sound_music)
15188       { 
15189         setup.sound_music = FALSE;
15190
15191         FadeMusic();
15192       }
15193       else if (audio.music_available)
15194       { 
15195         setup.sound = setup.sound_music = TRUE;
15196
15197         SetAudioMode(setup.sound);
15198
15199         PlayLevelMusic();
15200       }
15201       break;
15202
15203     case SOUND_CTRL_ID_LOOPS:
15204       if (setup.sound_loops)
15205         setup.sound_loops = FALSE;
15206       else if (audio.loops_available)
15207       {
15208         setup.sound = setup.sound_loops = TRUE;
15209
15210         SetAudioMode(setup.sound);
15211       }
15212       break;
15213
15214     case SOUND_CTRL_ID_SIMPLE:
15215       if (setup.sound_simple)
15216         setup.sound_simple = FALSE;
15217       else if (audio.sound_available)
15218       {
15219         setup.sound = setup.sound_simple = TRUE;
15220
15221         SetAudioMode(setup.sound);
15222       }
15223       break;
15224
15225     default:
15226       break;
15227   }
15228 }
15229
15230 static void HandleGameButtons(struct GadgetInfo *gi)
15231 {
15232   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15233 }
15234
15235 void HandleSoundButtonKeys(Key key)
15236 {
15237
15238   if (key == setup.shortcut.sound_simple)
15239     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15240   else if (key == setup.shortcut.sound_loops)
15241     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15242   else if (key == setup.shortcut.sound_music)
15243     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15244 }