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