cleanup of redraw masks
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   /* ---------- initialize game engine snapshots ---------------------------- */
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     game.snapshot.last_action[i] = 0;
3041   game.snapshot.changed_action = FALSE;
3042   game.snapshot.mode =
3043     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3044      SNAPSHOT_MODE_EVERY_STEP :
3045      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3046      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3047 }
3048
3049 int get_num_special_action(int element, int action_first, int action_last)
3050 {
3051   int num_special_action = 0;
3052   int i, j;
3053
3054   for (i = action_first; i <= action_last; i++)
3055   {
3056     boolean found = FALSE;
3057
3058     for (j = 0; j < NUM_DIRECTIONS; j++)
3059       if (el_act_dir2img(element, i, j) !=
3060           el_act_dir2img(element, ACTION_DEFAULT, j))
3061         found = TRUE;
3062
3063     if (found)
3064       num_special_action++;
3065     else
3066       break;
3067   }
3068
3069   return num_special_action;
3070 }
3071
3072
3073 /*
3074   =============================================================================
3075   InitGame()
3076   -----------------------------------------------------------------------------
3077   initialize and start new game
3078   =============================================================================
3079 */
3080
3081 void InitGame()
3082 {
3083   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3084   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3085
3086   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3087   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3088   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3089   int initial_move_dir = MV_DOWN;
3090   int i, j, x, y;
3091
3092   game_status = GAME_MODE_PLAYING;
3093
3094   StopAnimation();
3095
3096   if (!game.restart_level)
3097     CloseDoor(DOOR_CLOSE_1);
3098
3099   if (level_editor_test_game)
3100     FadeSkipNextFadeIn();
3101   else
3102     FadeSetEnterScreen();
3103
3104   FadeOut(REDRAW_FIELD);
3105
3106   /* needed if different viewport properties defined for playing */
3107   ChangeViewportPropertiesIfNeeded();
3108
3109   DrawCompleteVideoDisplay();
3110
3111   InitGameEngine();
3112   InitGameControlValues();
3113
3114   /* don't play tapes over network */
3115   network_playing = (options.network && !tape.playing);
3116
3117   for (i = 0; i < MAX_PLAYERS; i++)
3118   {
3119     struct PlayerInfo *player = &stored_player[i];
3120
3121     player->index_nr = i;
3122     player->index_bit = (1 << i);
3123     player->element_nr = EL_PLAYER_1 + i;
3124
3125     player->present = FALSE;
3126     player->active = FALSE;
3127     player->mapped = FALSE;
3128
3129     player->killed = FALSE;
3130     player->reanimated = FALSE;
3131
3132     player->action = 0;
3133     player->effective_action = 0;
3134     player->programmed_action = 0;
3135
3136     player->score = 0;
3137     player->score_final = 0;
3138
3139     player->gems_still_needed = level.gems_needed;
3140     player->sokobanfields_still_needed = 0;
3141     player->lights_still_needed = 0;
3142     player->friends_still_needed = 0;
3143
3144     for (j = 0; j < MAX_NUM_KEYS; j++)
3145       player->key[j] = FALSE;
3146
3147     player->num_white_keys = 0;
3148
3149     player->dynabomb_count = 0;
3150     player->dynabomb_size = 1;
3151     player->dynabombs_left = 0;
3152     player->dynabomb_xl = FALSE;
3153
3154     player->MovDir = initial_move_dir;
3155     player->MovPos = 0;
3156     player->GfxPos = 0;
3157     player->GfxDir = initial_move_dir;
3158     player->GfxAction = ACTION_DEFAULT;
3159     player->Frame = 0;
3160     player->StepFrame = 0;
3161
3162     player->initial_element = player->element_nr;
3163     player->artwork_element =
3164       (level.use_artwork_element[i] ? level.artwork_element[i] :
3165        player->element_nr);
3166     player->use_murphy = FALSE;
3167
3168     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3169     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3170
3171     player->gravity = level.initial_player_gravity[i];
3172
3173     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3174
3175     player->actual_frame_counter = 0;
3176
3177     player->step_counter = 0;
3178
3179     player->last_move_dir = initial_move_dir;
3180
3181     player->is_active = FALSE;
3182
3183     player->is_waiting = FALSE;
3184     player->is_moving = FALSE;
3185     player->is_auto_moving = FALSE;
3186     player->is_digging = FALSE;
3187     player->is_snapping = FALSE;
3188     player->is_collecting = FALSE;
3189     player->is_pushing = FALSE;
3190     player->is_switching = FALSE;
3191     player->is_dropping = FALSE;
3192     player->is_dropping_pressed = FALSE;
3193
3194     player->is_bored = FALSE;
3195     player->is_sleeping = FALSE;
3196
3197     player->frame_counter_bored = -1;
3198     player->frame_counter_sleeping = -1;
3199
3200     player->anim_delay_counter = 0;
3201     player->post_delay_counter = 0;
3202
3203     player->dir_waiting = initial_move_dir;
3204     player->action_waiting = ACTION_DEFAULT;
3205     player->last_action_waiting = ACTION_DEFAULT;
3206     player->special_action_bored = ACTION_DEFAULT;
3207     player->special_action_sleeping = ACTION_DEFAULT;
3208
3209     player->switch_x = -1;
3210     player->switch_y = -1;
3211
3212     player->drop_x = -1;
3213     player->drop_y = -1;
3214
3215     player->show_envelope = 0;
3216
3217     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3218
3219     player->push_delay       = -1;      /* initialized when pushing starts */
3220     player->push_delay_value = game.initial_push_delay_value;
3221
3222     player->drop_delay = 0;
3223     player->drop_pressed_delay = 0;
3224
3225     player->last_jx = -1;
3226     player->last_jy = -1;
3227     player->jx = -1;
3228     player->jy = -1;
3229
3230     player->shield_normal_time_left = 0;
3231     player->shield_deadly_time_left = 0;
3232
3233     player->inventory_infinite_element = EL_UNDEFINED;
3234     player->inventory_size = 0;
3235
3236     if (level.use_initial_inventory[i])
3237     {
3238       for (j = 0; j < level.initial_inventory_size[i]; j++)
3239       {
3240         int element = level.initial_inventory_content[i][j];
3241         int collect_count = element_info[element].collect_count_initial;
3242         int k;
3243
3244         if (!IS_CUSTOM_ELEMENT(element))
3245           collect_count = 1;
3246
3247         if (collect_count == 0)
3248           player->inventory_infinite_element = element;
3249         else
3250           for (k = 0; k < collect_count; k++)
3251             if (player->inventory_size < MAX_INVENTORY_SIZE)
3252               player->inventory_element[player->inventory_size++] = element;
3253       }
3254     }
3255
3256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3257     SnapField(player, 0, 0);
3258
3259     player->LevelSolved = FALSE;
3260     player->GameOver = FALSE;
3261
3262     player->LevelSolved_GameWon = FALSE;
3263     player->LevelSolved_GameEnd = FALSE;
3264     player->LevelSolved_PanelOff = FALSE;
3265     player->LevelSolved_SaveTape = FALSE;
3266     player->LevelSolved_SaveScore = FALSE;
3267     player->LevelSolved_CountingTime = 0;
3268     player->LevelSolved_CountingScore = 0;
3269
3270     map_player_action[i] = i;
3271   }
3272
3273   network_player_action_received = FALSE;
3274
3275 #if defined(NETWORK_AVALIABLE)
3276   /* initial null action */
3277   if (network_playing)
3278     SendToServer_MovePlayer(MV_NONE);
3279 #endif
3280
3281   ZX = ZY = -1;
3282   ExitX = ExitY = -1;
3283
3284   FrameCounter = 0;
3285   TimeFrames = 0;
3286   TimePlayed = 0;
3287   TimeLeft = level.time;
3288   TapeTime = 0;
3289
3290   ScreenMovDir = MV_NONE;
3291   ScreenMovPos = 0;
3292   ScreenGfxPos = 0;
3293
3294   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3295
3296   AllPlayersGone = FALSE;
3297
3298   game.no_time_limit = (level.time == 0);
3299
3300   game.yamyam_content_nr = 0;
3301   game.robot_wheel_active = FALSE;
3302   game.magic_wall_active = FALSE;
3303   game.magic_wall_time_left = 0;
3304   game.light_time_left = 0;
3305   game.timegate_time_left = 0;
3306   game.switchgate_pos = 0;
3307   game.wind_direction = level.wind_direction_initial;
3308
3309   game.lenses_time_left = 0;
3310   game.magnify_time_left = 0;
3311
3312   game.ball_state = level.ball_state_initial;
3313   game.ball_content_nr = 0;
3314
3315   game.envelope_active = FALSE;
3316
3317   /* set focus to local player for network games, else to all players */
3318   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3319   game.centered_player_nr_next = game.centered_player_nr;
3320   game.set_centered_player = FALSE;
3321
3322   if (network_playing && tape.recording)
3323   {
3324     /* store client dependent player focus when recording network games */
3325     tape.centered_player_nr_next = game.centered_player_nr_next;
3326     tape.set_centered_player = TRUE;
3327   }
3328
3329   for (i = 0; i < NUM_BELTS; i++)
3330   {
3331     game.belt_dir[i] = MV_NONE;
3332     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3333   }
3334
3335   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3336     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3337
3338 #if DEBUG_INIT_PLAYER
3339   if (options.debug)
3340   {
3341     printf("Player status at level initialization:\n");
3342   }
3343 #endif
3344
3345   SCAN_PLAYFIELD(x, y)
3346   {
3347     Feld[x][y] = level.field[x][y];
3348     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3349     ChangeDelay[x][y] = 0;
3350     ChangePage[x][y] = -1;
3351     CustomValue[x][y] = 0;              /* initialized in InitField() */
3352     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3353     AmoebaNr[x][y] = 0;
3354     WasJustMoving[x][y] = 0;
3355     WasJustFalling[x][y] = 0;
3356     CheckCollision[x][y] = 0;
3357     CheckImpact[x][y] = 0;
3358     Stop[x][y] = FALSE;
3359     Pushed[x][y] = FALSE;
3360
3361     ChangeCount[x][y] = 0;
3362     ChangeEvent[x][y] = -1;
3363
3364     ExplodePhase[x][y] = 0;
3365     ExplodeDelay[x][y] = 0;
3366     ExplodeField[x][y] = EX_TYPE_NONE;
3367
3368     RunnerVisit[x][y] = 0;
3369     PlayerVisit[x][y] = 0;
3370
3371     GfxFrame[x][y] = 0;
3372     GfxRandom[x][y] = INIT_GFX_RANDOM();
3373     GfxElement[x][y] = EL_UNDEFINED;
3374     GfxAction[x][y] = ACTION_DEFAULT;
3375     GfxDir[x][y] = MV_NONE;
3376     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3377   }
3378
3379   SCAN_PLAYFIELD(x, y)
3380   {
3381     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3382       emulate_bd = FALSE;
3383     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3384       emulate_sb = FALSE;
3385     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3386       emulate_sp = FALSE;
3387
3388     InitField(x, y, TRUE);
3389
3390     ResetGfxAnimation(x, y);
3391   }
3392
3393   InitBeltMovement();
3394
3395   for (i = 0; i < MAX_PLAYERS; i++)
3396   {
3397     struct PlayerInfo *player = &stored_player[i];
3398
3399     /* set number of special actions for bored and sleeping animation */
3400     player->num_special_action_bored =
3401       get_num_special_action(player->artwork_element,
3402                              ACTION_BORING_1, ACTION_BORING_LAST);
3403     player->num_special_action_sleeping =
3404       get_num_special_action(player->artwork_element,
3405                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3406   }
3407
3408   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3409                     emulate_sb ? EMU_SOKOBAN :
3410                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3411
3412   /* initialize type of slippery elements */
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414   {
3415     if (!IS_CUSTOM_ELEMENT(i))
3416     {
3417       /* default: elements slip down either to the left or right randomly */
3418       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3419
3420       /* SP style elements prefer to slip down on the left side */
3421       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3422         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3423
3424       /* BD style elements prefer to slip down on the left side */
3425       if (game.emulation == EMU_BOULDERDASH)
3426         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3427     }
3428   }
3429
3430   /* initialize explosion and ignition delay */
3431   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432   {
3433     if (!IS_CUSTOM_ELEMENT(i))
3434     {
3435       int num_phase = 8;
3436       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3437                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3438                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3439       int last_phase = (num_phase + 1) * delay;
3440       int half_phase = (num_phase / 2) * delay;
3441
3442       element_info[i].explosion_delay = last_phase - 1;
3443       element_info[i].ignition_delay = half_phase;
3444
3445       if (i == EL_BLACK_ORB)
3446         element_info[i].ignition_delay = 1;
3447     }
3448   }
3449
3450   /* correct non-moving belts to start moving left */
3451   for (i = 0; i < NUM_BELTS; i++)
3452     if (game.belt_dir[i] == MV_NONE)
3453       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3454
3455 #if USE_NEW_PLAYER_ASSIGNMENTS
3456   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3457   /* choose default local player */
3458   local_player = &stored_player[0];
3459
3460   for (i = 0; i < MAX_PLAYERS; i++)
3461     stored_player[i].connected = FALSE;
3462
3463   local_player->connected = TRUE;
3464   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3465
3466   if (tape.playing)
3467   {
3468     for (i = 0; i < MAX_PLAYERS; i++)
3469       stored_player[i].connected = tape.player_participates[i];
3470   }
3471   else if (game.team_mode && !options.network)
3472   {
3473     /* try to guess locally connected team mode players (needed for correct
3474        assignment of player figures from level to locally playing players) */
3475
3476     for (i = 0; i < MAX_PLAYERS; i++)
3477       if (setup.input[i].use_joystick ||
3478           setup.input[i].key.left != KSYM_UNDEFINED)
3479         stored_player[i].connected = TRUE;
3480   }
3481
3482 #if DEBUG_INIT_PLAYER
3483   if (options.debug)
3484   {
3485     printf("Player status after level initialization:\n");
3486
3487     for (i = 0; i < MAX_PLAYERS; i++)
3488     {
3489       struct PlayerInfo *player = &stored_player[i];
3490
3491       printf("- player %d: present == %d, connected == %d, active == %d",
3492              i + 1,
3493              player->present,
3494              player->connected,
3495              player->active);
3496
3497       if (local_player == player)
3498         printf(" (local player)");
3499
3500       printf("\n");
3501     }
3502   }
3503 #endif
3504
3505 #if DEBUG_INIT_PLAYER
3506   if (options.debug)
3507     printf("Reassigning players ...\n");
3508 #endif
3509
3510   /* check if any connected player was not found in playfield */
3511   for (i = 0; i < MAX_PLAYERS; i++)
3512   {
3513     struct PlayerInfo *player = &stored_player[i];
3514
3515     if (player->connected && !player->present)
3516     {
3517       struct PlayerInfo *field_player = NULL;
3518
3519 #if DEBUG_INIT_PLAYER
3520       if (options.debug)
3521         printf("- looking for field player for player %d ...\n", i + 1);
3522 #endif
3523
3524       /* assign first free player found that is present in the playfield */
3525
3526       /* first try: look for unmapped playfield player that is not connected */
3527       for (j = 0; j < MAX_PLAYERS; j++)
3528         if (field_player == NULL &&
3529             stored_player[j].present &&
3530             !stored_player[j].mapped &&
3531             !stored_player[j].connected)
3532           field_player = &stored_player[j];
3533
3534       /* second try: look for *any* unmapped playfield player */
3535       for (j = 0; j < MAX_PLAYERS; j++)
3536         if (field_player == NULL &&
3537             stored_player[j].present &&
3538             !stored_player[j].mapped)
3539           field_player = &stored_player[j];
3540
3541       if (field_player != NULL)
3542       {
3543         int jx = field_player->jx, jy = field_player->jy;
3544
3545 #if DEBUG_INIT_PLAYER
3546         if (options.debug)
3547           printf("- found player %d\n", field_player->index_nr + 1);
3548 #endif
3549
3550         player->present = FALSE;
3551         player->active = FALSE;
3552
3553         field_player->present = TRUE;
3554         field_player->active = TRUE;
3555
3556         /*
3557         player->initial_element = field_player->initial_element;
3558         player->artwork_element = field_player->artwork_element;
3559
3560         player->block_last_field       = field_player->block_last_field;
3561         player->block_delay_adjustment = field_player->block_delay_adjustment;
3562         */
3563
3564         StorePlayer[jx][jy] = field_player->element_nr;
3565
3566         field_player->jx = field_player->last_jx = jx;
3567         field_player->jy = field_player->last_jy = jy;
3568
3569         if (local_player == player)
3570           local_player = field_player;
3571
3572         map_player_action[field_player->index_nr] = i;
3573
3574         field_player->mapped = TRUE;
3575
3576 #if DEBUG_INIT_PLAYER
3577         if (options.debug)
3578           printf("- map_player_action[%d] == %d\n",
3579                  field_player->index_nr + 1, i + 1);
3580 #endif
3581       }
3582     }
3583
3584     if (player->connected && player->present)
3585       player->mapped = TRUE;
3586   }
3587
3588 #if DEBUG_INIT_PLAYER
3589   if (options.debug)
3590   {
3591     printf("Player status after player assignment (first stage):\n");
3592
3593     for (i = 0; i < MAX_PLAYERS; i++)
3594     {
3595       struct PlayerInfo *player = &stored_player[i];
3596
3597       printf("- player %d: present == %d, connected == %d, active == %d",
3598              i + 1,
3599              player->present,
3600              player->connected,
3601              player->active);
3602
3603       if (local_player == player)
3604         printf(" (local player)");
3605
3606       printf("\n");
3607     }
3608   }
3609 #endif
3610
3611 #else
3612
3613   /* check if any connected player was not found in playfield */
3614   for (i = 0; i < MAX_PLAYERS; i++)
3615   {
3616     struct PlayerInfo *player = &stored_player[i];
3617
3618     if (player->connected && !player->present)
3619     {
3620       for (j = 0; j < MAX_PLAYERS; j++)
3621       {
3622         struct PlayerInfo *field_player = &stored_player[j];
3623         int jx = field_player->jx, jy = field_player->jy;
3624
3625         /* assign first free player found that is present in the playfield */
3626         if (field_player->present && !field_player->connected)
3627         {
3628           player->present = TRUE;
3629           player->active = TRUE;
3630
3631           field_player->present = FALSE;
3632           field_player->active = FALSE;
3633
3634           player->initial_element = field_player->initial_element;
3635           player->artwork_element = field_player->artwork_element;
3636
3637           player->block_last_field       = field_player->block_last_field;
3638           player->block_delay_adjustment = field_player->block_delay_adjustment;
3639
3640           StorePlayer[jx][jy] = player->element_nr;
3641
3642           player->jx = player->last_jx = jx;
3643           player->jy = player->last_jy = jy;
3644
3645           break;
3646         }
3647       }
3648     }
3649   }
3650 #endif
3651
3652 #if 0
3653   printf("::: local_player->present == %d\n", local_player->present);
3654 #endif
3655
3656   if (tape.playing)
3657   {
3658     /* when playing a tape, eliminate all players who do not participate */
3659
3660 #if USE_NEW_PLAYER_ASSIGNMENTS
3661
3662     if (!game.team_mode)
3663     {
3664       for (i = 0; i < MAX_PLAYERS; i++)
3665       {
3666         if (stored_player[i].active &&
3667             !tape.player_participates[map_player_action[i]])
3668         {
3669           struct PlayerInfo *player = &stored_player[i];
3670           int jx = player->jx, jy = player->jy;
3671
3672 #if DEBUG_INIT_PLAYER
3673           if (options.debug)
3674             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3675 #endif
3676
3677           player->active = FALSE;
3678           StorePlayer[jx][jy] = 0;
3679           Feld[jx][jy] = EL_EMPTY;
3680         }
3681       }
3682     }
3683
3684 #else
3685
3686     for (i = 0; i < MAX_PLAYERS; i++)
3687     {
3688       if (stored_player[i].active &&
3689           !tape.player_participates[i])
3690       {
3691         struct PlayerInfo *player = &stored_player[i];
3692         int jx = player->jx, jy = player->jy;
3693
3694         player->active = FALSE;
3695         StorePlayer[jx][jy] = 0;
3696         Feld[jx][jy] = EL_EMPTY;
3697       }
3698     }
3699 #endif
3700   }
3701   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3702   {
3703     /* when in single player mode, eliminate all but the first active player */
3704
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706     {
3707       if (stored_player[i].active)
3708       {
3709         for (j = i + 1; j < MAX_PLAYERS; j++)
3710         {
3711           if (stored_player[j].active)
3712           {
3713             struct PlayerInfo *player = &stored_player[j];
3714             int jx = player->jx, jy = player->jy;
3715
3716             player->active = FALSE;
3717             player->present = FALSE;
3718
3719             StorePlayer[jx][jy] = 0;
3720             Feld[jx][jy] = EL_EMPTY;
3721           }
3722         }
3723       }
3724     }
3725   }
3726
3727   /* when recording the game, store which players take part in the game */
3728   if (tape.recording)
3729   {
3730 #if USE_NEW_PLAYER_ASSIGNMENTS
3731     for (i = 0; i < MAX_PLAYERS; i++)
3732       if (stored_player[i].connected)
3733         tape.player_participates[i] = TRUE;
3734 #else
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736       if (stored_player[i].active)
3737         tape.player_participates[i] = TRUE;
3738 #endif
3739   }
3740
3741 #if DEBUG_INIT_PLAYER
3742   if (options.debug)
3743   {
3744     printf("Player status after player assignment (final stage):\n");
3745
3746     for (i = 0; i < MAX_PLAYERS; i++)
3747     {
3748       struct PlayerInfo *player = &stored_player[i];
3749
3750       printf("- player %d: present == %d, connected == %d, active == %d",
3751              i + 1,
3752              player->present,
3753              player->connected,
3754              player->active);
3755
3756       if (local_player == player)
3757         printf(" (local player)");
3758
3759       printf("\n");
3760     }
3761   }
3762 #endif
3763
3764   if (BorderElement == EL_EMPTY)
3765   {
3766     SBX_Left = 0;
3767     SBX_Right = lev_fieldx - SCR_FIELDX;
3768     SBY_Upper = 0;
3769     SBY_Lower = lev_fieldy - SCR_FIELDY;
3770   }
3771   else
3772   {
3773     SBX_Left = -1;
3774     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3775     SBY_Upper = -1;
3776     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3777   }
3778
3779   if (full_lev_fieldx <= SCR_FIELDX)
3780     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3781   if (full_lev_fieldy <= SCR_FIELDY)
3782     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3783
3784   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3785     SBX_Left--;
3786   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3787     SBY_Upper--;
3788
3789   /* if local player not found, look for custom element that might create
3790      the player (make some assumptions about the right custom element) */
3791   if (!local_player->present)
3792   {
3793     int start_x = 0, start_y = 0;
3794     int found_rating = 0;
3795     int found_element = EL_UNDEFINED;
3796     int player_nr = local_player->index_nr;
3797
3798     SCAN_PLAYFIELD(x, y)
3799     {
3800       int element = Feld[x][y];
3801       int content;
3802       int xx, yy;
3803       boolean is_player;
3804
3805       if (level.use_start_element[player_nr] &&
3806           level.start_element[player_nr] == element &&
3807           found_rating < 4)
3808       {
3809         start_x = x;
3810         start_y = y;
3811
3812         found_rating = 4;
3813         found_element = element;
3814       }
3815
3816       if (!IS_CUSTOM_ELEMENT(element))
3817         continue;
3818
3819       if (CAN_CHANGE(element))
3820       {
3821         for (i = 0; i < element_info[element].num_change_pages; i++)
3822         {
3823           /* check for player created from custom element as single target */
3824           content = element_info[element].change_page[i].target_element;
3825           is_player = ELEM_IS_PLAYER(content);
3826
3827           if (is_player && (found_rating < 3 ||
3828                             (found_rating == 3 && element < found_element)))
3829           {
3830             start_x = x;
3831             start_y = y;
3832
3833             found_rating = 3;
3834             found_element = element;
3835           }
3836         }
3837       }
3838
3839       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3840       {
3841         /* check for player created from custom element as explosion content */
3842         content = element_info[element].content.e[xx][yy];
3843         is_player = ELEM_IS_PLAYER(content);
3844
3845         if (is_player && (found_rating < 2 ||
3846                           (found_rating == 2 && element < found_element)))
3847         {
3848           start_x = x + xx - 1;
3849           start_y = y + yy - 1;
3850
3851           found_rating = 2;
3852           found_element = element;
3853         }
3854
3855         if (!CAN_CHANGE(element))
3856           continue;
3857
3858         for (i = 0; i < element_info[element].num_change_pages; i++)
3859         {
3860           /* check for player created from custom element as extended target */
3861           content =
3862             element_info[element].change_page[i].target_content.e[xx][yy];
3863
3864           is_player = ELEM_IS_PLAYER(content);
3865
3866           if (is_player && (found_rating < 1 ||
3867                             (found_rating == 1 && element < found_element)))
3868           {
3869             start_x = x + xx - 1;
3870             start_y = y + yy - 1;
3871
3872             found_rating = 1;
3873             found_element = element;
3874           }
3875         }
3876       }
3877     }
3878
3879     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3880                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3881                 start_x - MIDPOSX);
3882
3883     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3884                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3885                 start_y - MIDPOSY);
3886   }
3887   else
3888   {
3889     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3891                 local_player->jx - MIDPOSX);
3892
3893     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 local_player->jy - MIDPOSY);
3896   }
3897
3898   /* !!! FIX THIS (START) !!! */
3899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3900   {
3901     InitGameEngine_EM();
3902   }
3903   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3904   {
3905     InitGameEngine_SP();
3906   }
3907   else
3908   {
3909     DrawLevel(REDRAW_FIELD);
3910     DrawAllPlayers();
3911
3912     /* after drawing the level, correct some elements */
3913     if (game.timegate_time_left == 0)
3914       CloseAllOpenTimegates();
3915   }
3916
3917   /* blit playfield from scroll buffer to normal back buffer for fading in */
3918   BlitScreenToBitmap(backbuffer);
3919   /* !!! FIX THIS (END) !!! */
3920
3921   FadeIn(REDRAW_FIELD);
3922
3923 #if 1
3924   // full screen redraw is required at this point in the following cases:
3925   // - special editor door undrawn when game was started from level editor
3926   // - drawing area (playfield) was changed and has to be removed completely
3927   redraw_mask = REDRAW_ALL;
3928   BackToFront();
3929 #endif
3930
3931   if (!game.restart_level)
3932   {
3933     /* copy default game door content to main double buffer */
3934
3935     /* !!! CHECK AGAIN !!! */
3936     SetPanelBackground();
3937     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3938     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3939   }
3940
3941   SetPanelBackground();
3942   SetDrawBackgroundMask(REDRAW_DOOR_1);
3943
3944   UpdateAndDisplayGameControlValues();
3945
3946   if (!game.restart_level)
3947   {
3948     UnmapGameButtons();
3949     UnmapTapeButtons();
3950     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3951     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3952     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3953     MapGameButtons();
3954     MapTapeButtons();
3955
3956     /* copy actual game door content to door double buffer for OpenDoor() */
3957     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3958
3959     OpenDoor(DOOR_OPEN_ALL);
3960
3961     PlaySound(SND_GAME_STARTING);
3962
3963     if (setup.sound_music)
3964       PlayLevelMusic();
3965
3966     KeyboardAutoRepeatOffUnlessAutoplay();
3967
3968 #if DEBUG_INIT_PLAYER
3969     if (options.debug)
3970     {
3971       printf("Player status (final):\n");
3972
3973       for (i = 0; i < MAX_PLAYERS; i++)
3974       {
3975         struct PlayerInfo *player = &stored_player[i];
3976
3977         printf("- player %d: present == %d, connected == %d, active == %d",
3978                i + 1,
3979                player->present,
3980                player->connected,
3981                player->active);
3982
3983         if (local_player == player)
3984           printf(" (local player)");
3985
3986         printf("\n");
3987       }
3988     }
3989 #endif
3990   }
3991
3992   UnmapAllGadgets();
3993
3994   MapGameButtons();
3995   MapTapeButtons();
3996
3997   if (!game.restart_level && !tape.playing)
3998   {
3999     LevelStats_incPlayed(level_nr);
4000
4001     SaveLevelSetup_SeriesInfo();
4002   }
4003
4004   game.restart_level = FALSE;
4005
4006   SaveEngineSnapshotToListInitial();
4007 }
4008
4009 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4010 {
4011   /* this is used for non-R'n'D game engines to update certain engine values */
4012
4013   /* needed to determine if sounds are played within the visible screen area */
4014   scroll_x = actual_scroll_x;
4015   scroll_y = actual_scroll_y;
4016 }
4017
4018 void InitMovDir(int x, int y)
4019 {
4020   int i, element = Feld[x][y];
4021   static int xy[4][2] =
4022   {
4023     {  0, +1 },
4024     { +1,  0 },
4025     {  0, -1 },
4026     { -1,  0 }
4027   };
4028   static int direction[3][4] =
4029   {
4030     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4031     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4032     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4033   };
4034
4035   switch (element)
4036   {
4037     case EL_BUG_RIGHT:
4038     case EL_BUG_UP:
4039     case EL_BUG_LEFT:
4040     case EL_BUG_DOWN:
4041       Feld[x][y] = EL_BUG;
4042       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4043       break;
4044
4045     case EL_SPACESHIP_RIGHT:
4046     case EL_SPACESHIP_UP:
4047     case EL_SPACESHIP_LEFT:
4048     case EL_SPACESHIP_DOWN:
4049       Feld[x][y] = EL_SPACESHIP;
4050       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4051       break;
4052
4053     case EL_BD_BUTTERFLY_RIGHT:
4054     case EL_BD_BUTTERFLY_UP:
4055     case EL_BD_BUTTERFLY_LEFT:
4056     case EL_BD_BUTTERFLY_DOWN:
4057       Feld[x][y] = EL_BD_BUTTERFLY;
4058       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4059       break;
4060
4061     case EL_BD_FIREFLY_RIGHT:
4062     case EL_BD_FIREFLY_UP:
4063     case EL_BD_FIREFLY_LEFT:
4064     case EL_BD_FIREFLY_DOWN:
4065       Feld[x][y] = EL_BD_FIREFLY;
4066       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4067       break;
4068
4069     case EL_PACMAN_RIGHT:
4070     case EL_PACMAN_UP:
4071     case EL_PACMAN_LEFT:
4072     case EL_PACMAN_DOWN:
4073       Feld[x][y] = EL_PACMAN;
4074       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4075       break;
4076
4077     case EL_YAMYAM_LEFT:
4078     case EL_YAMYAM_RIGHT:
4079     case EL_YAMYAM_UP:
4080     case EL_YAMYAM_DOWN:
4081       Feld[x][y] = EL_YAMYAM;
4082       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4083       break;
4084
4085     case EL_SP_SNIKSNAK:
4086       MovDir[x][y] = MV_UP;
4087       break;
4088
4089     case EL_SP_ELECTRON:
4090       MovDir[x][y] = MV_LEFT;
4091       break;
4092
4093     case EL_MOLE_LEFT:
4094     case EL_MOLE_RIGHT:
4095     case EL_MOLE_UP:
4096     case EL_MOLE_DOWN:
4097       Feld[x][y] = EL_MOLE;
4098       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4099       break;
4100
4101     default:
4102       if (IS_CUSTOM_ELEMENT(element))
4103       {
4104         struct ElementInfo *ei = &element_info[element];
4105         int move_direction_initial = ei->move_direction_initial;
4106         int move_pattern = ei->move_pattern;
4107
4108         if (move_direction_initial == MV_START_PREVIOUS)
4109         {
4110           if (MovDir[x][y] != MV_NONE)
4111             return;
4112
4113           move_direction_initial = MV_START_AUTOMATIC;
4114         }
4115
4116         if (move_direction_initial == MV_START_RANDOM)
4117           MovDir[x][y] = 1 << RND(4);
4118         else if (move_direction_initial & MV_ANY_DIRECTION)
4119           MovDir[x][y] = move_direction_initial;
4120         else if (move_pattern == MV_ALL_DIRECTIONS ||
4121                  move_pattern == MV_TURNING_LEFT ||
4122                  move_pattern == MV_TURNING_RIGHT ||
4123                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4124                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4125                  move_pattern == MV_TURNING_RANDOM)
4126           MovDir[x][y] = 1 << RND(4);
4127         else if (move_pattern == MV_HORIZONTAL)
4128           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4129         else if (move_pattern == MV_VERTICAL)
4130           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4131         else if (move_pattern & MV_ANY_DIRECTION)
4132           MovDir[x][y] = element_info[element].move_pattern;
4133         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4134                  move_pattern == MV_ALONG_RIGHT_SIDE)
4135         {
4136           /* use random direction as default start direction */
4137           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4138             MovDir[x][y] = 1 << RND(4);
4139
4140           for (i = 0; i < NUM_DIRECTIONS; i++)
4141           {
4142             int x1 = x + xy[i][0];
4143             int y1 = y + xy[i][1];
4144
4145             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4146             {
4147               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4148                 MovDir[x][y] = direction[0][i];
4149               else
4150                 MovDir[x][y] = direction[1][i];
4151
4152               break;
4153             }
4154           }
4155         }                
4156       }
4157       else
4158       {
4159         MovDir[x][y] = 1 << RND(4);
4160
4161         if (element != EL_BUG &&
4162             element != EL_SPACESHIP &&
4163             element != EL_BD_BUTTERFLY &&
4164             element != EL_BD_FIREFLY)
4165           break;
4166
4167         for (i = 0; i < NUM_DIRECTIONS; i++)
4168         {
4169           int x1 = x + xy[i][0];
4170           int y1 = y + xy[i][1];
4171
4172           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4173           {
4174             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4175             {
4176               MovDir[x][y] = direction[0][i];
4177               break;
4178             }
4179             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4180                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4181             {
4182               MovDir[x][y] = direction[1][i];
4183               break;
4184             }
4185           }
4186         }
4187       }
4188       break;
4189   }
4190
4191   GfxDir[x][y] = MovDir[x][y];
4192 }
4193
4194 void InitAmoebaNr(int x, int y)
4195 {
4196   int i;
4197   int group_nr = AmoebeNachbarNr(x, y);
4198
4199   if (group_nr == 0)
4200   {
4201     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4202     {
4203       if (AmoebaCnt[i] == 0)
4204       {
4205         group_nr = i;
4206         break;
4207       }
4208     }
4209   }
4210
4211   AmoebaNr[x][y] = group_nr;
4212   AmoebaCnt[group_nr]++;
4213   AmoebaCnt2[group_nr]++;
4214 }
4215
4216 static void PlayerWins(struct PlayerInfo *player)
4217 {
4218   player->LevelSolved = TRUE;
4219   player->GameOver = TRUE;
4220
4221   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4222                          level.native_em_level->lev->score : player->score);
4223
4224   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4225                                       TimeLeft);
4226   player->LevelSolved_CountingScore = player->score_final;
4227 }
4228
4229 void GameWon()
4230 {
4231   static int time, time_final;
4232   static int score, score_final;
4233   static int game_over_delay_1 = 0;
4234   static int game_over_delay_2 = 0;
4235   int game_over_delay_value_1 = 50;
4236   int game_over_delay_value_2 = 50;
4237
4238   if (!local_player->LevelSolved_GameWon)
4239   {
4240     int i;
4241
4242     /* do not start end game actions before the player stops moving (to exit) */
4243     if (local_player->MovPos)
4244       return;
4245
4246     local_player->LevelSolved_GameWon = TRUE;
4247     local_player->LevelSolved_SaveTape = tape.recording;
4248     local_player->LevelSolved_SaveScore = !tape.playing;
4249
4250     if (!tape.playing)
4251     {
4252       LevelStats_incSolved(level_nr);
4253
4254       SaveLevelSetup_SeriesInfo();
4255     }
4256
4257     if (tape.auto_play)         /* tape might already be stopped here */
4258       tape.auto_play_level_solved = TRUE;
4259
4260     TapeStop();
4261
4262     game_over_delay_1 = game_over_delay_value_1;
4263     game_over_delay_2 = game_over_delay_value_2;
4264
4265     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4266     score = score_final = local_player->score_final;
4267
4268     if (TimeLeft > 0)
4269     {
4270       time_final = 0;
4271       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4272     }
4273     else if (game.no_time_limit && TimePlayed < 999)
4274     {
4275       time_final = 999;
4276       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4277     }
4278
4279     local_player->score_final = score_final;
4280
4281     if (level_editor_test_game)
4282     {
4283       time = time_final;
4284       score = score_final;
4285
4286       local_player->LevelSolved_CountingTime = time;
4287       local_player->LevelSolved_CountingScore = score;
4288
4289       game_panel_controls[GAME_PANEL_TIME].value = time;
4290       game_panel_controls[GAME_PANEL_SCORE].value = score;
4291
4292       DisplayGameControlValues();
4293     }
4294
4295     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4296     {
4297       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4298       {
4299         /* close exit door after last player */
4300         if ((AllPlayersGone &&
4301              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4302               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4303               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4304             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4305             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4306         {
4307           int element = Feld[ExitX][ExitY];
4308
4309           Feld[ExitX][ExitY] =
4310             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4311              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4312              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4313              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4314              EL_EM_STEEL_EXIT_CLOSING);
4315
4316           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4317         }
4318
4319         /* player disappears */
4320         DrawLevelField(ExitX, ExitY);
4321       }
4322
4323       for (i = 0; i < MAX_PLAYERS; i++)
4324       {
4325         struct PlayerInfo *player = &stored_player[i];
4326
4327         if (player->present)
4328         {
4329           RemovePlayer(player);
4330
4331           /* player disappears */
4332           DrawLevelField(player->jx, player->jy);
4333         }
4334       }
4335     }
4336
4337     PlaySound(SND_GAME_WINNING);
4338   }
4339
4340   if (game_over_delay_1 > 0)
4341   {
4342     game_over_delay_1--;
4343
4344     return;
4345   }
4346
4347   if (time != time_final)
4348   {
4349     int time_to_go = ABS(time_final - time);
4350     int time_count_dir = (time < time_final ? +1 : -1);
4351     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4352
4353     time  += time_count_steps * time_count_dir;
4354     score += time_count_steps * level.score[SC_TIME_BONUS];
4355
4356     local_player->LevelSolved_CountingTime = time;
4357     local_player->LevelSolved_CountingScore = score;
4358
4359     game_panel_controls[GAME_PANEL_TIME].value = time;
4360     game_panel_controls[GAME_PANEL_SCORE].value = score;
4361
4362     DisplayGameControlValues();
4363
4364     if (time == time_final)
4365       StopSound(SND_GAME_LEVELTIME_BONUS);
4366     else if (setup.sound_loops)
4367       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4368     else
4369       PlaySound(SND_GAME_LEVELTIME_BONUS);
4370
4371     return;
4372   }
4373
4374   local_player->LevelSolved_PanelOff = TRUE;
4375
4376   if (game_over_delay_2 > 0)
4377   {
4378     game_over_delay_2--;
4379
4380     return;
4381   }
4382
4383   GameEnd();
4384 }
4385
4386 void GameEnd()
4387 {
4388   int hi_pos;
4389   boolean raise_level = FALSE;
4390
4391   local_player->LevelSolved_GameEnd = TRUE;
4392
4393   if (!global.use_envelope_request)
4394     CloseDoor(DOOR_CLOSE_1);
4395
4396   if (local_player->LevelSolved_SaveTape)
4397   {
4398     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4399   }
4400
4401   CloseDoor(DOOR_CLOSE_ALL);
4402
4403   if (level_editor_test_game)
4404   {
4405     game_status = GAME_MODE_MAIN;
4406
4407     DrawAndFadeInMainMenu(REDRAW_FIELD);
4408
4409     return;
4410   }
4411
4412   if (!local_player->LevelSolved_SaveScore)
4413   {
4414     FadeOut(REDRAW_FIELD);
4415
4416     game_status = GAME_MODE_MAIN;
4417
4418     DrawAndFadeInMainMenu(REDRAW_FIELD);
4419
4420     return;
4421   }
4422
4423   if (level_nr == leveldir_current->handicap_level)
4424   {
4425     leveldir_current->handicap_level++;
4426
4427     SaveLevelSetup_SeriesInfo();
4428   }
4429
4430   if (level_nr < leveldir_current->last_level)
4431     raise_level = TRUE;                 /* advance to next level */
4432
4433   if ((hi_pos = NewHiScore()) >= 0) 
4434   {
4435     game_status = GAME_MODE_SCORES;
4436
4437     /* needed if different viewport properties defined for scores */
4438     ChangeViewportPropertiesIfNeeded();
4439
4440     DrawHallOfFame(hi_pos);
4441
4442     if (raise_level)
4443     {
4444       level_nr++;
4445       TapeErase();
4446     }
4447   }
4448   else
4449   {
4450     FadeOut(REDRAW_FIELD);
4451
4452     game_status = GAME_MODE_MAIN;
4453
4454     if (raise_level)
4455     {
4456       level_nr++;
4457       TapeErase();
4458     }
4459
4460     DrawAndFadeInMainMenu(REDRAW_FIELD);
4461   }
4462 }
4463
4464 int NewHiScore()
4465 {
4466   int k, l;
4467   int position = -1;
4468
4469   LoadScore(level_nr);
4470
4471   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4472       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4473     return -1;
4474
4475   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4476   {
4477     if (local_player->score_final > highscore[k].Score)
4478     {
4479       /* player has made it to the hall of fame */
4480
4481       if (k < MAX_SCORE_ENTRIES - 1)
4482       {
4483         int m = MAX_SCORE_ENTRIES - 1;
4484
4485 #ifdef ONE_PER_NAME
4486         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4487           if (strEqual(setup.player_name, highscore[l].Name))
4488             m = l;
4489         if (m == k)     /* player's new highscore overwrites his old one */
4490           goto put_into_list;
4491 #endif
4492
4493         for (l = m; l > k; l--)
4494         {
4495           strcpy(highscore[l].Name, highscore[l - 1].Name);
4496           highscore[l].Score = highscore[l - 1].Score;
4497         }
4498       }
4499
4500 #ifdef ONE_PER_NAME
4501       put_into_list:
4502 #endif
4503       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4504       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4505       highscore[k].Score = local_player->score_final; 
4506       position = k;
4507       break;
4508     }
4509
4510 #ifdef ONE_PER_NAME
4511     else if (!strncmp(setup.player_name, highscore[k].Name,
4512                       MAX_PLAYER_NAME_LEN))
4513       break;    /* player already there with a higher score */
4514 #endif
4515
4516   }
4517
4518   if (position >= 0) 
4519     SaveScore(level_nr);
4520
4521   return position;
4522 }
4523
4524 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4525 {
4526   int element = Feld[x][y];
4527   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4528   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4529   int horiz_move = (dx != 0);
4530   int sign = (horiz_move ? dx : dy);
4531   int step = sign * element_info[element].move_stepsize;
4532
4533   /* special values for move stepsize for spring and things on conveyor belt */
4534   if (horiz_move)
4535   {
4536     if (CAN_FALL(element) &&
4537         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4538       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4539     else if (element == EL_SPRING)
4540       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4541   }
4542
4543   return step;
4544 }
4545
4546 inline static int getElementMoveStepsize(int x, int y)
4547 {
4548   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4549 }
4550
4551 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4552 {
4553   if (player->GfxAction != action || player->GfxDir != dir)
4554   {
4555     player->GfxAction = action;
4556     player->GfxDir = dir;
4557     player->Frame = 0;
4558     player->StepFrame = 0;
4559   }
4560 }
4561
4562 static void ResetGfxFrame(int x, int y, boolean redraw)
4563 {
4564   int element = Feld[x][y];
4565   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4566   int last_gfx_frame = GfxFrame[x][y];
4567
4568   if (graphic_info[graphic].anim_global_sync)
4569     GfxFrame[x][y] = FrameCounter;
4570   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4571     GfxFrame[x][y] = CustomValue[x][y];
4572   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4573     GfxFrame[x][y] = element_info[element].collect_score;
4574   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4575     GfxFrame[x][y] = ChangeDelay[x][y];
4576
4577   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4578     DrawLevelGraphicAnimation(x, y, graphic);
4579 }
4580
4581 static void ResetGfxAnimation(int x, int y)
4582 {
4583   GfxAction[x][y] = ACTION_DEFAULT;
4584   GfxDir[x][y] = MovDir[x][y];
4585   GfxFrame[x][y] = 0;
4586
4587   ResetGfxFrame(x, y, FALSE);
4588 }
4589
4590 static void ResetRandomAnimationValue(int x, int y)
4591 {
4592   GfxRandom[x][y] = INIT_GFX_RANDOM();
4593 }
4594
4595 void InitMovingField(int x, int y, int direction)
4596 {
4597   int element = Feld[x][y];
4598   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4599   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4600   int newx = x + dx;
4601   int newy = y + dy;
4602   boolean is_moving_before, is_moving_after;
4603
4604   /* check if element was/is moving or being moved before/after mode change */
4605   is_moving_before = (WasJustMoving[x][y] != 0);
4606   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4607
4608   /* reset animation only for moving elements which change direction of moving
4609      or which just started or stopped moving
4610      (else CEs with property "can move" / "not moving" are reset each frame) */
4611   if (is_moving_before != is_moving_after ||
4612       direction != MovDir[x][y])
4613     ResetGfxAnimation(x, y);
4614
4615   MovDir[x][y] = direction;
4616   GfxDir[x][y] = direction;
4617
4618   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4619                      direction == MV_DOWN && CAN_FALL(element) ?
4620                      ACTION_FALLING : ACTION_MOVING);
4621
4622   /* this is needed for CEs with property "can move" / "not moving" */
4623
4624   if (is_moving_after)
4625   {
4626     if (Feld[newx][newy] == EL_EMPTY)
4627       Feld[newx][newy] = EL_BLOCKED;
4628
4629     MovDir[newx][newy] = MovDir[x][y];
4630
4631     CustomValue[newx][newy] = CustomValue[x][y];
4632
4633     GfxFrame[newx][newy] = GfxFrame[x][y];
4634     GfxRandom[newx][newy] = GfxRandom[x][y];
4635     GfxAction[newx][newy] = GfxAction[x][y];
4636     GfxDir[newx][newy] = GfxDir[x][y];
4637   }
4638 }
4639
4640 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4641 {
4642   int direction = MovDir[x][y];
4643   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4644   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4645
4646   *goes_to_x = newx;
4647   *goes_to_y = newy;
4648 }
4649
4650 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4651 {
4652   int oldx = x, oldy = y;
4653   int direction = MovDir[x][y];
4654
4655   if (direction == MV_LEFT)
4656     oldx++;
4657   else if (direction == MV_RIGHT)
4658     oldx--;
4659   else if (direction == MV_UP)
4660     oldy++;
4661   else if (direction == MV_DOWN)
4662     oldy--;
4663
4664   *comes_from_x = oldx;
4665   *comes_from_y = oldy;
4666 }
4667
4668 int MovingOrBlocked2Element(int x, int y)
4669 {
4670   int element = Feld[x][y];
4671
4672   if (element == EL_BLOCKED)
4673   {
4674     int oldx, oldy;
4675
4676     Blocked2Moving(x, y, &oldx, &oldy);
4677     return Feld[oldx][oldy];
4678   }
4679   else
4680     return element;
4681 }
4682
4683 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4684 {
4685   /* like MovingOrBlocked2Element(), but if element is moving
4686      and (x,y) is the field the moving element is just leaving,
4687      return EL_BLOCKED instead of the element value */
4688   int element = Feld[x][y];
4689
4690   if (IS_MOVING(x, y))
4691   {
4692     if (element == EL_BLOCKED)
4693     {
4694       int oldx, oldy;
4695
4696       Blocked2Moving(x, y, &oldx, &oldy);
4697       return Feld[oldx][oldy];
4698     }
4699     else
4700       return EL_BLOCKED;
4701   }
4702   else
4703     return element;
4704 }
4705
4706 static void RemoveField(int x, int y)
4707 {
4708   Feld[x][y] = EL_EMPTY;
4709
4710   MovPos[x][y] = 0;
4711   MovDir[x][y] = 0;
4712   MovDelay[x][y] = 0;
4713
4714   CustomValue[x][y] = 0;
4715
4716   AmoebaNr[x][y] = 0;
4717   ChangeDelay[x][y] = 0;
4718   ChangePage[x][y] = -1;
4719   Pushed[x][y] = FALSE;
4720
4721   GfxElement[x][y] = EL_UNDEFINED;
4722   GfxAction[x][y] = ACTION_DEFAULT;
4723   GfxDir[x][y] = MV_NONE;
4724 }
4725
4726 void RemoveMovingField(int x, int y)
4727 {
4728   int oldx = x, oldy = y, newx = x, newy = y;
4729   int element = Feld[x][y];
4730   int next_element = EL_UNDEFINED;
4731
4732   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4733     return;
4734
4735   if (IS_MOVING(x, y))
4736   {
4737     Moving2Blocked(x, y, &newx, &newy);
4738
4739     if (Feld[newx][newy] != EL_BLOCKED)
4740     {
4741       /* element is moving, but target field is not free (blocked), but
4742          already occupied by something different (example: acid pool);
4743          in this case, only remove the moving field, but not the target */
4744
4745       RemoveField(oldx, oldy);
4746
4747       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4748
4749       TEST_DrawLevelField(oldx, oldy);
4750
4751       return;
4752     }
4753   }
4754   else if (element == EL_BLOCKED)
4755   {
4756     Blocked2Moving(x, y, &oldx, &oldy);
4757     if (!IS_MOVING(oldx, oldy))
4758       return;
4759   }
4760
4761   if (element == EL_BLOCKED &&
4762       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4763        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4764        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4765        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4766        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4767        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4768     next_element = get_next_element(Feld[oldx][oldy]);
4769
4770   RemoveField(oldx, oldy);
4771   RemoveField(newx, newy);
4772
4773   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4774
4775   if (next_element != EL_UNDEFINED)
4776     Feld[oldx][oldy] = next_element;
4777
4778   TEST_DrawLevelField(oldx, oldy);
4779   TEST_DrawLevelField(newx, newy);
4780 }
4781
4782 void DrawDynamite(int x, int y)
4783 {
4784   int sx = SCREENX(x), sy = SCREENY(y);
4785   int graphic = el2img(Feld[x][y]);
4786   int frame;
4787
4788   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4789     return;
4790
4791   if (IS_WALKABLE_INSIDE(Back[x][y]))
4792     return;
4793
4794   if (Back[x][y])
4795     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4796   else if (Store[x][y])
4797     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4798
4799   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4800
4801   if (Back[x][y] || Store[x][y])
4802     DrawGraphicThruMask(sx, sy, graphic, frame);
4803   else
4804     DrawGraphic(sx, sy, graphic, frame);
4805 }
4806
4807 void CheckDynamite(int x, int y)
4808 {
4809   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4810   {
4811     MovDelay[x][y]--;
4812
4813     if (MovDelay[x][y] != 0)
4814     {
4815       DrawDynamite(x, y);
4816       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4817
4818       return;
4819     }
4820   }
4821
4822   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4823
4824   Bang(x, y);
4825 }
4826
4827 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4828 {
4829   boolean num_checked_players = 0;
4830   int i;
4831
4832   for (i = 0; i < MAX_PLAYERS; i++)
4833   {
4834     if (stored_player[i].active)
4835     {
4836       int sx = stored_player[i].jx;
4837       int sy = stored_player[i].jy;
4838
4839       if (num_checked_players == 0)
4840       {
4841         *sx1 = *sx2 = sx;
4842         *sy1 = *sy2 = sy;
4843       }
4844       else
4845       {
4846         *sx1 = MIN(*sx1, sx);
4847         *sy1 = MIN(*sy1, sy);
4848         *sx2 = MAX(*sx2, sx);
4849         *sy2 = MAX(*sy2, sy);
4850       }
4851
4852       num_checked_players++;
4853     }
4854   }
4855 }
4856
4857 static boolean checkIfAllPlayersFitToScreen_RND()
4858 {
4859   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4860
4861   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4862
4863   return (sx2 - sx1 < SCR_FIELDX &&
4864           sy2 - sy1 < SCR_FIELDY);
4865 }
4866
4867 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4868 {
4869   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4870
4871   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4872
4873   *sx = (sx1 + sx2) / 2;
4874   *sy = (sy1 + sy2) / 2;
4875 }
4876
4877 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4878                         boolean center_screen, boolean quick_relocation)
4879 {
4880   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4881   boolean no_delay = (tape.warp_forward);
4882   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4883   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4884
4885   if (quick_relocation)
4886   {
4887     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4888     {
4889       if (!level.shifted_relocation || center_screen)
4890       {
4891         /* quick relocation (without scrolling), with centering of screen */
4892
4893         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4894                     x > SBX_Right + MIDPOSX ? SBX_Right :
4895                     x - MIDPOSX);
4896
4897         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4898                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4899                     y - MIDPOSY);
4900       }
4901       else
4902       {
4903         /* quick relocation (without scrolling), but do not center screen */
4904
4905         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4906                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4907                                old_x - MIDPOSX);
4908
4909         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4910                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4911                                old_y - MIDPOSY);
4912
4913         int offset_x = x + (scroll_x - center_scroll_x);
4914         int offset_y = y + (scroll_y - center_scroll_y);
4915
4916         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4917                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4918                     offset_x - MIDPOSX);
4919
4920         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4921                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4922                     offset_y - MIDPOSY);
4923       }
4924     }
4925     else
4926     {
4927       if (!level.shifted_relocation || center_screen)
4928       {
4929         /* quick relocation (without scrolling), with centering of screen */
4930
4931         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4932                     x > SBX_Right + MIDPOSX ? SBX_Right :
4933                     x - MIDPOSX);
4934
4935         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4936                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4937                     y - MIDPOSY);
4938       }
4939       else
4940       {
4941         /* quick relocation (without scrolling), but do not center screen */
4942
4943         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4944                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4945                                old_x - MIDPOSX);
4946
4947         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4948                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4949                                old_y - MIDPOSY);
4950
4951         int offset_x = x + (scroll_x - center_scroll_x);
4952         int offset_y = y + (scroll_y - center_scroll_y);
4953
4954         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4955                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4956                     offset_x - MIDPOSX);
4957
4958         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4959                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4960                     offset_y - MIDPOSY);
4961       }
4962     }
4963
4964     RedrawPlayfield(TRUE, 0,0,0,0);
4965   }
4966   else
4967   {
4968     int scroll_xx, scroll_yy;
4969
4970     if (!level.shifted_relocation || center_screen)
4971     {
4972       /* visible relocation (with scrolling), with centering of screen */
4973
4974       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4975                    x > SBX_Right + MIDPOSX ? SBX_Right :
4976                    x - MIDPOSX);
4977
4978       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4979                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4980                    y - MIDPOSY);
4981     }
4982     else
4983     {
4984       /* visible relocation (with scrolling), but do not center screen */
4985
4986       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4987                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4988                              old_x - MIDPOSX);
4989
4990       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4991                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4992                              old_y - MIDPOSY);
4993
4994       int offset_x = x + (scroll_x - center_scroll_x);
4995       int offset_y = y + (scroll_y - center_scroll_y);
4996
4997       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4998                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4999                    offset_x - MIDPOSX);
5000
5001       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5002                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5003                    offset_y - MIDPOSY);
5004     }
5005
5006
5007     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5008
5009     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5010     {
5011       int dx = 0, dy = 0;
5012       int fx = FX, fy = FY;
5013
5014       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5015       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5016
5017       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5018         break;
5019
5020       scroll_x -= dx;
5021       scroll_y -= dy;
5022
5023       fx += dx * TILEX / 2;
5024       fy += dy * TILEY / 2;
5025
5026       ScrollLevel(dx, dy);
5027       DrawAllPlayers();
5028
5029       /* scroll in two steps of half tile size to make things smoother */
5030       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5031       Delay(wait_delay_value);
5032
5033       /* scroll second step to align at full tile size */
5034       BackToFront();
5035       Delay(wait_delay_value);
5036     }
5037
5038     DrawAllPlayers();
5039     BackToFront();
5040     Delay(wait_delay_value);
5041   }
5042 }
5043
5044 void RelocatePlayer(int jx, int jy, int el_player_raw)
5045 {
5046   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5047   int player_nr = GET_PLAYER_NR(el_player);
5048   struct PlayerInfo *player = &stored_player[player_nr];
5049   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5050   boolean no_delay = (tape.warp_forward);
5051   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5052   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5053   int old_jx = player->jx;
5054   int old_jy = player->jy;
5055   int old_element = Feld[old_jx][old_jy];
5056   int element = Feld[jx][jy];
5057   boolean player_relocated = (old_jx != jx || old_jy != jy);
5058
5059   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5060   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5061   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5062   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5063   int leave_side_horiz = move_dir_horiz;
5064   int leave_side_vert  = move_dir_vert;
5065   int enter_side = enter_side_horiz | enter_side_vert;
5066   int leave_side = leave_side_horiz | leave_side_vert;
5067
5068   if (player->GameOver)         /* do not reanimate dead player */
5069     return;
5070
5071   if (!player_relocated)        /* no need to relocate the player */
5072     return;
5073
5074   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5075   {
5076     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5077     DrawLevelField(jx, jy);
5078   }
5079
5080   if (player->present)
5081   {
5082     while (player->MovPos)
5083     {
5084       ScrollPlayer(player, SCROLL_GO_ON);
5085       ScrollScreen(NULL, SCROLL_GO_ON);
5086
5087       AdvanceFrameAndPlayerCounters(player->index_nr);
5088
5089       DrawPlayer(player);
5090
5091       BackToFront();
5092       Delay(wait_delay_value);
5093     }
5094
5095     DrawPlayer(player);         /* needed here only to cleanup last field */
5096     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5097
5098     player->is_moving = FALSE;
5099   }
5100
5101   if (IS_CUSTOM_ELEMENT(old_element))
5102     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5103                                CE_LEFT_BY_PLAYER,
5104                                player->index_bit, leave_side);
5105
5106   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5107                                       CE_PLAYER_LEAVES_X,
5108                                       player->index_bit, leave_side);
5109
5110   Feld[jx][jy] = el_player;
5111   InitPlayerField(jx, jy, el_player, TRUE);
5112
5113   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5114      possible that the relocation target field did not contain a player element,
5115      but a walkable element, to which the new player was relocated -- in this
5116      case, restore that (already initialized!) element on the player field */
5117   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5118   {
5119     Feld[jx][jy] = element;     /* restore previously existing element */
5120   }
5121
5122   /* only visually relocate centered player */
5123   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5124                      FALSE, level.instant_relocation);
5125
5126   TestIfPlayerTouchesBadThing(jx, jy);
5127   TestIfPlayerTouchesCustomElement(jx, jy);
5128
5129   if (IS_CUSTOM_ELEMENT(element))
5130     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5131                                player->index_bit, enter_side);
5132
5133   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5134                                       player->index_bit, enter_side);
5135
5136   if (player->is_switching)
5137   {
5138     /* ensure that relocation while still switching an element does not cause
5139        a new element to be treated as also switched directly after relocation
5140        (this is important for teleporter switches that teleport the player to
5141        a place where another teleporter switch is in the same direction, which
5142        would then incorrectly be treated as immediately switched before the
5143        direction key that caused the switch was released) */
5144
5145     player->switch_x += jx - old_jx;
5146     player->switch_y += jy - old_jy;
5147   }
5148 }
5149
5150 void Explode(int ex, int ey, int phase, int mode)
5151 {
5152   int x, y;
5153   int last_phase;
5154   int border_element;
5155
5156   /* !!! eliminate this variable !!! */
5157   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5158
5159   if (game.explosions_delayed)
5160   {
5161     ExplodeField[ex][ey] = mode;
5162     return;
5163   }
5164
5165   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5166   {
5167     int center_element = Feld[ex][ey];
5168     int artwork_element, explosion_element;     /* set these values later */
5169
5170     /* remove things displayed in background while burning dynamite */
5171     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5172       Back[ex][ey] = 0;
5173
5174     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5175     {
5176       /* put moving element to center field (and let it explode there) */
5177       center_element = MovingOrBlocked2Element(ex, ey);
5178       RemoveMovingField(ex, ey);
5179       Feld[ex][ey] = center_element;
5180     }
5181
5182     /* now "center_element" is finally determined -- set related values now */
5183     artwork_element = center_element;           /* for custom player artwork */
5184     explosion_element = center_element;         /* for custom player artwork */
5185
5186     if (IS_PLAYER(ex, ey))
5187     {
5188       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5189
5190       artwork_element = stored_player[player_nr].artwork_element;
5191
5192       if (level.use_explosion_element[player_nr])
5193       {
5194         explosion_element = level.explosion_element[player_nr];
5195         artwork_element = explosion_element;
5196       }
5197     }
5198
5199     if (mode == EX_TYPE_NORMAL ||
5200         mode == EX_TYPE_CENTER ||
5201         mode == EX_TYPE_CROSS)
5202       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5203
5204     last_phase = element_info[explosion_element].explosion_delay + 1;
5205
5206     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5207     {
5208       int xx = x - ex + 1;
5209       int yy = y - ey + 1;
5210       int element;
5211
5212       if (!IN_LEV_FIELD(x, y) ||
5213           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5214           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5215         continue;
5216
5217       element = Feld[x][y];
5218
5219       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5220       {
5221         element = MovingOrBlocked2Element(x, y);
5222
5223         if (!IS_EXPLOSION_PROOF(element))
5224           RemoveMovingField(x, y);
5225       }
5226
5227       /* indestructible elements can only explode in center (but not flames) */
5228       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5229                                            mode == EX_TYPE_BORDER)) ||
5230           element == EL_FLAMES)
5231         continue;
5232
5233       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5234          behaviour, for example when touching a yamyam that explodes to rocks
5235          with active deadly shield, a rock is created under the player !!! */
5236       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5237 #if 0
5238       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5239           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5240            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5241 #else
5242       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5243 #endif
5244       {
5245         if (IS_ACTIVE_BOMB(element))
5246         {
5247           /* re-activate things under the bomb like gate or penguin */
5248           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5249           Back[x][y] = 0;
5250         }
5251
5252         continue;
5253       }
5254
5255       /* save walkable background elements while explosion on same tile */
5256       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5257           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5258         Back[x][y] = element;
5259
5260       /* ignite explodable elements reached by other explosion */
5261       if (element == EL_EXPLOSION)
5262         element = Store2[x][y];
5263
5264       if (AmoebaNr[x][y] &&
5265           (element == EL_AMOEBA_FULL ||
5266            element == EL_BD_AMOEBA ||
5267            element == EL_AMOEBA_GROWING))
5268       {
5269         AmoebaCnt[AmoebaNr[x][y]]--;
5270         AmoebaCnt2[AmoebaNr[x][y]]--;
5271       }
5272
5273       RemoveField(x, y);
5274
5275       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5276       {
5277         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5278
5279         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5280
5281         if (PLAYERINFO(ex, ey)->use_murphy)
5282           Store[x][y] = EL_EMPTY;
5283       }
5284
5285       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5286          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5287       else if (ELEM_IS_PLAYER(center_element))
5288         Store[x][y] = EL_EMPTY;
5289       else if (center_element == EL_YAMYAM)
5290         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5291       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5292         Store[x][y] = element_info[center_element].content.e[xx][yy];
5293 #if 1
5294       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5295          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5296          otherwise) -- FIX THIS !!! */
5297       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5298         Store[x][y] = element_info[element].content.e[1][1];
5299 #else
5300       else if (!CAN_EXPLODE(element))
5301         Store[x][y] = element_info[element].content.e[1][1];
5302 #endif
5303       else
5304         Store[x][y] = EL_EMPTY;
5305
5306       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5307           center_element == EL_AMOEBA_TO_DIAMOND)
5308         Store2[x][y] = element;
5309
5310       Feld[x][y] = EL_EXPLOSION;
5311       GfxElement[x][y] = artwork_element;
5312
5313       ExplodePhase[x][y] = 1;
5314       ExplodeDelay[x][y] = last_phase;
5315
5316       Stop[x][y] = TRUE;
5317     }
5318
5319     if (center_element == EL_YAMYAM)
5320       game.yamyam_content_nr =
5321         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5322
5323     return;
5324   }
5325
5326   if (Stop[ex][ey])
5327     return;
5328
5329   x = ex;
5330   y = ey;
5331
5332   if (phase == 1)
5333     GfxFrame[x][y] = 0;         /* restart explosion animation */
5334
5335   last_phase = ExplodeDelay[x][y];
5336
5337   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5338
5339   /* this can happen if the player leaves an explosion just in time */
5340   if (GfxElement[x][y] == EL_UNDEFINED)
5341     GfxElement[x][y] = EL_EMPTY;
5342
5343   border_element = Store2[x][y];
5344   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5345     border_element = StorePlayer[x][y];
5346
5347   if (phase == element_info[border_element].ignition_delay ||
5348       phase == last_phase)
5349   {
5350     boolean border_explosion = FALSE;
5351
5352     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5353         !PLAYER_EXPLOSION_PROTECTED(x, y))
5354     {
5355       KillPlayerUnlessExplosionProtected(x, y);
5356       border_explosion = TRUE;
5357     }
5358     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5359     {
5360       Feld[x][y] = Store2[x][y];
5361       Store2[x][y] = 0;
5362       Bang(x, y);
5363       border_explosion = TRUE;
5364     }
5365     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5366     {
5367       AmoebeUmwandeln(x, y);
5368       Store2[x][y] = 0;
5369       border_explosion = TRUE;
5370     }
5371
5372     /* if an element just explodes due to another explosion (chain-reaction),
5373        do not immediately end the new explosion when it was the last frame of
5374        the explosion (as it would be done in the following "if"-statement!) */
5375     if (border_explosion && phase == last_phase)
5376       return;
5377   }
5378
5379   if (phase == last_phase)
5380   {
5381     int element;
5382
5383     element = Feld[x][y] = Store[x][y];
5384     Store[x][y] = Store2[x][y] = 0;
5385     GfxElement[x][y] = EL_UNDEFINED;
5386
5387     /* player can escape from explosions and might therefore be still alive */
5388     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5389         element <= EL_PLAYER_IS_EXPLODING_4)
5390     {
5391       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5392       int explosion_element = EL_PLAYER_1 + player_nr;
5393       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5394       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5395
5396       if (level.use_explosion_element[player_nr])
5397         explosion_element = level.explosion_element[player_nr];
5398
5399       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5400                     element_info[explosion_element].content.e[xx][yy]);
5401     }
5402
5403     /* restore probably existing indestructible background element */
5404     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5405       element = Feld[x][y] = Back[x][y];
5406     Back[x][y] = 0;
5407
5408     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5409     GfxDir[x][y] = MV_NONE;
5410     ChangeDelay[x][y] = 0;
5411     ChangePage[x][y] = -1;
5412
5413     CustomValue[x][y] = 0;
5414
5415     InitField_WithBug2(x, y, FALSE);
5416
5417     TEST_DrawLevelField(x, y);
5418
5419     TestIfElementTouchesCustomElement(x, y);
5420
5421     if (GFX_CRUMBLED(element))
5422       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5423
5424     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5425       StorePlayer[x][y] = 0;
5426
5427     if (ELEM_IS_PLAYER(element))
5428       RelocatePlayer(x, y, element);
5429   }
5430   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5431   {
5432     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5433     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5434
5435     if (phase == delay)
5436       TEST_DrawLevelFieldCrumbled(x, y);
5437
5438     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5439     {
5440       DrawLevelElement(x, y, Back[x][y]);
5441       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5442     }
5443     else if (IS_WALKABLE_UNDER(Back[x][y]))
5444     {
5445       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5446       DrawLevelElementThruMask(x, y, Back[x][y]);
5447     }
5448     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5449       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5450   }
5451 }
5452
5453 void DynaExplode(int ex, int ey)
5454 {
5455   int i, j;
5456   int dynabomb_element = Feld[ex][ey];
5457   int dynabomb_size = 1;
5458   boolean dynabomb_xl = FALSE;
5459   struct PlayerInfo *player;
5460   static int xy[4][2] =
5461   {
5462     { 0, -1 },
5463     { -1, 0 },
5464     { +1, 0 },
5465     { 0, +1 }
5466   };
5467
5468   if (IS_ACTIVE_BOMB(dynabomb_element))
5469   {
5470     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5471     dynabomb_size = player->dynabomb_size;
5472     dynabomb_xl = player->dynabomb_xl;
5473     player->dynabombs_left++;
5474   }
5475
5476   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5477
5478   for (i = 0; i < NUM_DIRECTIONS; i++)
5479   {
5480     for (j = 1; j <= dynabomb_size; j++)
5481     {
5482       int x = ex + j * xy[i][0];
5483       int y = ey + j * xy[i][1];
5484       int element;
5485
5486       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5487         break;
5488
5489       element = Feld[x][y];
5490
5491       /* do not restart explosions of fields with active bombs */
5492       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5493         continue;
5494
5495       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5496
5497       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5498           !IS_DIGGABLE(element) && !dynabomb_xl)
5499         break;
5500     }
5501   }
5502 }
5503
5504 void Bang(int x, int y)
5505 {
5506   int element = MovingOrBlocked2Element(x, y);
5507   int explosion_type = EX_TYPE_NORMAL;
5508
5509   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5510   {
5511     struct PlayerInfo *player = PLAYERINFO(x, y);
5512
5513     element = Feld[x][y] = player->initial_element;
5514
5515     if (level.use_explosion_element[player->index_nr])
5516     {
5517       int explosion_element = level.explosion_element[player->index_nr];
5518
5519       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5520         explosion_type = EX_TYPE_CROSS;
5521       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5522         explosion_type = EX_TYPE_CENTER;
5523     }
5524   }
5525
5526   switch (element)
5527   {
5528     case EL_BUG:
5529     case EL_SPACESHIP:
5530     case EL_BD_BUTTERFLY:
5531     case EL_BD_FIREFLY:
5532     case EL_YAMYAM:
5533     case EL_DARK_YAMYAM:
5534     case EL_ROBOT:
5535     case EL_PACMAN:
5536     case EL_MOLE:
5537       RaiseScoreElement(element);
5538       break;
5539
5540     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5541     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5542     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5543     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5544     case EL_DYNABOMB_INCREASE_NUMBER:
5545     case EL_DYNABOMB_INCREASE_SIZE:
5546     case EL_DYNABOMB_INCREASE_POWER:
5547       explosion_type = EX_TYPE_DYNA;
5548       break;
5549
5550     case EL_DC_LANDMINE:
5551       explosion_type = EX_TYPE_CENTER;
5552       break;
5553
5554     case EL_PENGUIN:
5555     case EL_LAMP:
5556     case EL_LAMP_ACTIVE:
5557     case EL_AMOEBA_TO_DIAMOND:
5558       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5559         explosion_type = EX_TYPE_CENTER;
5560       break;
5561
5562     default:
5563       if (element_info[element].explosion_type == EXPLODES_CROSS)
5564         explosion_type = EX_TYPE_CROSS;
5565       else if (element_info[element].explosion_type == EXPLODES_1X1)
5566         explosion_type = EX_TYPE_CENTER;
5567       break;
5568   }
5569
5570   if (explosion_type == EX_TYPE_DYNA)
5571     DynaExplode(x, y);
5572   else
5573     Explode(x, y, EX_PHASE_START, explosion_type);
5574
5575   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5576 }
5577
5578 void SplashAcid(int x, int y)
5579 {
5580   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5581       (!IN_LEV_FIELD(x - 1, y - 2) ||
5582        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5583     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5584
5585   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5586       (!IN_LEV_FIELD(x + 1, y - 2) ||
5587        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5588     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5589
5590   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5591 }
5592
5593 static void InitBeltMovement()
5594 {
5595   static int belt_base_element[4] =
5596   {
5597     EL_CONVEYOR_BELT_1_LEFT,
5598     EL_CONVEYOR_BELT_2_LEFT,
5599     EL_CONVEYOR_BELT_3_LEFT,
5600     EL_CONVEYOR_BELT_4_LEFT
5601   };
5602   static int belt_base_active_element[4] =
5603   {
5604     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5605     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5606     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5607     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5608   };
5609
5610   int x, y, i, j;
5611
5612   /* set frame order for belt animation graphic according to belt direction */
5613   for (i = 0; i < NUM_BELTS; i++)
5614   {
5615     int belt_nr = i;
5616
5617     for (j = 0; j < NUM_BELT_PARTS; j++)
5618     {
5619       int element = belt_base_active_element[belt_nr] + j;
5620       int graphic_1 = el2img(element);
5621       int graphic_2 = el2panelimg(element);
5622
5623       if (game.belt_dir[i] == MV_LEFT)
5624       {
5625         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5626         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5627       }
5628       else
5629       {
5630         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5631         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5632       }
5633     }
5634   }
5635
5636   SCAN_PLAYFIELD(x, y)
5637   {
5638     int element = Feld[x][y];
5639
5640     for (i = 0; i < NUM_BELTS; i++)
5641     {
5642       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5643       {
5644         int e_belt_nr = getBeltNrFromBeltElement(element);
5645         int belt_nr = i;
5646
5647         if (e_belt_nr == belt_nr)
5648         {
5649           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5650
5651           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5652         }
5653       }
5654     }
5655   }
5656 }
5657
5658 static void ToggleBeltSwitch(int x, int y)
5659 {
5660   static int belt_base_element[4] =
5661   {
5662     EL_CONVEYOR_BELT_1_LEFT,
5663     EL_CONVEYOR_BELT_2_LEFT,
5664     EL_CONVEYOR_BELT_3_LEFT,
5665     EL_CONVEYOR_BELT_4_LEFT
5666   };
5667   static int belt_base_active_element[4] =
5668   {
5669     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5670     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5671     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5672     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5673   };
5674   static int belt_base_switch_element[4] =
5675   {
5676     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5677     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5678     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5679     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5680   };
5681   static int belt_move_dir[4] =
5682   {
5683     MV_LEFT,
5684     MV_NONE,
5685     MV_RIGHT,
5686     MV_NONE,
5687   };
5688
5689   int element = Feld[x][y];
5690   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5691   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5692   int belt_dir = belt_move_dir[belt_dir_nr];
5693   int xx, yy, i;
5694
5695   if (!IS_BELT_SWITCH(element))
5696     return;
5697
5698   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5699   game.belt_dir[belt_nr] = belt_dir;
5700
5701   if (belt_dir_nr == 3)
5702     belt_dir_nr = 1;
5703
5704   /* set frame order for belt animation graphic according to belt direction */
5705   for (i = 0; i < NUM_BELT_PARTS; i++)
5706   {
5707     int element = belt_base_active_element[belt_nr] + i;
5708     int graphic_1 = el2img(element);
5709     int graphic_2 = el2panelimg(element);
5710
5711     if (belt_dir == MV_LEFT)
5712     {
5713       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5714       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5715     }
5716     else
5717     {
5718       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5719       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5720     }
5721   }
5722
5723   SCAN_PLAYFIELD(xx, yy)
5724   {
5725     int element = Feld[xx][yy];
5726
5727     if (IS_BELT_SWITCH(element))
5728     {
5729       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5730
5731       if (e_belt_nr == belt_nr)
5732       {
5733         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5734         TEST_DrawLevelField(xx, yy);
5735       }
5736     }
5737     else if (IS_BELT(element) && belt_dir != MV_NONE)
5738     {
5739       int e_belt_nr = getBeltNrFromBeltElement(element);
5740
5741       if (e_belt_nr == belt_nr)
5742       {
5743         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5744
5745         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5746         TEST_DrawLevelField(xx, yy);
5747       }
5748     }
5749     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5750     {
5751       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5752
5753       if (e_belt_nr == belt_nr)
5754       {
5755         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5756
5757         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5758         TEST_DrawLevelField(xx, yy);
5759       }
5760     }
5761   }
5762 }
5763
5764 static void ToggleSwitchgateSwitch(int x, int y)
5765 {
5766   int xx, yy;
5767
5768   game.switchgate_pos = !game.switchgate_pos;
5769
5770   SCAN_PLAYFIELD(xx, yy)
5771   {
5772     int element = Feld[xx][yy];
5773
5774     if (element == EL_SWITCHGATE_SWITCH_UP)
5775     {
5776       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5777       TEST_DrawLevelField(xx, yy);
5778     }
5779     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5780     {
5781       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5782       TEST_DrawLevelField(xx, yy);
5783     }
5784     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5785     {
5786       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5787       TEST_DrawLevelField(xx, yy);
5788     }
5789     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5790     {
5791       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5792       TEST_DrawLevelField(xx, yy);
5793     }
5794     else if (element == EL_SWITCHGATE_OPEN ||
5795              element == EL_SWITCHGATE_OPENING)
5796     {
5797       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5798
5799       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5800     }
5801     else if (element == EL_SWITCHGATE_CLOSED ||
5802              element == EL_SWITCHGATE_CLOSING)
5803     {
5804       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5805
5806       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5807     }
5808   }
5809 }
5810
5811 static int getInvisibleActiveFromInvisibleElement(int element)
5812 {
5813   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5814           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5815           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5816           element);
5817 }
5818
5819 static int getInvisibleFromInvisibleActiveElement(int element)
5820 {
5821   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5822           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5823           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5824           element);
5825 }
5826
5827 static void RedrawAllLightSwitchesAndInvisibleElements()
5828 {
5829   int x, y;
5830
5831   SCAN_PLAYFIELD(x, y)
5832   {
5833     int element = Feld[x][y];
5834
5835     if (element == EL_LIGHT_SWITCH &&
5836         game.light_time_left > 0)
5837     {
5838       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5839       TEST_DrawLevelField(x, y);
5840     }
5841     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5842              game.light_time_left == 0)
5843     {
5844       Feld[x][y] = EL_LIGHT_SWITCH;
5845       TEST_DrawLevelField(x, y);
5846     }
5847     else if (element == EL_EMC_DRIPPER &&
5848              game.light_time_left > 0)
5849     {
5850       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5851       TEST_DrawLevelField(x, y);
5852     }
5853     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5854              game.light_time_left == 0)
5855     {
5856       Feld[x][y] = EL_EMC_DRIPPER;
5857       TEST_DrawLevelField(x, y);
5858     }
5859     else if (element == EL_INVISIBLE_STEELWALL ||
5860              element == EL_INVISIBLE_WALL ||
5861              element == EL_INVISIBLE_SAND)
5862     {
5863       if (game.light_time_left > 0)
5864         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5865
5866       TEST_DrawLevelField(x, y);
5867
5868       /* uncrumble neighbour fields, if needed */
5869       if (element == EL_INVISIBLE_SAND)
5870         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5871     }
5872     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5873              element == EL_INVISIBLE_WALL_ACTIVE ||
5874              element == EL_INVISIBLE_SAND_ACTIVE)
5875     {
5876       if (game.light_time_left == 0)
5877         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5878
5879       TEST_DrawLevelField(x, y);
5880
5881       /* re-crumble neighbour fields, if needed */
5882       if (element == EL_INVISIBLE_SAND)
5883         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5884     }
5885   }
5886 }
5887
5888 static void RedrawAllInvisibleElementsForLenses()
5889 {
5890   int x, y;
5891
5892   SCAN_PLAYFIELD(x, y)
5893   {
5894     int element = Feld[x][y];
5895
5896     if (element == EL_EMC_DRIPPER &&
5897         game.lenses_time_left > 0)
5898     {
5899       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5900       TEST_DrawLevelField(x, y);
5901     }
5902     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5903              game.lenses_time_left == 0)
5904     {
5905       Feld[x][y] = EL_EMC_DRIPPER;
5906       TEST_DrawLevelField(x, y);
5907     }
5908     else if (element == EL_INVISIBLE_STEELWALL ||
5909              element == EL_INVISIBLE_WALL ||
5910              element == EL_INVISIBLE_SAND)
5911     {
5912       if (game.lenses_time_left > 0)
5913         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5914
5915       TEST_DrawLevelField(x, y);
5916
5917       /* uncrumble neighbour fields, if needed */
5918       if (element == EL_INVISIBLE_SAND)
5919         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5920     }
5921     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5922              element == EL_INVISIBLE_WALL_ACTIVE ||
5923              element == EL_INVISIBLE_SAND_ACTIVE)
5924     {
5925       if (game.lenses_time_left == 0)
5926         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5927
5928       TEST_DrawLevelField(x, y);
5929
5930       /* re-crumble neighbour fields, if needed */
5931       if (element == EL_INVISIBLE_SAND)
5932         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5933     }
5934   }
5935 }
5936
5937 static void RedrawAllInvisibleElementsForMagnifier()
5938 {
5939   int x, y;
5940
5941   SCAN_PLAYFIELD(x, y)
5942   {
5943     int element = Feld[x][y];
5944
5945     if (element == EL_EMC_FAKE_GRASS &&
5946         game.magnify_time_left > 0)
5947     {
5948       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5949       TEST_DrawLevelField(x, y);
5950     }
5951     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5952              game.magnify_time_left == 0)
5953     {
5954       Feld[x][y] = EL_EMC_FAKE_GRASS;
5955       TEST_DrawLevelField(x, y);
5956     }
5957     else if (IS_GATE_GRAY(element) &&
5958              game.magnify_time_left > 0)
5959     {
5960       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5961                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5962                     IS_EM_GATE_GRAY(element) ?
5963                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5964                     IS_EMC_GATE_GRAY(element) ?
5965                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5966                     IS_DC_GATE_GRAY(element) ?
5967                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5968                     element);
5969       TEST_DrawLevelField(x, y);
5970     }
5971     else if (IS_GATE_GRAY_ACTIVE(element) &&
5972              game.magnify_time_left == 0)
5973     {
5974       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5975                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5976                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5977                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5978                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5979                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5980                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5981                     EL_DC_GATE_WHITE_GRAY :
5982                     element);
5983       TEST_DrawLevelField(x, y);
5984     }
5985   }
5986 }
5987
5988 static void ToggleLightSwitch(int x, int y)
5989 {
5990   int element = Feld[x][y];
5991
5992   game.light_time_left =
5993     (element == EL_LIGHT_SWITCH ?
5994      level.time_light * FRAMES_PER_SECOND : 0);
5995
5996   RedrawAllLightSwitchesAndInvisibleElements();
5997 }
5998
5999 static void ActivateTimegateSwitch(int x, int y)
6000 {
6001   int xx, yy;
6002
6003   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6004
6005   SCAN_PLAYFIELD(xx, yy)
6006   {
6007     int element = Feld[xx][yy];
6008
6009     if (element == EL_TIMEGATE_CLOSED ||
6010         element == EL_TIMEGATE_CLOSING)
6011     {
6012       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6013       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6014     }
6015
6016     /*
6017     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6018     {
6019       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6020       TEST_DrawLevelField(xx, yy);
6021     }
6022     */
6023
6024   }
6025
6026   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6027                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6028 }
6029
6030 void Impact(int x, int y)
6031 {
6032   boolean last_line = (y == lev_fieldy - 1);
6033   boolean object_hit = FALSE;
6034   boolean impact = (last_line || object_hit);
6035   int element = Feld[x][y];
6036   int smashed = EL_STEELWALL;
6037
6038   if (!last_line)       /* check if element below was hit */
6039   {
6040     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6041       return;
6042
6043     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6044                                          MovDir[x][y + 1] != MV_DOWN ||
6045                                          MovPos[x][y + 1] <= TILEY / 2));
6046
6047     /* do not smash moving elements that left the smashed field in time */
6048     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6049         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6050       object_hit = FALSE;
6051
6052 #if USE_QUICKSAND_IMPACT_BUGFIX
6053     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6054     {
6055       RemoveMovingField(x, y + 1);
6056       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6057       Feld[x][y + 2] = EL_ROCK;
6058       TEST_DrawLevelField(x, y + 2);
6059
6060       object_hit = TRUE;
6061     }
6062
6063     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6064     {
6065       RemoveMovingField(x, y + 1);
6066       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6067       Feld[x][y + 2] = EL_ROCK;
6068       TEST_DrawLevelField(x, y + 2);
6069
6070       object_hit = TRUE;
6071     }
6072 #endif
6073
6074     if (object_hit)
6075       smashed = MovingOrBlocked2Element(x, y + 1);
6076
6077     impact = (last_line || object_hit);
6078   }
6079
6080   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6081   {
6082     SplashAcid(x, y + 1);
6083     return;
6084   }
6085
6086   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6087   /* only reset graphic animation if graphic really changes after impact */
6088   if (impact &&
6089       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6090   {
6091     ResetGfxAnimation(x, y);
6092     TEST_DrawLevelField(x, y);
6093   }
6094
6095   if (impact && CAN_EXPLODE_IMPACT(element))
6096   {
6097     Bang(x, y);
6098     return;
6099   }
6100   else if (impact && element == EL_PEARL &&
6101            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6102   {
6103     ResetGfxAnimation(x, y);
6104
6105     Feld[x][y] = EL_PEARL_BREAKING;
6106     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6107     return;
6108   }
6109   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6110   {
6111     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6112
6113     return;
6114   }
6115
6116   if (impact && element == EL_AMOEBA_DROP)
6117   {
6118     if (object_hit && IS_PLAYER(x, y + 1))
6119       KillPlayerUnlessEnemyProtected(x, y + 1);
6120     else if (object_hit && smashed == EL_PENGUIN)
6121       Bang(x, y + 1);
6122     else
6123     {
6124       Feld[x][y] = EL_AMOEBA_GROWING;
6125       Store[x][y] = EL_AMOEBA_WET;
6126
6127       ResetRandomAnimationValue(x, y);
6128     }
6129     return;
6130   }
6131
6132   if (object_hit)               /* check which object was hit */
6133   {
6134     if ((CAN_PASS_MAGIC_WALL(element) && 
6135          (smashed == EL_MAGIC_WALL ||
6136           smashed == EL_BD_MAGIC_WALL)) ||
6137         (CAN_PASS_DC_MAGIC_WALL(element) &&
6138          smashed == EL_DC_MAGIC_WALL))
6139     {
6140       int xx, yy;
6141       int activated_magic_wall =
6142         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6143          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6144          EL_DC_MAGIC_WALL_ACTIVE);
6145
6146       /* activate magic wall / mill */
6147       SCAN_PLAYFIELD(xx, yy)
6148       {
6149         if (Feld[xx][yy] == smashed)
6150           Feld[xx][yy] = activated_magic_wall;
6151       }
6152
6153       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6154       game.magic_wall_active = TRUE;
6155
6156       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6157                             SND_MAGIC_WALL_ACTIVATING :
6158                             smashed == EL_BD_MAGIC_WALL ?
6159                             SND_BD_MAGIC_WALL_ACTIVATING :
6160                             SND_DC_MAGIC_WALL_ACTIVATING));
6161     }
6162
6163     if (IS_PLAYER(x, y + 1))
6164     {
6165       if (CAN_SMASH_PLAYER(element))
6166       {
6167         KillPlayerUnlessEnemyProtected(x, y + 1);
6168         return;
6169       }
6170     }
6171     else if (smashed == EL_PENGUIN)
6172     {
6173       if (CAN_SMASH_PLAYER(element))
6174       {
6175         Bang(x, y + 1);
6176         return;
6177       }
6178     }
6179     else if (element == EL_BD_DIAMOND)
6180     {
6181       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6182       {
6183         Bang(x, y + 1);
6184         return;
6185       }
6186     }
6187     else if (((element == EL_SP_INFOTRON ||
6188                element == EL_SP_ZONK) &&
6189               (smashed == EL_SP_SNIKSNAK ||
6190                smashed == EL_SP_ELECTRON ||
6191                smashed == EL_SP_DISK_ORANGE)) ||
6192              (element == EL_SP_INFOTRON &&
6193               smashed == EL_SP_DISK_YELLOW))
6194     {
6195       Bang(x, y + 1);
6196       return;
6197     }
6198     else if (CAN_SMASH_EVERYTHING(element))
6199     {
6200       if (IS_CLASSIC_ENEMY(smashed) ||
6201           CAN_EXPLODE_SMASHED(smashed))
6202       {
6203         Bang(x, y + 1);
6204         return;
6205       }
6206       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6207       {
6208         if (smashed == EL_LAMP ||
6209             smashed == EL_LAMP_ACTIVE)
6210         {
6211           Bang(x, y + 1);
6212           return;
6213         }
6214         else if (smashed == EL_NUT)
6215         {
6216           Feld[x][y + 1] = EL_NUT_BREAKING;
6217           PlayLevelSound(x, y, SND_NUT_BREAKING);
6218           RaiseScoreElement(EL_NUT);
6219           return;
6220         }
6221         else if (smashed == EL_PEARL)
6222         {
6223           ResetGfxAnimation(x, y);
6224
6225           Feld[x][y + 1] = EL_PEARL_BREAKING;
6226           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6227           return;
6228         }
6229         else if (smashed == EL_DIAMOND)
6230         {
6231           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6232           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6233           return;
6234         }
6235         else if (IS_BELT_SWITCH(smashed))
6236         {
6237           ToggleBeltSwitch(x, y + 1);
6238         }
6239         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6240                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6241                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6242                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6243         {
6244           ToggleSwitchgateSwitch(x, y + 1);
6245         }
6246         else if (smashed == EL_LIGHT_SWITCH ||
6247                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6248         {
6249           ToggleLightSwitch(x, y + 1);
6250         }
6251         else
6252         {
6253           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6254
6255           CheckElementChangeBySide(x, y + 1, smashed, element,
6256                                    CE_SWITCHED, CH_SIDE_TOP);
6257           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6258                                             CH_SIDE_TOP);
6259         }
6260       }
6261       else
6262       {
6263         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6264       }
6265     }
6266   }
6267
6268   /* play sound of magic wall / mill */
6269   if (!last_line &&
6270       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6271        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6272        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6273   {
6274     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6275       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6276     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6277       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6278     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6279       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6280
6281     return;
6282   }
6283
6284   /* play sound of object that hits the ground */
6285   if (last_line || object_hit)
6286     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6287 }
6288
6289 inline static void TurnRoundExt(int x, int y)
6290 {
6291   static struct
6292   {
6293     int dx, dy;
6294   } move_xy[] =
6295   {
6296     {  0,  0 },
6297     { -1,  0 },
6298     { +1,  0 },
6299     {  0,  0 },
6300     {  0, -1 },
6301     {  0,  0 }, { 0, 0 }, { 0, 0 },
6302     {  0, +1 }
6303   };
6304   static struct
6305   {
6306     int left, right, back;
6307   } turn[] =
6308   {
6309     { 0,        0,              0        },
6310     { MV_DOWN,  MV_UP,          MV_RIGHT },
6311     { MV_UP,    MV_DOWN,        MV_LEFT  },
6312     { 0,        0,              0        },
6313     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6314     { 0,        0,              0        },
6315     { 0,        0,              0        },
6316     { 0,        0,              0        },
6317     { MV_RIGHT, MV_LEFT,        MV_UP    }
6318   };
6319
6320   int element = Feld[x][y];
6321   int move_pattern = element_info[element].move_pattern;
6322
6323   int old_move_dir = MovDir[x][y];
6324   int left_dir  = turn[old_move_dir].left;
6325   int right_dir = turn[old_move_dir].right;
6326   int back_dir  = turn[old_move_dir].back;
6327
6328   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6329   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6330   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6331   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6332
6333   int left_x  = x + left_dx,  left_y  = y + left_dy;
6334   int right_x = x + right_dx, right_y = y + right_dy;
6335   int move_x  = x + move_dx,  move_y  = y + move_dy;
6336
6337   int xx, yy;
6338
6339   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6340   {
6341     TestIfBadThingTouchesOtherBadThing(x, y);
6342
6343     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6344       MovDir[x][y] = right_dir;
6345     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6346       MovDir[x][y] = left_dir;
6347
6348     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6349       MovDelay[x][y] = 9;
6350     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6351       MovDelay[x][y] = 1;
6352   }
6353   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6354   {
6355     TestIfBadThingTouchesOtherBadThing(x, y);
6356
6357     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6358       MovDir[x][y] = left_dir;
6359     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6360       MovDir[x][y] = right_dir;
6361
6362     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6363       MovDelay[x][y] = 9;
6364     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6365       MovDelay[x][y] = 1;
6366   }
6367   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6368   {
6369     TestIfBadThingTouchesOtherBadThing(x, y);
6370
6371     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6372       MovDir[x][y] = left_dir;
6373     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6374       MovDir[x][y] = right_dir;
6375
6376     if (MovDir[x][y] != old_move_dir)
6377       MovDelay[x][y] = 9;
6378   }
6379   else if (element == EL_YAMYAM)
6380   {
6381     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6382     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6383
6384     if (can_turn_left && can_turn_right)
6385       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6386     else if (can_turn_left)
6387       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6388     else if (can_turn_right)
6389       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6390     else
6391       MovDir[x][y] = back_dir;
6392
6393     MovDelay[x][y] = 16 + 16 * RND(3);
6394   }
6395   else if (element == EL_DARK_YAMYAM)
6396   {
6397     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6398                                                          left_x, left_y);
6399     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6400                                                          right_x, right_y);
6401
6402     if (can_turn_left && can_turn_right)
6403       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6404     else if (can_turn_left)
6405       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6406     else if (can_turn_right)
6407       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6408     else
6409       MovDir[x][y] = back_dir;
6410
6411     MovDelay[x][y] = 16 + 16 * RND(3);
6412   }
6413   else if (element == EL_PACMAN)
6414   {
6415     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6416     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6417
6418     if (can_turn_left && can_turn_right)
6419       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6420     else if (can_turn_left)
6421       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6422     else if (can_turn_right)
6423       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6424     else
6425       MovDir[x][y] = back_dir;
6426
6427     MovDelay[x][y] = 6 + RND(40);
6428   }
6429   else if (element == EL_PIG)
6430   {
6431     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6432     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6433     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6434     boolean should_turn_left, should_turn_right, should_move_on;
6435     int rnd_value = 24;
6436     int rnd = RND(rnd_value);
6437
6438     should_turn_left = (can_turn_left &&
6439                         (!can_move_on ||
6440                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6441                                                    y + back_dy + left_dy)));
6442     should_turn_right = (can_turn_right &&
6443                          (!can_move_on ||
6444                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6445                                                     y + back_dy + right_dy)));
6446     should_move_on = (can_move_on &&
6447                       (!can_turn_left ||
6448                        !can_turn_right ||
6449                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6450                                                  y + move_dy + left_dy) ||
6451                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6452                                                  y + move_dy + right_dy)));
6453
6454     if (should_turn_left || should_turn_right || should_move_on)
6455     {
6456       if (should_turn_left && should_turn_right && should_move_on)
6457         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6458                         rnd < 2 * rnd_value / 3 ? right_dir :
6459                         old_move_dir);
6460       else if (should_turn_left && should_turn_right)
6461         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6462       else if (should_turn_left && should_move_on)
6463         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6464       else if (should_turn_right && should_move_on)
6465         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6466       else if (should_turn_left)
6467         MovDir[x][y] = left_dir;
6468       else if (should_turn_right)
6469         MovDir[x][y] = right_dir;
6470       else if (should_move_on)
6471         MovDir[x][y] = old_move_dir;
6472     }
6473     else if (can_move_on && rnd > rnd_value / 8)
6474       MovDir[x][y] = old_move_dir;
6475     else if (can_turn_left && can_turn_right)
6476       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6477     else if (can_turn_left && rnd > rnd_value / 8)
6478       MovDir[x][y] = left_dir;
6479     else if (can_turn_right && rnd > rnd_value/8)
6480       MovDir[x][y] = right_dir;
6481     else
6482       MovDir[x][y] = back_dir;
6483
6484     xx = x + move_xy[MovDir[x][y]].dx;
6485     yy = y + move_xy[MovDir[x][y]].dy;
6486
6487     if (!IN_LEV_FIELD(xx, yy) ||
6488         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6489       MovDir[x][y] = old_move_dir;
6490
6491     MovDelay[x][y] = 0;
6492   }
6493   else if (element == EL_DRAGON)
6494   {
6495     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6496     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6497     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6498     int rnd_value = 24;
6499     int rnd = RND(rnd_value);
6500
6501     if (can_move_on && rnd > rnd_value / 8)
6502       MovDir[x][y] = old_move_dir;
6503     else if (can_turn_left && can_turn_right)
6504       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6505     else if (can_turn_left && rnd > rnd_value / 8)
6506       MovDir[x][y] = left_dir;
6507     else if (can_turn_right && rnd > rnd_value / 8)
6508       MovDir[x][y] = right_dir;
6509     else
6510       MovDir[x][y] = back_dir;
6511
6512     xx = x + move_xy[MovDir[x][y]].dx;
6513     yy = y + move_xy[MovDir[x][y]].dy;
6514
6515     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6516       MovDir[x][y] = old_move_dir;
6517
6518     MovDelay[x][y] = 0;
6519   }
6520   else if (element == EL_MOLE)
6521   {
6522     boolean can_move_on =
6523       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6524                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6525                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6526     if (!can_move_on)
6527     {
6528       boolean can_turn_left =
6529         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6530                               IS_AMOEBOID(Feld[left_x][left_y])));
6531
6532       boolean can_turn_right =
6533         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6534                               IS_AMOEBOID(Feld[right_x][right_y])));
6535
6536       if (can_turn_left && can_turn_right)
6537         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6538       else if (can_turn_left)
6539         MovDir[x][y] = left_dir;
6540       else
6541         MovDir[x][y] = right_dir;
6542     }
6543
6544     if (MovDir[x][y] != old_move_dir)
6545       MovDelay[x][y] = 9;
6546   }
6547   else if (element == EL_BALLOON)
6548   {
6549     MovDir[x][y] = game.wind_direction;
6550     MovDelay[x][y] = 0;
6551   }
6552   else if (element == EL_SPRING)
6553   {
6554     if (MovDir[x][y] & MV_HORIZONTAL)
6555     {
6556       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6557           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6558       {
6559         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6560         ResetGfxAnimation(move_x, move_y);
6561         TEST_DrawLevelField(move_x, move_y);
6562
6563         MovDir[x][y] = back_dir;
6564       }
6565       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6566                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6567         MovDir[x][y] = MV_NONE;
6568     }
6569
6570     MovDelay[x][y] = 0;
6571   }
6572   else if (element == EL_ROBOT ||
6573            element == EL_SATELLITE ||
6574            element == EL_PENGUIN ||
6575            element == EL_EMC_ANDROID)
6576   {
6577     int attr_x = -1, attr_y = -1;
6578
6579     if (AllPlayersGone)
6580     {
6581       attr_x = ExitX;
6582       attr_y = ExitY;
6583     }
6584     else
6585     {
6586       int i;
6587
6588       for (i = 0; i < MAX_PLAYERS; i++)
6589       {
6590         struct PlayerInfo *player = &stored_player[i];
6591         int jx = player->jx, jy = player->jy;
6592
6593         if (!player->active)
6594           continue;
6595
6596         if (attr_x == -1 ||
6597             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6598         {
6599           attr_x = jx;
6600           attr_y = jy;
6601         }
6602       }
6603     }
6604
6605     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6606         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6607          game.engine_version < VERSION_IDENT(3,1,0,0)))
6608     {
6609       attr_x = ZX;
6610       attr_y = ZY;
6611     }
6612
6613     if (element == EL_PENGUIN)
6614     {
6615       int i;
6616       static int xy[4][2] =
6617       {
6618         { 0, -1 },
6619         { -1, 0 },
6620         { +1, 0 },
6621         { 0, +1 }
6622       };
6623
6624       for (i = 0; i < NUM_DIRECTIONS; i++)
6625       {
6626         int ex = x + xy[i][0];
6627         int ey = y + xy[i][1];
6628
6629         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6630                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6631                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6632                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6633         {
6634           attr_x = ex;
6635           attr_y = ey;
6636           break;
6637         }
6638       }
6639     }
6640
6641     MovDir[x][y] = MV_NONE;
6642     if (attr_x < x)
6643       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6644     else if (attr_x > x)
6645       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6646     if (attr_y < y)
6647       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6648     else if (attr_y > y)
6649       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6650
6651     if (element == EL_ROBOT)
6652     {
6653       int newx, newy;
6654
6655       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6656         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6657       Moving2Blocked(x, y, &newx, &newy);
6658
6659       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6660         MovDelay[x][y] = 8 + 8 * !RND(3);
6661       else
6662         MovDelay[x][y] = 16;
6663     }
6664     else if (element == EL_PENGUIN)
6665     {
6666       int newx, newy;
6667
6668       MovDelay[x][y] = 1;
6669
6670       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6671       {
6672         boolean first_horiz = RND(2);
6673         int new_move_dir = MovDir[x][y];
6674
6675         MovDir[x][y] =
6676           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6677         Moving2Blocked(x, y, &newx, &newy);
6678
6679         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6680           return;
6681
6682         MovDir[x][y] =
6683           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6684         Moving2Blocked(x, y, &newx, &newy);
6685
6686         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6687           return;
6688
6689         MovDir[x][y] = old_move_dir;
6690         return;
6691       }
6692     }
6693     else if (element == EL_SATELLITE)
6694     {
6695       int newx, newy;
6696
6697       MovDelay[x][y] = 1;
6698
6699       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6700       {
6701         boolean first_horiz = RND(2);
6702         int new_move_dir = MovDir[x][y];
6703
6704         MovDir[x][y] =
6705           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6706         Moving2Blocked(x, y, &newx, &newy);
6707
6708         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6709           return;
6710
6711         MovDir[x][y] =
6712           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6713         Moving2Blocked(x, y, &newx, &newy);
6714
6715         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6716           return;
6717
6718         MovDir[x][y] = old_move_dir;
6719         return;
6720       }
6721     }
6722     else if (element == EL_EMC_ANDROID)
6723     {
6724       static int check_pos[16] =
6725       {
6726         -1,             /*  0 => (invalid)          */
6727         7,              /*  1 => MV_LEFT            */
6728         3,              /*  2 => MV_RIGHT           */
6729         -1,             /*  3 => (invalid)          */
6730         1,              /*  4 =>            MV_UP   */
6731         0,              /*  5 => MV_LEFT  | MV_UP   */
6732         2,              /*  6 => MV_RIGHT | MV_UP   */
6733         -1,             /*  7 => (invalid)          */
6734         5,              /*  8 =>            MV_DOWN */
6735         6,              /*  9 => MV_LEFT  | MV_DOWN */
6736         4,              /* 10 => MV_RIGHT | MV_DOWN */
6737         -1,             /* 11 => (invalid)          */
6738         -1,             /* 12 => (invalid)          */
6739         -1,             /* 13 => (invalid)          */
6740         -1,             /* 14 => (invalid)          */
6741         -1,             /* 15 => (invalid)          */
6742       };
6743       static struct
6744       {
6745         int dx, dy;
6746         int dir;
6747       } check_xy[8] =
6748       {
6749         { -1, -1,       MV_LEFT  | MV_UP   },
6750         {  0, -1,                  MV_UP   },
6751         { +1, -1,       MV_RIGHT | MV_UP   },
6752         { +1,  0,       MV_RIGHT           },
6753         { +1, +1,       MV_RIGHT | MV_DOWN },
6754         {  0, +1,                  MV_DOWN },
6755         { -1, +1,       MV_LEFT  | MV_DOWN },
6756         { -1,  0,       MV_LEFT            },
6757       };
6758       int start_pos, check_order;
6759       boolean can_clone = FALSE;
6760       int i;
6761
6762       /* check if there is any free field around current position */
6763       for (i = 0; i < 8; i++)
6764       {
6765         int newx = x + check_xy[i].dx;
6766         int newy = y + check_xy[i].dy;
6767
6768         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6769         {
6770           can_clone = TRUE;
6771
6772           break;
6773         }
6774       }
6775
6776       if (can_clone)            /* randomly find an element to clone */
6777       {
6778         can_clone = FALSE;
6779
6780         start_pos = check_pos[RND(8)];
6781         check_order = (RND(2) ? -1 : +1);
6782
6783         for (i = 0; i < 8; i++)
6784         {
6785           int pos_raw = start_pos + i * check_order;
6786           int pos = (pos_raw + 8) % 8;
6787           int newx = x + check_xy[pos].dx;
6788           int newy = y + check_xy[pos].dy;
6789
6790           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6791           {
6792             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6793             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6794
6795             Store[x][y] = Feld[newx][newy];
6796
6797             can_clone = TRUE;
6798
6799             break;
6800           }
6801         }
6802       }
6803
6804       if (can_clone)            /* randomly find a direction to move */
6805       {
6806         can_clone = FALSE;
6807
6808         start_pos = check_pos[RND(8)];
6809         check_order = (RND(2) ? -1 : +1);
6810
6811         for (i = 0; i < 8; i++)
6812         {
6813           int pos_raw = start_pos + i * check_order;
6814           int pos = (pos_raw + 8) % 8;
6815           int newx = x + check_xy[pos].dx;
6816           int newy = y + check_xy[pos].dy;
6817           int new_move_dir = check_xy[pos].dir;
6818
6819           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6820           {
6821             MovDir[x][y] = new_move_dir;
6822             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6823
6824             can_clone = TRUE;
6825
6826             break;
6827           }
6828         }
6829       }
6830
6831       if (can_clone)            /* cloning and moving successful */
6832         return;
6833
6834       /* cannot clone -- try to move towards player */
6835
6836       start_pos = check_pos[MovDir[x][y] & 0x0f];
6837       check_order = (RND(2) ? -1 : +1);
6838
6839       for (i = 0; i < 3; i++)
6840       {
6841         /* first check start_pos, then previous/next or (next/previous) pos */
6842         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6843         int pos = (pos_raw + 8) % 8;
6844         int newx = x + check_xy[pos].dx;
6845         int newy = y + check_xy[pos].dy;
6846         int new_move_dir = check_xy[pos].dir;
6847
6848         if (IS_PLAYER(newx, newy))
6849           break;
6850
6851         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6852         {
6853           MovDir[x][y] = new_move_dir;
6854           MovDelay[x][y] = level.android_move_time * 8 + 1;
6855
6856           break;
6857         }
6858       }
6859     }
6860   }
6861   else if (move_pattern == MV_TURNING_LEFT ||
6862            move_pattern == MV_TURNING_RIGHT ||
6863            move_pattern == MV_TURNING_LEFT_RIGHT ||
6864            move_pattern == MV_TURNING_RIGHT_LEFT ||
6865            move_pattern == MV_TURNING_RANDOM ||
6866            move_pattern == MV_ALL_DIRECTIONS)
6867   {
6868     boolean can_turn_left =
6869       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6870     boolean can_turn_right =
6871       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6872
6873     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6874       return;
6875
6876     if (move_pattern == MV_TURNING_LEFT)
6877       MovDir[x][y] = left_dir;
6878     else if (move_pattern == MV_TURNING_RIGHT)
6879       MovDir[x][y] = right_dir;
6880     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6881       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6882     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6883       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6884     else if (move_pattern == MV_TURNING_RANDOM)
6885       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6886                       can_turn_right && !can_turn_left ? right_dir :
6887                       RND(2) ? left_dir : right_dir);
6888     else if (can_turn_left && can_turn_right)
6889       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6890     else if (can_turn_left)
6891       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6892     else if (can_turn_right)
6893       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6894     else
6895       MovDir[x][y] = back_dir;
6896
6897     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6898   }
6899   else if (move_pattern == MV_HORIZONTAL ||
6900            move_pattern == MV_VERTICAL)
6901   {
6902     if (move_pattern & old_move_dir)
6903       MovDir[x][y] = back_dir;
6904     else if (move_pattern == MV_HORIZONTAL)
6905       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6906     else if (move_pattern == MV_VERTICAL)
6907       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6908
6909     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6910   }
6911   else if (move_pattern & MV_ANY_DIRECTION)
6912   {
6913     MovDir[x][y] = move_pattern;
6914     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6915   }
6916   else if (move_pattern & MV_WIND_DIRECTION)
6917   {
6918     MovDir[x][y] = game.wind_direction;
6919     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6920   }
6921   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6922   {
6923     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6924       MovDir[x][y] = left_dir;
6925     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6926       MovDir[x][y] = right_dir;
6927
6928     if (MovDir[x][y] != old_move_dir)
6929       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6930   }
6931   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6932   {
6933     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6934       MovDir[x][y] = right_dir;
6935     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6936       MovDir[x][y] = left_dir;
6937
6938     if (MovDir[x][y] != old_move_dir)
6939       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6940   }
6941   else if (move_pattern == MV_TOWARDS_PLAYER ||
6942            move_pattern == MV_AWAY_FROM_PLAYER)
6943   {
6944     int attr_x = -1, attr_y = -1;
6945     int newx, newy;
6946     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6947
6948     if (AllPlayersGone)
6949     {
6950       attr_x = ExitX;
6951       attr_y = ExitY;
6952     }
6953     else
6954     {
6955       int i;
6956
6957       for (i = 0; i < MAX_PLAYERS; i++)
6958       {
6959         struct PlayerInfo *player = &stored_player[i];
6960         int jx = player->jx, jy = player->jy;
6961
6962         if (!player->active)
6963           continue;
6964
6965         if (attr_x == -1 ||
6966             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6967         {
6968           attr_x = jx;
6969           attr_y = jy;
6970         }
6971       }
6972     }
6973
6974     MovDir[x][y] = MV_NONE;
6975     if (attr_x < x)
6976       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6977     else if (attr_x > x)
6978       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6979     if (attr_y < y)
6980       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6981     else if (attr_y > y)
6982       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6983
6984     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6985
6986     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6987     {
6988       boolean first_horiz = RND(2);
6989       int new_move_dir = MovDir[x][y];
6990
6991       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6992       {
6993         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6994         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6995
6996         return;
6997       }
6998
6999       MovDir[x][y] =
7000         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7001       Moving2Blocked(x, y, &newx, &newy);
7002
7003       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7004         return;
7005
7006       MovDir[x][y] =
7007         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7008       Moving2Blocked(x, y, &newx, &newy);
7009
7010       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7011         return;
7012
7013       MovDir[x][y] = old_move_dir;
7014     }
7015   }
7016   else if (move_pattern == MV_WHEN_PUSHED ||
7017            move_pattern == MV_WHEN_DROPPED)
7018   {
7019     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7020       MovDir[x][y] = MV_NONE;
7021
7022     MovDelay[x][y] = 0;
7023   }
7024   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7025   {
7026     static int test_xy[7][2] =
7027     {
7028       { 0, -1 },
7029       { -1, 0 },
7030       { +1, 0 },
7031       { 0, +1 },
7032       { 0, -1 },
7033       { -1, 0 },
7034       { +1, 0 },
7035     };
7036     static int test_dir[7] =
7037     {
7038       MV_UP,
7039       MV_LEFT,
7040       MV_RIGHT,
7041       MV_DOWN,
7042       MV_UP,
7043       MV_LEFT,
7044       MV_RIGHT,
7045     };
7046     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7047     int move_preference = -1000000;     /* start with very low preference */
7048     int new_move_dir = MV_NONE;
7049     int start_test = RND(4);
7050     int i;
7051
7052     for (i = 0; i < NUM_DIRECTIONS; i++)
7053     {
7054       int move_dir = test_dir[start_test + i];
7055       int move_dir_preference;
7056
7057       xx = x + test_xy[start_test + i][0];
7058       yy = y + test_xy[start_test + i][1];
7059
7060       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7061           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7062       {
7063         new_move_dir = move_dir;
7064
7065         break;
7066       }
7067
7068       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7069         continue;
7070
7071       move_dir_preference = -1 * RunnerVisit[xx][yy];
7072       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7073         move_dir_preference = PlayerVisit[xx][yy];
7074
7075       if (move_dir_preference > move_preference)
7076       {
7077         /* prefer field that has not been visited for the longest time */
7078         move_preference = move_dir_preference;
7079         new_move_dir = move_dir;
7080       }
7081       else if (move_dir_preference == move_preference &&
7082                move_dir == old_move_dir)
7083       {
7084         /* prefer last direction when all directions are preferred equally */
7085         move_preference = move_dir_preference;
7086         new_move_dir = move_dir;
7087       }
7088     }
7089
7090     MovDir[x][y] = new_move_dir;
7091     if (old_move_dir != new_move_dir)
7092       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7093   }
7094 }
7095
7096 static void TurnRound(int x, int y)
7097 {
7098   int direction = MovDir[x][y];
7099
7100   TurnRoundExt(x, y);
7101
7102   GfxDir[x][y] = MovDir[x][y];
7103
7104   if (direction != MovDir[x][y])
7105     GfxFrame[x][y] = 0;
7106
7107   if (MovDelay[x][y])
7108     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7109
7110   ResetGfxFrame(x, y, FALSE);
7111 }
7112
7113 static boolean JustBeingPushed(int x, int y)
7114 {
7115   int i;
7116
7117   for (i = 0; i < MAX_PLAYERS; i++)
7118   {
7119     struct PlayerInfo *player = &stored_player[i];
7120
7121     if (player->active && player->is_pushing && player->MovPos)
7122     {
7123       int next_jx = player->jx + (player->jx - player->last_jx);
7124       int next_jy = player->jy + (player->jy - player->last_jy);
7125
7126       if (x == next_jx && y == next_jy)
7127         return TRUE;
7128     }
7129   }
7130
7131   return FALSE;
7132 }
7133
7134 void StartMoving(int x, int y)
7135 {
7136   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7137   int element = Feld[x][y];
7138
7139   if (Stop[x][y])
7140     return;
7141
7142   if (MovDelay[x][y] == 0)
7143     GfxAction[x][y] = ACTION_DEFAULT;
7144
7145   if (CAN_FALL(element) && y < lev_fieldy - 1)
7146   {
7147     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7148         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7149       if (JustBeingPushed(x, y))
7150         return;
7151
7152     if (element == EL_QUICKSAND_FULL)
7153     {
7154       if (IS_FREE(x, y + 1))
7155       {
7156         InitMovingField(x, y, MV_DOWN);
7157         started_moving = TRUE;
7158
7159         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7160 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7161         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7162           Store[x][y] = EL_ROCK;
7163 #else
7164         Store[x][y] = EL_ROCK;
7165 #endif
7166
7167         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7168       }
7169       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7170       {
7171         if (!MovDelay[x][y])
7172         {
7173           MovDelay[x][y] = TILEY + 1;
7174
7175           ResetGfxAnimation(x, y);
7176           ResetGfxAnimation(x, y + 1);
7177         }
7178
7179         if (MovDelay[x][y])
7180         {
7181           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7182           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7183
7184           MovDelay[x][y]--;
7185           if (MovDelay[x][y])
7186             return;
7187         }
7188
7189         Feld[x][y] = EL_QUICKSAND_EMPTY;
7190         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7191         Store[x][y + 1] = Store[x][y];
7192         Store[x][y] = 0;
7193
7194         PlayLevelSoundAction(x, y, ACTION_FILLING);
7195       }
7196       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7197       {
7198         if (!MovDelay[x][y])
7199         {
7200           MovDelay[x][y] = TILEY + 1;
7201
7202           ResetGfxAnimation(x, y);
7203           ResetGfxAnimation(x, y + 1);
7204         }
7205
7206         if (MovDelay[x][y])
7207         {
7208           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7209           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7210
7211           MovDelay[x][y]--;
7212           if (MovDelay[x][y])
7213             return;
7214         }
7215
7216         Feld[x][y] = EL_QUICKSAND_EMPTY;
7217         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7218         Store[x][y + 1] = Store[x][y];
7219         Store[x][y] = 0;
7220
7221         PlayLevelSoundAction(x, y, ACTION_FILLING);
7222       }
7223     }
7224     else if (element == EL_QUICKSAND_FAST_FULL)
7225     {
7226       if (IS_FREE(x, y + 1))
7227       {
7228         InitMovingField(x, y, MV_DOWN);
7229         started_moving = TRUE;
7230
7231         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7232 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7233         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7234           Store[x][y] = EL_ROCK;
7235 #else
7236         Store[x][y] = EL_ROCK;
7237 #endif
7238
7239         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7240       }
7241       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7242       {
7243         if (!MovDelay[x][y])
7244         {
7245           MovDelay[x][y] = TILEY + 1;
7246
7247           ResetGfxAnimation(x, y);
7248           ResetGfxAnimation(x, y + 1);
7249         }
7250
7251         if (MovDelay[x][y])
7252         {
7253           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7254           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7255
7256           MovDelay[x][y]--;
7257           if (MovDelay[x][y])
7258             return;
7259         }
7260
7261         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7262         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7263         Store[x][y + 1] = Store[x][y];
7264         Store[x][y] = 0;
7265
7266         PlayLevelSoundAction(x, y, ACTION_FILLING);
7267       }
7268       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7269       {
7270         if (!MovDelay[x][y])
7271         {
7272           MovDelay[x][y] = TILEY + 1;
7273
7274           ResetGfxAnimation(x, y);
7275           ResetGfxAnimation(x, y + 1);
7276         }
7277
7278         if (MovDelay[x][y])
7279         {
7280           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7281           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7282
7283           MovDelay[x][y]--;
7284           if (MovDelay[x][y])
7285             return;
7286         }
7287
7288         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7289         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7290         Store[x][y + 1] = Store[x][y];
7291         Store[x][y] = 0;
7292
7293         PlayLevelSoundAction(x, y, ACTION_FILLING);
7294       }
7295     }
7296     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7297              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7298     {
7299       InitMovingField(x, y, MV_DOWN);
7300       started_moving = TRUE;
7301
7302       Feld[x][y] = EL_QUICKSAND_FILLING;
7303       Store[x][y] = element;
7304
7305       PlayLevelSoundAction(x, y, ACTION_FILLING);
7306     }
7307     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7308              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7309     {
7310       InitMovingField(x, y, MV_DOWN);
7311       started_moving = TRUE;
7312
7313       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7314       Store[x][y] = element;
7315
7316       PlayLevelSoundAction(x, y, ACTION_FILLING);
7317     }
7318     else if (element == EL_MAGIC_WALL_FULL)
7319     {
7320       if (IS_FREE(x, y + 1))
7321       {
7322         InitMovingField(x, y, MV_DOWN);
7323         started_moving = TRUE;
7324
7325         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7326         Store[x][y] = EL_CHANGED(Store[x][y]);
7327       }
7328       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7329       {
7330         if (!MovDelay[x][y])
7331           MovDelay[x][y] = TILEY / 4 + 1;
7332
7333         if (MovDelay[x][y])
7334         {
7335           MovDelay[x][y]--;
7336           if (MovDelay[x][y])
7337             return;
7338         }
7339
7340         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7341         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7342         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7343         Store[x][y] = 0;
7344       }
7345     }
7346     else if (element == EL_BD_MAGIC_WALL_FULL)
7347     {
7348       if (IS_FREE(x, y + 1))
7349       {
7350         InitMovingField(x, y, MV_DOWN);
7351         started_moving = TRUE;
7352
7353         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7354         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7355       }
7356       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7357       {
7358         if (!MovDelay[x][y])
7359           MovDelay[x][y] = TILEY / 4 + 1;
7360
7361         if (MovDelay[x][y])
7362         {
7363           MovDelay[x][y]--;
7364           if (MovDelay[x][y])
7365             return;
7366         }
7367
7368         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7369         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7370         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7371         Store[x][y] = 0;
7372       }
7373     }
7374     else if (element == EL_DC_MAGIC_WALL_FULL)
7375     {
7376       if (IS_FREE(x, y + 1))
7377       {
7378         InitMovingField(x, y, MV_DOWN);
7379         started_moving = TRUE;
7380
7381         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7382         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7383       }
7384       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7385       {
7386         if (!MovDelay[x][y])
7387           MovDelay[x][y] = TILEY / 4 + 1;
7388
7389         if (MovDelay[x][y])
7390         {
7391           MovDelay[x][y]--;
7392           if (MovDelay[x][y])
7393             return;
7394         }
7395
7396         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7397         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7398         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7399         Store[x][y] = 0;
7400       }
7401     }
7402     else if ((CAN_PASS_MAGIC_WALL(element) &&
7403               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7404                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7405              (CAN_PASS_DC_MAGIC_WALL(element) &&
7406               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7407
7408     {
7409       InitMovingField(x, y, MV_DOWN);
7410       started_moving = TRUE;
7411
7412       Feld[x][y] =
7413         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7414          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7415          EL_DC_MAGIC_WALL_FILLING);
7416       Store[x][y] = element;
7417     }
7418     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7419     {
7420       SplashAcid(x, y + 1);
7421
7422       InitMovingField(x, y, MV_DOWN);
7423       started_moving = TRUE;
7424
7425       Store[x][y] = EL_ACID;
7426     }
7427     else if (
7428              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7429               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7430              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7431               CAN_FALL(element) && WasJustFalling[x][y] &&
7432               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7433
7434              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7435               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7436               (Feld[x][y + 1] == EL_BLOCKED)))
7437     {
7438       /* this is needed for a special case not covered by calling "Impact()"
7439          from "ContinueMoving()": if an element moves to a tile directly below
7440          another element which was just falling on that tile (which was empty
7441          in the previous frame), the falling element above would just stop
7442          instead of smashing the element below (in previous version, the above
7443          element was just checked for "moving" instead of "falling", resulting
7444          in incorrect smashes caused by horizontal movement of the above
7445          element; also, the case of the player being the element to smash was
7446          simply not covered here... :-/ ) */
7447
7448       CheckCollision[x][y] = 0;
7449       CheckImpact[x][y] = 0;
7450
7451       Impact(x, y);
7452     }
7453     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7454     {
7455       if (MovDir[x][y] == MV_NONE)
7456       {
7457         InitMovingField(x, y, MV_DOWN);
7458         started_moving = TRUE;
7459       }
7460     }
7461     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7462     {
7463       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7464         MovDir[x][y] = MV_DOWN;
7465
7466       InitMovingField(x, y, MV_DOWN);
7467       started_moving = TRUE;
7468     }
7469     else if (element == EL_AMOEBA_DROP)
7470     {
7471       Feld[x][y] = EL_AMOEBA_GROWING;
7472       Store[x][y] = EL_AMOEBA_WET;
7473     }
7474     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7475               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7476              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7477              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7478     {
7479       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7480                                 (IS_FREE(x - 1, y + 1) ||
7481                                  Feld[x - 1][y + 1] == EL_ACID));
7482       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7483                                 (IS_FREE(x + 1, y + 1) ||
7484                                  Feld[x + 1][y + 1] == EL_ACID));
7485       boolean can_fall_any  = (can_fall_left || can_fall_right);
7486       boolean can_fall_both = (can_fall_left && can_fall_right);
7487       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7488
7489       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7490       {
7491         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7492           can_fall_right = FALSE;
7493         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7494           can_fall_left = FALSE;
7495         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7496           can_fall_right = FALSE;
7497         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7498           can_fall_left = FALSE;
7499
7500         can_fall_any  = (can_fall_left || can_fall_right);
7501         can_fall_both = FALSE;
7502       }
7503
7504       if (can_fall_both)
7505       {
7506         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7507           can_fall_right = FALSE;       /* slip down on left side */
7508         else
7509           can_fall_left = !(can_fall_right = RND(2));
7510
7511         can_fall_both = FALSE;
7512       }
7513
7514       if (can_fall_any)
7515       {
7516         /* if not determined otherwise, prefer left side for slipping down */
7517         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7518         started_moving = TRUE;
7519       }
7520     }
7521     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7522     {
7523       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7524       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7525       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7526       int belt_dir = game.belt_dir[belt_nr];
7527
7528       if ((belt_dir == MV_LEFT  && left_is_free) ||
7529           (belt_dir == MV_RIGHT && right_is_free))
7530       {
7531         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7532
7533         InitMovingField(x, y, belt_dir);
7534         started_moving = TRUE;
7535
7536         Pushed[x][y] = TRUE;
7537         Pushed[nextx][y] = TRUE;
7538
7539         GfxAction[x][y] = ACTION_DEFAULT;
7540       }
7541       else
7542       {
7543         MovDir[x][y] = 0;       /* if element was moving, stop it */
7544       }
7545     }
7546   }
7547
7548   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7549   if (CAN_MOVE(element) && !started_moving)
7550   {
7551     int move_pattern = element_info[element].move_pattern;
7552     int newx, newy;
7553
7554     Moving2Blocked(x, y, &newx, &newy);
7555
7556     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7557       return;
7558
7559     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7560         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7561     {
7562       WasJustMoving[x][y] = 0;
7563       CheckCollision[x][y] = 0;
7564
7565       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7566
7567       if (Feld[x][y] != element)        /* element has changed */
7568         return;
7569     }
7570
7571     if (!MovDelay[x][y])        /* start new movement phase */
7572     {
7573       /* all objects that can change their move direction after each step
7574          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7575
7576       if (element != EL_YAMYAM &&
7577           element != EL_DARK_YAMYAM &&
7578           element != EL_PACMAN &&
7579           !(move_pattern & MV_ANY_DIRECTION) &&
7580           move_pattern != MV_TURNING_LEFT &&
7581           move_pattern != MV_TURNING_RIGHT &&
7582           move_pattern != MV_TURNING_LEFT_RIGHT &&
7583           move_pattern != MV_TURNING_RIGHT_LEFT &&
7584           move_pattern != MV_TURNING_RANDOM)
7585       {
7586         TurnRound(x, y);
7587
7588         if (MovDelay[x][y] && (element == EL_BUG ||
7589                                element == EL_SPACESHIP ||
7590                                element == EL_SP_SNIKSNAK ||
7591                                element == EL_SP_ELECTRON ||
7592                                element == EL_MOLE))
7593           TEST_DrawLevelField(x, y);
7594       }
7595     }
7596
7597     if (MovDelay[x][y])         /* wait some time before next movement */
7598     {
7599       MovDelay[x][y]--;
7600
7601       if (element == EL_ROBOT ||
7602           element == EL_YAMYAM ||
7603           element == EL_DARK_YAMYAM)
7604       {
7605         DrawLevelElementAnimationIfNeeded(x, y, element);
7606         PlayLevelSoundAction(x, y, ACTION_WAITING);
7607       }
7608       else if (element == EL_SP_ELECTRON)
7609         DrawLevelElementAnimationIfNeeded(x, y, element);
7610       else if (element == EL_DRAGON)
7611       {
7612         int i;
7613         int dir = MovDir[x][y];
7614         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7615         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7616         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7617                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7618                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7619                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7620         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7621
7622         GfxAction[x][y] = ACTION_ATTACKING;
7623
7624         if (IS_PLAYER(x, y))
7625           DrawPlayerField(x, y);
7626         else
7627           TEST_DrawLevelField(x, y);
7628
7629         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7630
7631         for (i = 1; i <= 3; i++)
7632         {
7633           int xx = x + i * dx;
7634           int yy = y + i * dy;
7635           int sx = SCREENX(xx);
7636           int sy = SCREENY(yy);
7637           int flame_graphic = graphic + (i - 1);
7638
7639           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7640             break;
7641
7642           if (MovDelay[x][y])
7643           {
7644             int flamed = MovingOrBlocked2Element(xx, yy);
7645
7646             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7647               Bang(xx, yy);
7648             else
7649               RemoveMovingField(xx, yy);
7650
7651             ChangeDelay[xx][yy] = 0;
7652
7653             Feld[xx][yy] = EL_FLAMES;
7654
7655             if (IN_SCR_FIELD(sx, sy))
7656             {
7657               TEST_DrawLevelFieldCrumbled(xx, yy);
7658               DrawGraphic(sx, sy, flame_graphic, frame);
7659             }
7660           }
7661           else
7662           {
7663             if (Feld[xx][yy] == EL_FLAMES)
7664               Feld[xx][yy] = EL_EMPTY;
7665             TEST_DrawLevelField(xx, yy);
7666           }
7667         }
7668       }
7669
7670       if (MovDelay[x][y])       /* element still has to wait some time */
7671       {
7672         PlayLevelSoundAction(x, y, ACTION_WAITING);
7673
7674         return;
7675       }
7676     }
7677
7678     /* now make next step */
7679
7680     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7681
7682     if (DONT_COLLIDE_WITH(element) &&
7683         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7684         !PLAYER_ENEMY_PROTECTED(newx, newy))
7685     {
7686       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7687
7688       return;
7689     }
7690
7691     else if (CAN_MOVE_INTO_ACID(element) &&
7692              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7693              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7694              (MovDir[x][y] == MV_DOWN ||
7695               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7696     {
7697       SplashAcid(newx, newy);
7698       Store[x][y] = EL_ACID;
7699     }
7700     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7701     {
7702       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7703           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7704           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7705           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7706       {
7707         RemoveField(x, y);
7708         TEST_DrawLevelField(x, y);
7709
7710         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7711         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7712           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7713
7714         local_player->friends_still_needed--;
7715         if (!local_player->friends_still_needed &&
7716             !local_player->GameOver && AllPlayersGone)
7717           PlayerWins(local_player);
7718
7719         return;
7720       }
7721       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7722       {
7723         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7724           TEST_DrawLevelField(newx, newy);
7725         else
7726           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7727       }
7728       else if (!IS_FREE(newx, newy))
7729       {
7730         GfxAction[x][y] = ACTION_WAITING;
7731
7732         if (IS_PLAYER(x, y))
7733           DrawPlayerField(x, y);
7734         else
7735           TEST_DrawLevelField(x, y);
7736
7737         return;
7738       }
7739     }
7740     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7741     {
7742       if (IS_FOOD_PIG(Feld[newx][newy]))
7743       {
7744         if (IS_MOVING(newx, newy))
7745           RemoveMovingField(newx, newy);
7746         else
7747         {
7748           Feld[newx][newy] = EL_EMPTY;
7749           TEST_DrawLevelField(newx, newy);
7750         }
7751
7752         PlayLevelSound(x, y, SND_PIG_DIGGING);
7753       }
7754       else if (!IS_FREE(newx, newy))
7755       {
7756         if (IS_PLAYER(x, y))
7757           DrawPlayerField(x, y);
7758         else
7759           TEST_DrawLevelField(x, y);
7760
7761         return;
7762       }
7763     }
7764     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7765     {
7766       if (Store[x][y] != EL_EMPTY)
7767       {
7768         boolean can_clone = FALSE;
7769         int xx, yy;
7770
7771         /* check if element to clone is still there */
7772         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7773         {
7774           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7775           {
7776             can_clone = TRUE;
7777
7778             break;
7779           }
7780         }
7781
7782         /* cannot clone or target field not free anymore -- do not clone */
7783         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7784           Store[x][y] = EL_EMPTY;
7785       }
7786
7787       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7788       {
7789         if (IS_MV_DIAGONAL(MovDir[x][y]))
7790         {
7791           int diagonal_move_dir = MovDir[x][y];
7792           int stored = Store[x][y];
7793           int change_delay = 8;
7794           int graphic;
7795
7796           /* android is moving diagonally */
7797
7798           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7799
7800           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7801           GfxElement[x][y] = EL_EMC_ANDROID;
7802           GfxAction[x][y] = ACTION_SHRINKING;
7803           GfxDir[x][y] = diagonal_move_dir;
7804           ChangeDelay[x][y] = change_delay;
7805
7806           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7807                                    GfxDir[x][y]);
7808
7809           DrawLevelGraphicAnimation(x, y, graphic);
7810           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7811
7812           if (Feld[newx][newy] == EL_ACID)
7813           {
7814             SplashAcid(newx, newy);
7815
7816             return;
7817           }
7818
7819           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7820
7821           Store[newx][newy] = EL_EMC_ANDROID;
7822           GfxElement[newx][newy] = EL_EMC_ANDROID;
7823           GfxAction[newx][newy] = ACTION_GROWING;
7824           GfxDir[newx][newy] = diagonal_move_dir;
7825           ChangeDelay[newx][newy] = change_delay;
7826
7827           graphic = el_act_dir2img(GfxElement[newx][newy],
7828                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7829
7830           DrawLevelGraphicAnimation(newx, newy, graphic);
7831           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7832
7833           return;
7834         }
7835         else
7836         {
7837           Feld[newx][newy] = EL_EMPTY;
7838           TEST_DrawLevelField(newx, newy);
7839
7840           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7841         }
7842       }
7843       else if (!IS_FREE(newx, newy))
7844       {
7845         return;
7846       }
7847     }
7848     else if (IS_CUSTOM_ELEMENT(element) &&
7849              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7850     {
7851       if (!DigFieldByCE(newx, newy, element))
7852         return;
7853
7854       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7855       {
7856         RunnerVisit[x][y] = FrameCounter;
7857         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7858       }
7859     }
7860     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7861     {
7862       if (!IS_FREE(newx, newy))
7863       {
7864         if (IS_PLAYER(x, y))
7865           DrawPlayerField(x, y);
7866         else
7867           TEST_DrawLevelField(x, y);
7868
7869         return;
7870       }
7871       else
7872       {
7873         boolean wanna_flame = !RND(10);
7874         int dx = newx - x, dy = newy - y;
7875         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7876         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7877         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7878                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7879         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7880                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7881
7882         if ((wanna_flame ||
7883              IS_CLASSIC_ENEMY(element1) ||
7884              IS_CLASSIC_ENEMY(element2)) &&
7885             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7886             element1 != EL_FLAMES && element2 != EL_FLAMES)
7887         {
7888           ResetGfxAnimation(x, y);
7889           GfxAction[x][y] = ACTION_ATTACKING;
7890
7891           if (IS_PLAYER(x, y))
7892             DrawPlayerField(x, y);
7893           else
7894             TEST_DrawLevelField(x, y);
7895
7896           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7897
7898           MovDelay[x][y] = 50;
7899
7900           Feld[newx][newy] = EL_FLAMES;
7901           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7902             Feld[newx1][newy1] = EL_FLAMES;
7903           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7904             Feld[newx2][newy2] = EL_FLAMES;
7905
7906           return;
7907         }
7908       }
7909     }
7910     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7911              Feld[newx][newy] == EL_DIAMOND)
7912     {
7913       if (IS_MOVING(newx, newy))
7914         RemoveMovingField(newx, newy);
7915       else
7916       {
7917         Feld[newx][newy] = EL_EMPTY;
7918         TEST_DrawLevelField(newx, newy);
7919       }
7920
7921       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7922     }
7923     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7924              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7925     {
7926       if (AmoebaNr[newx][newy])
7927       {
7928         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7929         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7930             Feld[newx][newy] == EL_BD_AMOEBA)
7931           AmoebaCnt[AmoebaNr[newx][newy]]--;
7932       }
7933
7934       if (IS_MOVING(newx, newy))
7935       {
7936         RemoveMovingField(newx, newy);
7937       }
7938       else
7939       {
7940         Feld[newx][newy] = EL_EMPTY;
7941         TEST_DrawLevelField(newx, newy);
7942       }
7943
7944       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7945     }
7946     else if ((element == EL_PACMAN || element == EL_MOLE)
7947              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7948     {
7949       if (AmoebaNr[newx][newy])
7950       {
7951         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7952         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7953             Feld[newx][newy] == EL_BD_AMOEBA)
7954           AmoebaCnt[AmoebaNr[newx][newy]]--;
7955       }
7956
7957       if (element == EL_MOLE)
7958       {
7959         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7960         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7961
7962         ResetGfxAnimation(x, y);
7963         GfxAction[x][y] = ACTION_DIGGING;
7964         TEST_DrawLevelField(x, y);
7965
7966         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7967
7968         return;                         /* wait for shrinking amoeba */
7969       }
7970       else      /* element == EL_PACMAN */
7971       {
7972         Feld[newx][newy] = EL_EMPTY;
7973         TEST_DrawLevelField(newx, newy);
7974         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7975       }
7976     }
7977     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7978              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7979               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7980     {
7981       /* wait for shrinking amoeba to completely disappear */
7982       return;
7983     }
7984     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7985     {
7986       /* object was running against a wall */
7987
7988       TurnRound(x, y);
7989
7990       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7991         DrawLevelElementAnimation(x, y, element);
7992
7993       if (DONT_TOUCH(element))
7994         TestIfBadThingTouchesPlayer(x, y);
7995
7996       return;
7997     }
7998
7999     InitMovingField(x, y, MovDir[x][y]);
8000
8001     PlayLevelSoundAction(x, y, ACTION_MOVING);
8002   }
8003
8004   if (MovDir[x][y])
8005     ContinueMoving(x, y);
8006 }
8007
8008 void ContinueMoving(int x, int y)
8009 {
8010   int element = Feld[x][y];
8011   struct ElementInfo *ei = &element_info[element];
8012   int direction = MovDir[x][y];
8013   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8014   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8015   int newx = x + dx, newy = y + dy;
8016   int stored = Store[x][y];
8017   int stored_new = Store[newx][newy];
8018   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8019   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8020   boolean last_line = (newy == lev_fieldy - 1);
8021
8022   MovPos[x][y] += getElementMoveStepsize(x, y);
8023
8024   if (pushed_by_player) /* special case: moving object pushed by player */
8025     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8026
8027   if (ABS(MovPos[x][y]) < TILEX)
8028   {
8029     TEST_DrawLevelField(x, y);
8030
8031     return;     /* element is still moving */
8032   }
8033
8034   /* element reached destination field */
8035
8036   Feld[x][y] = EL_EMPTY;
8037   Feld[newx][newy] = element;
8038   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8039
8040   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8041   {
8042     element = Feld[newx][newy] = EL_ACID;
8043   }
8044   else if (element == EL_MOLE)
8045   {
8046     Feld[x][y] = EL_SAND;
8047
8048     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8049   }
8050   else if (element == EL_QUICKSAND_FILLING)
8051   {
8052     element = Feld[newx][newy] = get_next_element(element);
8053     Store[newx][newy] = Store[x][y];
8054   }
8055   else if (element == EL_QUICKSAND_EMPTYING)
8056   {
8057     Feld[x][y] = get_next_element(element);
8058     element = Feld[newx][newy] = Store[x][y];
8059   }
8060   else if (element == EL_QUICKSAND_FAST_FILLING)
8061   {
8062     element = Feld[newx][newy] = get_next_element(element);
8063     Store[newx][newy] = Store[x][y];
8064   }
8065   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8066   {
8067     Feld[x][y] = get_next_element(element);
8068     element = Feld[newx][newy] = Store[x][y];
8069   }
8070   else if (element == EL_MAGIC_WALL_FILLING)
8071   {
8072     element = Feld[newx][newy] = get_next_element(element);
8073     if (!game.magic_wall_active)
8074       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8075     Store[newx][newy] = Store[x][y];
8076   }
8077   else if (element == EL_MAGIC_WALL_EMPTYING)
8078   {
8079     Feld[x][y] = get_next_element(element);
8080     if (!game.magic_wall_active)
8081       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8082     element = Feld[newx][newy] = Store[x][y];
8083
8084     InitField(newx, newy, FALSE);
8085   }
8086   else if (element == EL_BD_MAGIC_WALL_FILLING)
8087   {
8088     element = Feld[newx][newy] = get_next_element(element);
8089     if (!game.magic_wall_active)
8090       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8091     Store[newx][newy] = Store[x][y];
8092   }
8093   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8094   {
8095     Feld[x][y] = get_next_element(element);
8096     if (!game.magic_wall_active)
8097       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8098     element = Feld[newx][newy] = Store[x][y];
8099
8100     InitField(newx, newy, FALSE);
8101   }
8102   else if (element == EL_DC_MAGIC_WALL_FILLING)
8103   {
8104     element = Feld[newx][newy] = get_next_element(element);
8105     if (!game.magic_wall_active)
8106       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8107     Store[newx][newy] = Store[x][y];
8108   }
8109   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8110   {
8111     Feld[x][y] = get_next_element(element);
8112     if (!game.magic_wall_active)
8113       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8114     element = Feld[newx][newy] = Store[x][y];
8115
8116     InitField(newx, newy, FALSE);
8117   }
8118   else if (element == EL_AMOEBA_DROPPING)
8119   {
8120     Feld[x][y] = get_next_element(element);
8121     element = Feld[newx][newy] = Store[x][y];
8122   }
8123   else if (element == EL_SOKOBAN_OBJECT)
8124   {
8125     if (Back[x][y])
8126       Feld[x][y] = Back[x][y];
8127
8128     if (Back[newx][newy])
8129       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8130
8131     Back[x][y] = Back[newx][newy] = 0;
8132   }
8133
8134   Store[x][y] = EL_EMPTY;
8135   MovPos[x][y] = 0;
8136   MovDir[x][y] = 0;
8137   MovDelay[x][y] = 0;
8138
8139   MovDelay[newx][newy] = 0;
8140
8141   if (CAN_CHANGE_OR_HAS_ACTION(element))
8142   {
8143     /* copy element change control values to new field */
8144     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8145     ChangePage[newx][newy]  = ChangePage[x][y];
8146     ChangeCount[newx][newy] = ChangeCount[x][y];
8147     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8148   }
8149
8150   CustomValue[newx][newy] = CustomValue[x][y];
8151
8152   ChangeDelay[x][y] = 0;
8153   ChangePage[x][y] = -1;
8154   ChangeCount[x][y] = 0;
8155   ChangeEvent[x][y] = -1;
8156
8157   CustomValue[x][y] = 0;
8158
8159   /* copy animation control values to new field */
8160   GfxFrame[newx][newy]  = GfxFrame[x][y];
8161   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8162   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8163   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8164
8165   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8166
8167   /* some elements can leave other elements behind after moving */
8168   if (ei->move_leave_element != EL_EMPTY &&
8169       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8170       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8171   {
8172     int move_leave_element = ei->move_leave_element;
8173
8174     /* this makes it possible to leave the removed element again */
8175     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8176       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8177
8178     Feld[x][y] = move_leave_element;
8179
8180     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8181       MovDir[x][y] = direction;
8182
8183     InitField(x, y, FALSE);
8184
8185     if (GFX_CRUMBLED(Feld[x][y]))
8186       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8187
8188     if (ELEM_IS_PLAYER(move_leave_element))
8189       RelocatePlayer(x, y, move_leave_element);
8190   }
8191
8192   /* do this after checking for left-behind element */
8193   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8194
8195   if (!CAN_MOVE(element) ||
8196       (CAN_FALL(element) && direction == MV_DOWN &&
8197        (element == EL_SPRING ||
8198         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8199         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8200     GfxDir[x][y] = MovDir[newx][newy] = 0;
8201
8202   TEST_DrawLevelField(x, y);
8203   TEST_DrawLevelField(newx, newy);
8204
8205   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8206
8207   /* prevent pushed element from moving on in pushed direction */
8208   if (pushed_by_player && CAN_MOVE(element) &&
8209       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8210       !(element_info[element].move_pattern & direction))
8211     TurnRound(newx, newy);
8212
8213   /* prevent elements on conveyor belt from moving on in last direction */
8214   if (pushed_by_conveyor && CAN_FALL(element) &&
8215       direction & MV_HORIZONTAL)
8216     MovDir[newx][newy] = 0;
8217
8218   if (!pushed_by_player)
8219   {
8220     int nextx = newx + dx, nexty = newy + dy;
8221     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8222
8223     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8224
8225     if (CAN_FALL(element) && direction == MV_DOWN)
8226       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8227
8228     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8229       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8230
8231     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8232       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8233   }
8234
8235   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8236   {
8237     TestIfBadThingTouchesPlayer(newx, newy);
8238     TestIfBadThingTouchesFriend(newx, newy);
8239
8240     if (!IS_CUSTOM_ELEMENT(element))
8241       TestIfBadThingTouchesOtherBadThing(newx, newy);
8242   }
8243   else if (element == EL_PENGUIN)
8244     TestIfFriendTouchesBadThing(newx, newy);
8245
8246   if (DONT_GET_HIT_BY(element))
8247   {
8248     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8249   }
8250
8251   /* give the player one last chance (one more frame) to move away */
8252   if (CAN_FALL(element) && direction == MV_DOWN &&
8253       (last_line || (!IS_FREE(x, newy + 1) &&
8254                      (!IS_PLAYER(x, newy + 1) ||
8255                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8256     Impact(x, newy);
8257
8258   if (pushed_by_player && !game.use_change_when_pushing_bug)
8259   {
8260     int push_side = MV_DIR_OPPOSITE(direction);
8261     struct PlayerInfo *player = PLAYERINFO(x, y);
8262
8263     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8264                                player->index_bit, push_side);
8265     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8266                                         player->index_bit, push_side);
8267   }
8268
8269   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8270     MovDelay[newx][newy] = 1;
8271
8272   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8273
8274   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8275   TestIfElementHitsCustomElement(newx, newy, direction);
8276   TestIfPlayerTouchesCustomElement(newx, newy);
8277   TestIfElementTouchesCustomElement(newx, newy);
8278
8279   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8280       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8281     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8282                              MV_DIR_OPPOSITE(direction));
8283 }
8284
8285 int AmoebeNachbarNr(int ax, int ay)
8286 {
8287   int i;
8288   int element = Feld[ax][ay];
8289   int group_nr = 0;
8290   static int xy[4][2] =
8291   {
8292     { 0, -1 },
8293     { -1, 0 },
8294     { +1, 0 },
8295     { 0, +1 }
8296   };
8297
8298   for (i = 0; i < NUM_DIRECTIONS; i++)
8299   {
8300     int x = ax + xy[i][0];
8301     int y = ay + xy[i][1];
8302
8303     if (!IN_LEV_FIELD(x, y))
8304       continue;
8305
8306     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8307       group_nr = AmoebaNr[x][y];
8308   }
8309
8310   return group_nr;
8311 }
8312
8313 void AmoebenVereinigen(int ax, int ay)
8314 {
8315   int i, x, y, xx, yy;
8316   int new_group_nr = AmoebaNr[ax][ay];
8317   static int xy[4][2] =
8318   {
8319     { 0, -1 },
8320     { -1, 0 },
8321     { +1, 0 },
8322     { 0, +1 }
8323   };
8324
8325   if (new_group_nr == 0)
8326     return;
8327
8328   for (i = 0; i < NUM_DIRECTIONS; i++)
8329   {
8330     x = ax + xy[i][0];
8331     y = ay + xy[i][1];
8332
8333     if (!IN_LEV_FIELD(x, y))
8334       continue;
8335
8336     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8337          Feld[x][y] == EL_BD_AMOEBA ||
8338          Feld[x][y] == EL_AMOEBA_DEAD) &&
8339         AmoebaNr[x][y] != new_group_nr)
8340     {
8341       int old_group_nr = AmoebaNr[x][y];
8342
8343       if (old_group_nr == 0)
8344         return;
8345
8346       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8347       AmoebaCnt[old_group_nr] = 0;
8348       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8349       AmoebaCnt2[old_group_nr] = 0;
8350
8351       SCAN_PLAYFIELD(xx, yy)
8352       {
8353         if (AmoebaNr[xx][yy] == old_group_nr)
8354           AmoebaNr[xx][yy] = new_group_nr;
8355       }
8356     }
8357   }
8358 }
8359
8360 void AmoebeUmwandeln(int ax, int ay)
8361 {
8362   int i, x, y;
8363
8364   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8365   {
8366     int group_nr = AmoebaNr[ax][ay];
8367
8368 #ifdef DEBUG
8369     if (group_nr == 0)
8370     {
8371       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8372       printf("AmoebeUmwandeln(): This should never happen!\n");
8373       return;
8374     }
8375 #endif
8376
8377     SCAN_PLAYFIELD(x, y)
8378     {
8379       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8380       {
8381         AmoebaNr[x][y] = 0;
8382         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8383       }
8384     }
8385
8386     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8387                             SND_AMOEBA_TURNING_TO_GEM :
8388                             SND_AMOEBA_TURNING_TO_ROCK));
8389     Bang(ax, ay);
8390   }
8391   else
8392   {
8393     static int xy[4][2] =
8394     {
8395       { 0, -1 },
8396       { -1, 0 },
8397       { +1, 0 },
8398       { 0, +1 }
8399     };
8400
8401     for (i = 0; i < NUM_DIRECTIONS; i++)
8402     {
8403       x = ax + xy[i][0];
8404       y = ay + xy[i][1];
8405
8406       if (!IN_LEV_FIELD(x, y))
8407         continue;
8408
8409       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8410       {
8411         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8412                               SND_AMOEBA_TURNING_TO_GEM :
8413                               SND_AMOEBA_TURNING_TO_ROCK));
8414         Bang(x, y);
8415       }
8416     }
8417   }
8418 }
8419
8420 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8421 {
8422   int x, y;
8423   int group_nr = AmoebaNr[ax][ay];
8424   boolean done = FALSE;
8425
8426 #ifdef DEBUG
8427   if (group_nr == 0)
8428   {
8429     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8430     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8431     return;
8432   }
8433 #endif
8434
8435   SCAN_PLAYFIELD(x, y)
8436   {
8437     if (AmoebaNr[x][y] == group_nr &&
8438         (Feld[x][y] == EL_AMOEBA_DEAD ||
8439          Feld[x][y] == EL_BD_AMOEBA ||
8440          Feld[x][y] == EL_AMOEBA_GROWING))
8441     {
8442       AmoebaNr[x][y] = 0;
8443       Feld[x][y] = new_element;
8444       InitField(x, y, FALSE);
8445       TEST_DrawLevelField(x, y);
8446       done = TRUE;
8447     }
8448   }
8449
8450   if (done)
8451     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8452                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8453                             SND_BD_AMOEBA_TURNING_TO_GEM));
8454 }
8455
8456 void AmoebeWaechst(int x, int y)
8457 {
8458   static unsigned int sound_delay = 0;
8459   static unsigned int sound_delay_value = 0;
8460
8461   if (!MovDelay[x][y])          /* start new growing cycle */
8462   {
8463     MovDelay[x][y] = 7;
8464
8465     if (DelayReached(&sound_delay, sound_delay_value))
8466     {
8467       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8468       sound_delay_value = 30;
8469     }
8470   }
8471
8472   if (MovDelay[x][y])           /* wait some time before growing bigger */
8473   {
8474     MovDelay[x][y]--;
8475     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8476     {
8477       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8478                                            6 - MovDelay[x][y]);
8479
8480       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8481     }
8482
8483     if (!MovDelay[x][y])
8484     {
8485       Feld[x][y] = Store[x][y];
8486       Store[x][y] = 0;
8487       TEST_DrawLevelField(x, y);
8488     }
8489   }
8490 }
8491
8492 void AmoebaDisappearing(int x, int y)
8493 {
8494   static unsigned int sound_delay = 0;
8495   static unsigned int sound_delay_value = 0;
8496
8497   if (!MovDelay[x][y])          /* start new shrinking cycle */
8498   {
8499     MovDelay[x][y] = 7;
8500
8501     if (DelayReached(&sound_delay, sound_delay_value))
8502       sound_delay_value = 30;
8503   }
8504
8505   if (MovDelay[x][y])           /* wait some time before shrinking */
8506   {
8507     MovDelay[x][y]--;
8508     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8509     {
8510       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8511                                            6 - MovDelay[x][y]);
8512
8513       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8514     }
8515
8516     if (!MovDelay[x][y])
8517     {
8518       Feld[x][y] = EL_EMPTY;
8519       TEST_DrawLevelField(x, y);
8520
8521       /* don't let mole enter this field in this cycle;
8522          (give priority to objects falling to this field from above) */
8523       Stop[x][y] = TRUE;
8524     }
8525   }
8526 }
8527
8528 void AmoebeAbleger(int ax, int ay)
8529 {
8530   int i;
8531   int element = Feld[ax][ay];
8532   int graphic = el2img(element);
8533   int newax = ax, neway = ay;
8534   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8535   static int xy[4][2] =
8536   {
8537     { 0, -1 },
8538     { -1, 0 },
8539     { +1, 0 },
8540     { 0, +1 }
8541   };
8542
8543   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8544   {
8545     Feld[ax][ay] = EL_AMOEBA_DEAD;
8546     TEST_DrawLevelField(ax, ay);
8547     return;
8548   }
8549
8550   if (IS_ANIMATED(graphic))
8551     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8552
8553   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8554     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8555
8556   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8557   {
8558     MovDelay[ax][ay]--;
8559     if (MovDelay[ax][ay])
8560       return;
8561   }
8562
8563   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8564   {
8565     int start = RND(4);
8566     int x = ax + xy[start][0];
8567     int y = ay + xy[start][1];
8568
8569     if (!IN_LEV_FIELD(x, y))
8570       return;
8571
8572     if (IS_FREE(x, y) ||
8573         CAN_GROW_INTO(Feld[x][y]) ||
8574         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8575         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8576     {
8577       newax = x;
8578       neway = y;
8579     }
8580
8581     if (newax == ax && neway == ay)
8582       return;
8583   }
8584   else                          /* normal or "filled" (BD style) amoeba */
8585   {
8586     int start = RND(4);
8587     boolean waiting_for_player = FALSE;
8588
8589     for (i = 0; i < NUM_DIRECTIONS; i++)
8590     {
8591       int j = (start + i) % 4;
8592       int x = ax + xy[j][0];
8593       int y = ay + xy[j][1];
8594
8595       if (!IN_LEV_FIELD(x, y))
8596         continue;
8597
8598       if (IS_FREE(x, y) ||
8599           CAN_GROW_INTO(Feld[x][y]) ||
8600           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8601           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8602       {
8603         newax = x;
8604         neway = y;
8605         break;
8606       }
8607       else if (IS_PLAYER(x, y))
8608         waiting_for_player = TRUE;
8609     }
8610
8611     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8612     {
8613       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8614       {
8615         Feld[ax][ay] = EL_AMOEBA_DEAD;
8616         TEST_DrawLevelField(ax, ay);
8617         AmoebaCnt[AmoebaNr[ax][ay]]--;
8618
8619         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8620         {
8621           if (element == EL_AMOEBA_FULL)
8622             AmoebeUmwandeln(ax, ay);
8623           else if (element == EL_BD_AMOEBA)
8624             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8625         }
8626       }
8627       return;
8628     }
8629     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8630     {
8631       /* amoeba gets larger by growing in some direction */
8632
8633       int new_group_nr = AmoebaNr[ax][ay];
8634
8635 #ifdef DEBUG
8636   if (new_group_nr == 0)
8637   {
8638     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8639     printf("AmoebeAbleger(): This should never happen!\n");
8640     return;
8641   }
8642 #endif
8643
8644       AmoebaNr[newax][neway] = new_group_nr;
8645       AmoebaCnt[new_group_nr]++;
8646       AmoebaCnt2[new_group_nr]++;
8647
8648       /* if amoeba touches other amoeba(s) after growing, unify them */
8649       AmoebenVereinigen(newax, neway);
8650
8651       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8652       {
8653         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8654         return;
8655       }
8656     }
8657   }
8658
8659   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8660       (neway == lev_fieldy - 1 && newax != ax))
8661   {
8662     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8663     Store[newax][neway] = element;
8664   }
8665   else if (neway == ay || element == EL_EMC_DRIPPER)
8666   {
8667     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8668
8669     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8670   }
8671   else
8672   {
8673     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8674     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8675     Store[ax][ay] = EL_AMOEBA_DROP;
8676     ContinueMoving(ax, ay);
8677     return;
8678   }
8679
8680   TEST_DrawLevelField(newax, neway);
8681 }
8682
8683 void Life(int ax, int ay)
8684 {
8685   int x1, y1, x2, y2;
8686   int life_time = 40;
8687   int element = Feld[ax][ay];
8688   int graphic = el2img(element);
8689   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8690                          level.biomaze);
8691   boolean changed = FALSE;
8692
8693   if (IS_ANIMATED(graphic))
8694     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8695
8696   if (Stop[ax][ay])
8697     return;
8698
8699   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8700     MovDelay[ax][ay] = life_time;
8701
8702   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8703   {
8704     MovDelay[ax][ay]--;
8705     if (MovDelay[ax][ay])
8706       return;
8707   }
8708
8709   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8710   {
8711     int xx = ax+x1, yy = ay+y1;
8712     int nachbarn = 0;
8713
8714     if (!IN_LEV_FIELD(xx, yy))
8715       continue;
8716
8717     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8718     {
8719       int x = xx+x2, y = yy+y2;
8720
8721       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8722         continue;
8723
8724       if (((Feld[x][y] == element ||
8725             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8726            !Stop[x][y]) ||
8727           (IS_FREE(x, y) && Stop[x][y]))
8728         nachbarn++;
8729     }
8730
8731     if (xx == ax && yy == ay)           /* field in the middle */
8732     {
8733       if (nachbarn < life_parameter[0] ||
8734           nachbarn > life_parameter[1])
8735       {
8736         Feld[xx][yy] = EL_EMPTY;
8737         if (!Stop[xx][yy])
8738           TEST_DrawLevelField(xx, yy);
8739         Stop[xx][yy] = TRUE;
8740         changed = TRUE;
8741       }
8742     }
8743     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8744     {                                   /* free border field */
8745       if (nachbarn >= life_parameter[2] &&
8746           nachbarn <= life_parameter[3])
8747       {
8748         Feld[xx][yy] = element;
8749         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8750         if (!Stop[xx][yy])
8751           TEST_DrawLevelField(xx, yy);
8752         Stop[xx][yy] = TRUE;
8753         changed = TRUE;
8754       }
8755     }
8756   }
8757
8758   if (changed)
8759     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8760                    SND_GAME_OF_LIFE_GROWING);
8761 }
8762
8763 static void InitRobotWheel(int x, int y)
8764 {
8765   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8766 }
8767
8768 static void RunRobotWheel(int x, int y)
8769 {
8770   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8771 }
8772
8773 static void StopRobotWheel(int x, int y)
8774 {
8775   if (ZX == x && ZY == y)
8776   {
8777     ZX = ZY = -1;
8778
8779     game.robot_wheel_active = FALSE;
8780   }
8781 }
8782
8783 static void InitTimegateWheel(int x, int y)
8784 {
8785   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8786 }
8787
8788 static void RunTimegateWheel(int x, int y)
8789 {
8790   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8791 }
8792
8793 static void InitMagicBallDelay(int x, int y)
8794 {
8795   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8796 }
8797
8798 static void ActivateMagicBall(int bx, int by)
8799 {
8800   int x, y;
8801
8802   if (level.ball_random)
8803   {
8804     int pos_border = RND(8);    /* select one of the eight border elements */
8805     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8806     int xx = pos_content % 3;
8807     int yy = pos_content / 3;
8808
8809     x = bx - 1 + xx;
8810     y = by - 1 + yy;
8811
8812     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8813       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8814   }
8815   else
8816   {
8817     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8818     {
8819       int xx = x - bx + 1;
8820       int yy = y - by + 1;
8821
8822       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8823         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8824     }
8825   }
8826
8827   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8828 }
8829
8830 void CheckExit(int x, int y)
8831 {
8832   if (local_player->gems_still_needed > 0 ||
8833       local_player->sokobanfields_still_needed > 0 ||
8834       local_player->lights_still_needed > 0)
8835   {
8836     int element = Feld[x][y];
8837     int graphic = el2img(element);
8838
8839     if (IS_ANIMATED(graphic))
8840       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8841
8842     return;
8843   }
8844
8845   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8846     return;
8847
8848   Feld[x][y] = EL_EXIT_OPENING;
8849
8850   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8851 }
8852
8853 void CheckExitEM(int x, int y)
8854 {
8855   if (local_player->gems_still_needed > 0 ||
8856       local_player->sokobanfields_still_needed > 0 ||
8857       local_player->lights_still_needed > 0)
8858   {
8859     int element = Feld[x][y];
8860     int graphic = el2img(element);
8861
8862     if (IS_ANIMATED(graphic))
8863       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8864
8865     return;
8866   }
8867
8868   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8869     return;
8870
8871   Feld[x][y] = EL_EM_EXIT_OPENING;
8872
8873   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8874 }
8875
8876 void CheckExitSteel(int x, int y)
8877 {
8878   if (local_player->gems_still_needed > 0 ||
8879       local_player->sokobanfields_still_needed > 0 ||
8880       local_player->lights_still_needed > 0)
8881   {
8882     int element = Feld[x][y];
8883     int graphic = el2img(element);
8884
8885     if (IS_ANIMATED(graphic))
8886       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8887
8888     return;
8889   }
8890
8891   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8892     return;
8893
8894   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8895
8896   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8897 }
8898
8899 void CheckExitSteelEM(int x, int y)
8900 {
8901   if (local_player->gems_still_needed > 0 ||
8902       local_player->sokobanfields_still_needed > 0 ||
8903       local_player->lights_still_needed > 0)
8904   {
8905     int element = Feld[x][y];
8906     int graphic = el2img(element);
8907
8908     if (IS_ANIMATED(graphic))
8909       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8910
8911     return;
8912   }
8913
8914   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8915     return;
8916
8917   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8918
8919   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8920 }
8921
8922 void CheckExitSP(int x, int y)
8923 {
8924   if (local_player->gems_still_needed > 0)
8925   {
8926     int element = Feld[x][y];
8927     int graphic = el2img(element);
8928
8929     if (IS_ANIMATED(graphic))
8930       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8931
8932     return;
8933   }
8934
8935   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8936     return;
8937
8938   Feld[x][y] = EL_SP_EXIT_OPENING;
8939
8940   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8941 }
8942
8943 static void CloseAllOpenTimegates()
8944 {
8945   int x, y;
8946
8947   SCAN_PLAYFIELD(x, y)
8948   {
8949     int element = Feld[x][y];
8950
8951     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8952     {
8953       Feld[x][y] = EL_TIMEGATE_CLOSING;
8954
8955       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8956     }
8957   }
8958 }
8959
8960 void DrawTwinkleOnField(int x, int y)
8961 {
8962   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8963     return;
8964
8965   if (Feld[x][y] == EL_BD_DIAMOND)
8966     return;
8967
8968   if (MovDelay[x][y] == 0)      /* next animation frame */
8969     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8970
8971   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8972   {
8973     MovDelay[x][y]--;
8974
8975     DrawLevelElementAnimation(x, y, Feld[x][y]);
8976
8977     if (MovDelay[x][y] != 0)
8978     {
8979       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8980                                            10 - MovDelay[x][y]);
8981
8982       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8983     }
8984   }
8985 }
8986
8987 void MauerWaechst(int x, int y)
8988 {
8989   int delay = 6;
8990
8991   if (!MovDelay[x][y])          /* next animation frame */
8992     MovDelay[x][y] = 3 * delay;
8993
8994   if (MovDelay[x][y])           /* wait some time before next frame */
8995   {
8996     MovDelay[x][y]--;
8997
8998     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8999     {
9000       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9001       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9002
9003       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9004     }
9005
9006     if (!MovDelay[x][y])
9007     {
9008       if (MovDir[x][y] == MV_LEFT)
9009       {
9010         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9011           TEST_DrawLevelField(x - 1, y);
9012       }
9013       else if (MovDir[x][y] == MV_RIGHT)
9014       {
9015         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9016           TEST_DrawLevelField(x + 1, y);
9017       }
9018       else if (MovDir[x][y] == MV_UP)
9019       {
9020         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9021           TEST_DrawLevelField(x, y - 1);
9022       }
9023       else
9024       {
9025         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9026           TEST_DrawLevelField(x, y + 1);
9027       }
9028
9029       Feld[x][y] = Store[x][y];
9030       Store[x][y] = 0;
9031       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9032       TEST_DrawLevelField(x, y);
9033     }
9034   }
9035 }
9036
9037 void MauerAbleger(int ax, int ay)
9038 {
9039   int element = Feld[ax][ay];
9040   int graphic = el2img(element);
9041   boolean oben_frei = FALSE, unten_frei = FALSE;
9042   boolean links_frei = FALSE, rechts_frei = FALSE;
9043   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9044   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9045   boolean new_wall = FALSE;
9046
9047   if (IS_ANIMATED(graphic))
9048     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9049
9050   if (!MovDelay[ax][ay])        /* start building new wall */
9051     MovDelay[ax][ay] = 6;
9052
9053   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9054   {
9055     MovDelay[ax][ay]--;
9056     if (MovDelay[ax][ay])
9057       return;
9058   }
9059
9060   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9061     oben_frei = TRUE;
9062   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9063     unten_frei = TRUE;
9064   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9065     links_frei = TRUE;
9066   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9067     rechts_frei = TRUE;
9068
9069   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9070       element == EL_EXPANDABLE_WALL_ANY)
9071   {
9072     if (oben_frei)
9073     {
9074       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9075       Store[ax][ay-1] = element;
9076       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9077       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9078         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9079                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9080       new_wall = TRUE;
9081     }
9082     if (unten_frei)
9083     {
9084       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9085       Store[ax][ay+1] = element;
9086       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9087       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9088         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9089                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9090       new_wall = TRUE;
9091     }
9092   }
9093
9094   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9095       element == EL_EXPANDABLE_WALL_ANY ||
9096       element == EL_EXPANDABLE_WALL ||
9097       element == EL_BD_EXPANDABLE_WALL)
9098   {
9099     if (links_frei)
9100     {
9101       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9102       Store[ax-1][ay] = element;
9103       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9104       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9105         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9106                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9107       new_wall = TRUE;
9108     }
9109
9110     if (rechts_frei)
9111     {
9112       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9113       Store[ax+1][ay] = element;
9114       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9115       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9116         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9117                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9118       new_wall = TRUE;
9119     }
9120   }
9121
9122   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9123     TEST_DrawLevelField(ax, ay);
9124
9125   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9126     oben_massiv = TRUE;
9127   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9128     unten_massiv = TRUE;
9129   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9130     links_massiv = TRUE;
9131   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9132     rechts_massiv = TRUE;
9133
9134   if (((oben_massiv && unten_massiv) ||
9135        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9136        element == EL_EXPANDABLE_WALL) &&
9137       ((links_massiv && rechts_massiv) ||
9138        element == EL_EXPANDABLE_WALL_VERTICAL))
9139     Feld[ax][ay] = EL_WALL;
9140
9141   if (new_wall)
9142     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9143 }
9144
9145 void MauerAblegerStahl(int ax, int ay)
9146 {
9147   int element = Feld[ax][ay];
9148   int graphic = el2img(element);
9149   boolean oben_frei = FALSE, unten_frei = FALSE;
9150   boolean links_frei = FALSE, rechts_frei = FALSE;
9151   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9152   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9153   boolean new_wall = FALSE;
9154
9155   if (IS_ANIMATED(graphic))
9156     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9157
9158   if (!MovDelay[ax][ay])        /* start building new wall */
9159     MovDelay[ax][ay] = 6;
9160
9161   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9162   {
9163     MovDelay[ax][ay]--;
9164     if (MovDelay[ax][ay])
9165       return;
9166   }
9167
9168   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9169     oben_frei = TRUE;
9170   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9171     unten_frei = TRUE;
9172   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9173     links_frei = TRUE;
9174   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9175     rechts_frei = TRUE;
9176
9177   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9178       element == EL_EXPANDABLE_STEELWALL_ANY)
9179   {
9180     if (oben_frei)
9181     {
9182       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9183       Store[ax][ay-1] = element;
9184       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9185       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9186         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9187                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9188       new_wall = TRUE;
9189     }
9190     if (unten_frei)
9191     {
9192       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9193       Store[ax][ay+1] = element;
9194       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9195       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9196         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9197                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9198       new_wall = TRUE;
9199     }
9200   }
9201
9202   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9203       element == EL_EXPANDABLE_STEELWALL_ANY)
9204   {
9205     if (links_frei)
9206     {
9207       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9208       Store[ax-1][ay] = element;
9209       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9210       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9211         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9212                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9213       new_wall = TRUE;
9214     }
9215
9216     if (rechts_frei)
9217     {
9218       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9219       Store[ax+1][ay] = element;
9220       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9221       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9222         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9223                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9224       new_wall = TRUE;
9225     }
9226   }
9227
9228   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9229     oben_massiv = TRUE;
9230   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9231     unten_massiv = TRUE;
9232   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9233     links_massiv = TRUE;
9234   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9235     rechts_massiv = TRUE;
9236
9237   if (((oben_massiv && unten_massiv) ||
9238        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9239       ((links_massiv && rechts_massiv) ||
9240        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9241     Feld[ax][ay] = EL_STEELWALL;
9242
9243   if (new_wall)
9244     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9245 }
9246
9247 void CheckForDragon(int x, int y)
9248 {
9249   int i, j;
9250   boolean dragon_found = FALSE;
9251   static int xy[4][2] =
9252   {
9253     { 0, -1 },
9254     { -1, 0 },
9255     { +1, 0 },
9256     { 0, +1 }
9257   };
9258
9259   for (i = 0; i < NUM_DIRECTIONS; i++)
9260   {
9261     for (j = 0; j < 4; j++)
9262     {
9263       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9264
9265       if (IN_LEV_FIELD(xx, yy) &&
9266           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9267       {
9268         if (Feld[xx][yy] == EL_DRAGON)
9269           dragon_found = TRUE;
9270       }
9271       else
9272         break;
9273     }
9274   }
9275
9276   if (!dragon_found)
9277   {
9278     for (i = 0; i < NUM_DIRECTIONS; i++)
9279     {
9280       for (j = 0; j < 3; j++)
9281       {
9282         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9283   
9284         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9285         {
9286           Feld[xx][yy] = EL_EMPTY;
9287           TEST_DrawLevelField(xx, yy);
9288         }
9289         else
9290           break;
9291       }
9292     }
9293   }
9294 }
9295
9296 static void InitBuggyBase(int x, int y)
9297 {
9298   int element = Feld[x][y];
9299   int activating_delay = FRAMES_PER_SECOND / 4;
9300
9301   ChangeDelay[x][y] =
9302     (element == EL_SP_BUGGY_BASE ?
9303      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9304      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9305      activating_delay :
9306      element == EL_SP_BUGGY_BASE_ACTIVE ?
9307      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9308 }
9309
9310 static void WarnBuggyBase(int x, int y)
9311 {
9312   int i;
9313   static int xy[4][2] =
9314   {
9315     { 0, -1 },
9316     { -1, 0 },
9317     { +1, 0 },
9318     { 0, +1 }
9319   };
9320
9321   for (i = 0; i < NUM_DIRECTIONS; i++)
9322   {
9323     int xx = x + xy[i][0];
9324     int yy = y + xy[i][1];
9325
9326     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9327     {
9328       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9329
9330       break;
9331     }
9332   }
9333 }
9334
9335 static void InitTrap(int x, int y)
9336 {
9337   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9338 }
9339
9340 static void ActivateTrap(int x, int y)
9341 {
9342   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9343 }
9344
9345 static void ChangeActiveTrap(int x, int y)
9346 {
9347   int graphic = IMG_TRAP_ACTIVE;
9348
9349   /* if new animation frame was drawn, correct crumbled sand border */
9350   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9351     TEST_DrawLevelFieldCrumbled(x, y);
9352 }
9353
9354 static int getSpecialActionElement(int element, int number, int base_element)
9355 {
9356   return (element != EL_EMPTY ? element :
9357           number != -1 ? base_element + number - 1 :
9358           EL_EMPTY);
9359 }
9360
9361 static int getModifiedActionNumber(int value_old, int operator, int operand,
9362                                    int value_min, int value_max)
9363 {
9364   int value_new = (operator == CA_MODE_SET      ? operand :
9365                    operator == CA_MODE_ADD      ? value_old + operand :
9366                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9367                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9368                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9369                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9370                    value_old);
9371
9372   return (value_new < value_min ? value_min :
9373           value_new > value_max ? value_max :
9374           value_new);
9375 }
9376
9377 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9378 {
9379   struct ElementInfo *ei = &element_info[element];
9380   struct ElementChangeInfo *change = &ei->change_page[page];
9381   int target_element = change->target_element;
9382   int action_type = change->action_type;
9383   int action_mode = change->action_mode;
9384   int action_arg = change->action_arg;
9385   int action_element = change->action_element;
9386   int i;
9387
9388   if (!change->has_action)
9389     return;
9390
9391   /* ---------- determine action paramater values -------------------------- */
9392
9393   int level_time_value =
9394     (level.time > 0 ? TimeLeft :
9395      TimePlayed);
9396
9397   int action_arg_element_raw =
9398     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9399      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9400      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9401      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9402      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9403      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9404      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9405      EL_EMPTY);
9406   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9407
9408   int action_arg_direction =
9409     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9410      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9411      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9412      change->actual_trigger_side :
9413      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9414      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9415      MV_NONE);
9416
9417   int action_arg_number_min =
9418     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9419      CA_ARG_MIN);
9420
9421   int action_arg_number_max =
9422     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9423      action_type == CA_SET_LEVEL_GEMS ? 999 :
9424      action_type == CA_SET_LEVEL_TIME ? 9999 :
9425      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9426      action_type == CA_SET_CE_VALUE ? 9999 :
9427      action_type == CA_SET_CE_SCORE ? 9999 :
9428      CA_ARG_MAX);
9429
9430   int action_arg_number_reset =
9431     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9432      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9433      action_type == CA_SET_LEVEL_TIME ? level.time :
9434      action_type == CA_SET_LEVEL_SCORE ? 0 :
9435      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9436      action_type == CA_SET_CE_SCORE ? 0 :
9437      0);
9438
9439   int action_arg_number =
9440     (action_arg <= CA_ARG_MAX ? action_arg :
9441      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9442      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9443      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9444      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9445      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9446      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9447      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9448      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9449      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9450      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9451      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9452      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9453      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9454      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9455      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9456      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9457      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9458      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9459      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9460      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9461      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9462      -1);
9463
9464   int action_arg_number_old =
9465     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9466      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9467      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9468      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9469      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9470      0);
9471
9472   int action_arg_number_new =
9473     getModifiedActionNumber(action_arg_number_old,
9474                             action_mode, action_arg_number,
9475                             action_arg_number_min, action_arg_number_max);
9476
9477   int trigger_player_bits =
9478     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9479      change->actual_trigger_player_bits : change->trigger_player);
9480
9481   int action_arg_player_bits =
9482     (action_arg >= CA_ARG_PLAYER_1 &&
9483      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9484      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9485      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9486      PLAYER_BITS_ANY);
9487
9488   /* ---------- execute action  -------------------------------------------- */
9489
9490   switch (action_type)
9491   {
9492     case CA_NO_ACTION:
9493     {
9494       return;
9495     }
9496
9497     /* ---------- level actions  ------------------------------------------- */
9498
9499     case CA_RESTART_LEVEL:
9500     {
9501       game.restart_level = TRUE;
9502
9503       break;
9504     }
9505
9506     case CA_SHOW_ENVELOPE:
9507     {
9508       int element = getSpecialActionElement(action_arg_element,
9509                                             action_arg_number, EL_ENVELOPE_1);
9510
9511       if (IS_ENVELOPE(element))
9512         local_player->show_envelope = element;
9513
9514       break;
9515     }
9516
9517     case CA_SET_LEVEL_TIME:
9518     {
9519       if (level.time > 0)       /* only modify limited time value */
9520       {
9521         TimeLeft = action_arg_number_new;
9522
9523         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9524
9525         DisplayGameControlValues();
9526
9527         if (!TimeLeft && setup.time_limit)
9528           for (i = 0; i < MAX_PLAYERS; i++)
9529             KillPlayer(&stored_player[i]);
9530       }
9531
9532       break;
9533     }
9534
9535     case CA_SET_LEVEL_SCORE:
9536     {
9537       local_player->score = action_arg_number_new;
9538
9539       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9540
9541       DisplayGameControlValues();
9542
9543       break;
9544     }
9545
9546     case CA_SET_LEVEL_GEMS:
9547     {
9548       local_player->gems_still_needed = action_arg_number_new;
9549
9550       game_panel_controls[GAME_PANEL_GEMS].value =
9551         local_player->gems_still_needed;
9552
9553       DisplayGameControlValues();
9554
9555       break;
9556     }
9557
9558     case CA_SET_LEVEL_WIND:
9559     {
9560       game.wind_direction = action_arg_direction;
9561
9562       break;
9563     }
9564
9565     case CA_SET_LEVEL_RANDOM_SEED:
9566     {
9567       /* ensure that setting a new random seed while playing is predictable */
9568       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9569
9570       break;
9571     }
9572
9573     /* ---------- player actions  ------------------------------------------ */
9574
9575     case CA_MOVE_PLAYER:
9576     {
9577       /* automatically move to the next field in specified direction */
9578       for (i = 0; i < MAX_PLAYERS; i++)
9579         if (trigger_player_bits & (1 << i))
9580           stored_player[i].programmed_action = action_arg_direction;
9581
9582       break;
9583     }
9584
9585     case CA_EXIT_PLAYER:
9586     {
9587       for (i = 0; i < MAX_PLAYERS; i++)
9588         if (action_arg_player_bits & (1 << i))
9589           PlayerWins(&stored_player[i]);
9590
9591       break;
9592     }
9593
9594     case CA_KILL_PLAYER:
9595     {
9596       for (i = 0; i < MAX_PLAYERS; i++)
9597         if (action_arg_player_bits & (1 << i))
9598           KillPlayer(&stored_player[i]);
9599
9600       break;
9601     }
9602
9603     case CA_SET_PLAYER_KEYS:
9604     {
9605       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9606       int element = getSpecialActionElement(action_arg_element,
9607                                             action_arg_number, EL_KEY_1);
9608
9609       if (IS_KEY(element))
9610       {
9611         for (i = 0; i < MAX_PLAYERS; i++)
9612         {
9613           if (trigger_player_bits & (1 << i))
9614           {
9615             stored_player[i].key[KEY_NR(element)] = key_state;
9616
9617             DrawGameDoorValues();
9618           }
9619         }
9620       }
9621
9622       break;
9623     }
9624
9625     case CA_SET_PLAYER_SPEED:
9626     {
9627       for (i = 0; i < MAX_PLAYERS; i++)
9628       {
9629         if (trigger_player_bits & (1 << i))
9630         {
9631           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9632
9633           if (action_arg == CA_ARG_SPEED_FASTER &&
9634               stored_player[i].cannot_move)
9635           {
9636             action_arg_number = STEPSIZE_VERY_SLOW;
9637           }
9638           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9639                    action_arg == CA_ARG_SPEED_FASTER)
9640           {
9641             action_arg_number = 2;
9642             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9643                            CA_MODE_MULTIPLY);
9644           }
9645           else if (action_arg == CA_ARG_NUMBER_RESET)
9646           {
9647             action_arg_number = level.initial_player_stepsize[i];
9648           }
9649
9650           move_stepsize =
9651             getModifiedActionNumber(move_stepsize,
9652                                     action_mode,
9653                                     action_arg_number,
9654                                     action_arg_number_min,
9655                                     action_arg_number_max);
9656
9657           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9658         }
9659       }
9660
9661       break;
9662     }
9663
9664     case CA_SET_PLAYER_SHIELD:
9665     {
9666       for (i = 0; i < MAX_PLAYERS; i++)
9667       {
9668         if (trigger_player_bits & (1 << i))
9669         {
9670           if (action_arg == CA_ARG_SHIELD_OFF)
9671           {
9672             stored_player[i].shield_normal_time_left = 0;
9673             stored_player[i].shield_deadly_time_left = 0;
9674           }
9675           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9676           {
9677             stored_player[i].shield_normal_time_left = 999999;
9678           }
9679           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9680           {
9681             stored_player[i].shield_normal_time_left = 999999;
9682             stored_player[i].shield_deadly_time_left = 999999;
9683           }
9684         }
9685       }
9686
9687       break;
9688     }
9689
9690     case CA_SET_PLAYER_GRAVITY:
9691     {
9692       for (i = 0; i < MAX_PLAYERS; i++)
9693       {
9694         if (trigger_player_bits & (1 << i))
9695         {
9696           stored_player[i].gravity =
9697             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9698              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9699              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9700              stored_player[i].gravity);
9701         }
9702       }
9703
9704       break;
9705     }
9706
9707     case CA_SET_PLAYER_ARTWORK:
9708     {
9709       for (i = 0; i < MAX_PLAYERS; i++)
9710       {
9711         if (trigger_player_bits & (1 << i))
9712         {
9713           int artwork_element = action_arg_element;
9714
9715           if (action_arg == CA_ARG_ELEMENT_RESET)
9716             artwork_element =
9717               (level.use_artwork_element[i] ? level.artwork_element[i] :
9718                stored_player[i].element_nr);
9719
9720           if (stored_player[i].artwork_element != artwork_element)
9721             stored_player[i].Frame = 0;
9722
9723           stored_player[i].artwork_element = artwork_element;
9724
9725           SetPlayerWaiting(&stored_player[i], FALSE);
9726
9727           /* set number of special actions for bored and sleeping animation */
9728           stored_player[i].num_special_action_bored =
9729             get_num_special_action(artwork_element,
9730                                    ACTION_BORING_1, ACTION_BORING_LAST);
9731           stored_player[i].num_special_action_sleeping =
9732             get_num_special_action(artwork_element,
9733                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9734         }
9735       }
9736
9737       break;
9738     }
9739
9740     case CA_SET_PLAYER_INVENTORY:
9741     {
9742       for (i = 0; i < MAX_PLAYERS; i++)
9743       {
9744         struct PlayerInfo *player = &stored_player[i];
9745         int j, k;
9746
9747         if (trigger_player_bits & (1 << i))
9748         {
9749           int inventory_element = action_arg_element;
9750
9751           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9752               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9753               action_arg == CA_ARG_ELEMENT_ACTION)
9754           {
9755             int element = inventory_element;
9756             int collect_count = element_info[element].collect_count_initial;
9757
9758             if (!IS_CUSTOM_ELEMENT(element))
9759               collect_count = 1;
9760
9761             if (collect_count == 0)
9762               player->inventory_infinite_element = element;
9763             else
9764               for (k = 0; k < collect_count; k++)
9765                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9766                   player->inventory_element[player->inventory_size++] =
9767                     element;
9768           }
9769           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9770                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9771                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9772           {
9773             if (player->inventory_infinite_element != EL_UNDEFINED &&
9774                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9775                                      action_arg_element_raw))
9776               player->inventory_infinite_element = EL_UNDEFINED;
9777
9778             for (k = 0, j = 0; j < player->inventory_size; j++)
9779             {
9780               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9781                                         action_arg_element_raw))
9782                 player->inventory_element[k++] = player->inventory_element[j];
9783             }
9784
9785             player->inventory_size = k;
9786           }
9787           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9788           {
9789             if (player->inventory_size > 0)
9790             {
9791               for (j = 0; j < player->inventory_size - 1; j++)
9792                 player->inventory_element[j] = player->inventory_element[j + 1];
9793
9794               player->inventory_size--;
9795             }
9796           }
9797           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9798           {
9799             if (player->inventory_size > 0)
9800               player->inventory_size--;
9801           }
9802           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9803           {
9804             player->inventory_infinite_element = EL_UNDEFINED;
9805             player->inventory_size = 0;
9806           }
9807           else if (action_arg == CA_ARG_INVENTORY_RESET)
9808           {
9809             player->inventory_infinite_element = EL_UNDEFINED;
9810             player->inventory_size = 0;
9811
9812             if (level.use_initial_inventory[i])
9813             {
9814               for (j = 0; j < level.initial_inventory_size[i]; j++)
9815               {
9816                 int element = level.initial_inventory_content[i][j];
9817                 int collect_count = element_info[element].collect_count_initial;
9818
9819                 if (!IS_CUSTOM_ELEMENT(element))
9820                   collect_count = 1;
9821
9822                 if (collect_count == 0)
9823                   player->inventory_infinite_element = element;
9824                 else
9825                   for (k = 0; k < collect_count; k++)
9826                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9827                       player->inventory_element[player->inventory_size++] =
9828                         element;
9829               }
9830             }
9831           }
9832         }
9833       }
9834
9835       break;
9836     }
9837
9838     /* ---------- CE actions  ---------------------------------------------- */
9839
9840     case CA_SET_CE_VALUE:
9841     {
9842       int last_ce_value = CustomValue[x][y];
9843
9844       CustomValue[x][y] = action_arg_number_new;
9845
9846       if (CustomValue[x][y] != last_ce_value)
9847       {
9848         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9849         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9850
9851         if (CustomValue[x][y] == 0)
9852         {
9853           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9854           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9855         }
9856       }
9857
9858       break;
9859     }
9860
9861     case CA_SET_CE_SCORE:
9862     {
9863       int last_ce_score = ei->collect_score;
9864
9865       ei->collect_score = action_arg_number_new;
9866
9867       if (ei->collect_score != last_ce_score)
9868       {
9869         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9870         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9871
9872         if (ei->collect_score == 0)
9873         {
9874           int xx, yy;
9875
9876           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9877           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9878
9879           /*
9880             This is a very special case that seems to be a mixture between
9881             CheckElementChange() and CheckTriggeredElementChange(): while
9882             the first one only affects single elements that are triggered
9883             directly, the second one affects multiple elements in the playfield
9884             that are triggered indirectly by another element. This is a third
9885             case: Changing the CE score always affects multiple identical CEs,
9886             so every affected CE must be checked, not only the single CE for
9887             which the CE score was changed in the first place (as every instance
9888             of that CE shares the same CE score, and therefore also can change)!
9889           */
9890           SCAN_PLAYFIELD(xx, yy)
9891           {
9892             if (Feld[xx][yy] == element)
9893               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9894                                  CE_SCORE_GETS_ZERO);
9895           }
9896         }
9897       }
9898
9899       break;
9900     }
9901
9902     case CA_SET_CE_ARTWORK:
9903     {
9904       int artwork_element = action_arg_element;
9905       boolean reset_frame = FALSE;
9906       int xx, yy;
9907
9908       if (action_arg == CA_ARG_ELEMENT_RESET)
9909         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9910                            element);
9911
9912       if (ei->gfx_element != artwork_element)
9913         reset_frame = TRUE;
9914
9915       ei->gfx_element = artwork_element;
9916
9917       SCAN_PLAYFIELD(xx, yy)
9918       {
9919         if (Feld[xx][yy] == element)
9920         {
9921           if (reset_frame)
9922           {
9923             ResetGfxAnimation(xx, yy);
9924             ResetRandomAnimationValue(xx, yy);
9925           }
9926
9927           TEST_DrawLevelField(xx, yy);
9928         }
9929       }
9930
9931       break;
9932     }
9933
9934     /* ---------- engine actions  ------------------------------------------ */
9935
9936     case CA_SET_ENGINE_SCAN_MODE:
9937     {
9938       InitPlayfieldScanMode(action_arg);
9939
9940       break;
9941     }
9942
9943     default:
9944       break;
9945   }
9946 }
9947
9948 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9949 {
9950   int old_element = Feld[x][y];
9951   int new_element = GetElementFromGroupElement(element);
9952   int previous_move_direction = MovDir[x][y];
9953   int last_ce_value = CustomValue[x][y];
9954   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9955   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9956   boolean add_player_onto_element = (new_element_is_player &&
9957                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9958                                      IS_WALKABLE(old_element));
9959
9960   if (!add_player_onto_element)
9961   {
9962     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9963       RemoveMovingField(x, y);
9964     else
9965       RemoveField(x, y);
9966
9967     Feld[x][y] = new_element;
9968
9969     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9970       MovDir[x][y] = previous_move_direction;
9971
9972     if (element_info[new_element].use_last_ce_value)
9973       CustomValue[x][y] = last_ce_value;
9974
9975     InitField_WithBug1(x, y, FALSE);
9976
9977     new_element = Feld[x][y];   /* element may have changed */
9978
9979     ResetGfxAnimation(x, y);
9980     ResetRandomAnimationValue(x, y);
9981
9982     TEST_DrawLevelField(x, y);
9983
9984     if (GFX_CRUMBLED(new_element))
9985       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9986   }
9987
9988   /* check if element under the player changes from accessible to unaccessible
9989      (needed for special case of dropping element which then changes) */
9990   /* (must be checked after creating new element for walkable group elements) */
9991   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9992       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9993   {
9994     Bang(x, y);
9995
9996     return;
9997   }
9998
9999   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10000   if (new_element_is_player)
10001     RelocatePlayer(x, y, new_element);
10002
10003   if (is_change)
10004     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10005
10006   TestIfBadThingTouchesPlayer(x, y);
10007   TestIfPlayerTouchesCustomElement(x, y);
10008   TestIfElementTouchesCustomElement(x, y);
10009 }
10010
10011 static void CreateField(int x, int y, int element)
10012 {
10013   CreateFieldExt(x, y, element, FALSE);
10014 }
10015
10016 static void CreateElementFromChange(int x, int y, int element)
10017 {
10018   element = GET_VALID_RUNTIME_ELEMENT(element);
10019
10020   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10021   {
10022     int old_element = Feld[x][y];
10023
10024     /* prevent changed element from moving in same engine frame
10025        unless both old and new element can either fall or move */
10026     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10027         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10028       Stop[x][y] = TRUE;
10029   }
10030
10031   CreateFieldExt(x, y, element, TRUE);
10032 }
10033
10034 static boolean ChangeElement(int x, int y, int element, int page)
10035 {
10036   struct ElementInfo *ei = &element_info[element];
10037   struct ElementChangeInfo *change = &ei->change_page[page];
10038   int ce_value = CustomValue[x][y];
10039   int ce_score = ei->collect_score;
10040   int target_element;
10041   int old_element = Feld[x][y];
10042
10043   /* always use default change event to prevent running into a loop */
10044   if (ChangeEvent[x][y] == -1)
10045     ChangeEvent[x][y] = CE_DELAY;
10046
10047   if (ChangeEvent[x][y] == CE_DELAY)
10048   {
10049     /* reset actual trigger element, trigger player and action element */
10050     change->actual_trigger_element = EL_EMPTY;
10051     change->actual_trigger_player = EL_EMPTY;
10052     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10053     change->actual_trigger_side = CH_SIDE_NONE;
10054     change->actual_trigger_ce_value = 0;
10055     change->actual_trigger_ce_score = 0;
10056   }
10057
10058   /* do not change elements more than a specified maximum number of changes */
10059   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10060     return FALSE;
10061
10062   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10063
10064   if (change->explode)
10065   {
10066     Bang(x, y);
10067
10068     return TRUE;
10069   }
10070
10071   if (change->use_target_content)
10072   {
10073     boolean complete_replace = TRUE;
10074     boolean can_replace[3][3];
10075     int xx, yy;
10076
10077     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10078     {
10079       boolean is_empty;
10080       boolean is_walkable;
10081       boolean is_diggable;
10082       boolean is_collectible;
10083       boolean is_removable;
10084       boolean is_destructible;
10085       int ex = x + xx - 1;
10086       int ey = y + yy - 1;
10087       int content_element = change->target_content.e[xx][yy];
10088       int e;
10089
10090       can_replace[xx][yy] = TRUE;
10091
10092       if (ex == x && ey == y)   /* do not check changing element itself */
10093         continue;
10094
10095       if (content_element == EL_EMPTY_SPACE)
10096       {
10097         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10098
10099         continue;
10100       }
10101
10102       if (!IN_LEV_FIELD(ex, ey))
10103       {
10104         can_replace[xx][yy] = FALSE;
10105         complete_replace = FALSE;
10106
10107         continue;
10108       }
10109
10110       e = Feld[ex][ey];
10111
10112       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10113         e = MovingOrBlocked2Element(ex, ey);
10114
10115       is_empty = (IS_FREE(ex, ey) ||
10116                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10117
10118       is_walkable     = (is_empty || IS_WALKABLE(e));
10119       is_diggable     = (is_empty || IS_DIGGABLE(e));
10120       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10121       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10122       is_removable    = (is_diggable || is_collectible);
10123
10124       can_replace[xx][yy] =
10125         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10126           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10127           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10128           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10129           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10130           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10131          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10132
10133       if (!can_replace[xx][yy])
10134         complete_replace = FALSE;
10135     }
10136
10137     if (!change->only_if_complete || complete_replace)
10138     {
10139       boolean something_has_changed = FALSE;
10140
10141       if (change->only_if_complete && change->use_random_replace &&
10142           RND(100) < change->random_percentage)
10143         return FALSE;
10144
10145       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10146       {
10147         int ex = x + xx - 1;
10148         int ey = y + yy - 1;
10149         int content_element;
10150
10151         if (can_replace[xx][yy] && (!change->use_random_replace ||
10152                                     RND(100) < change->random_percentage))
10153         {
10154           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10155             RemoveMovingField(ex, ey);
10156
10157           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10158
10159           content_element = change->target_content.e[xx][yy];
10160           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10161                                               ce_value, ce_score);
10162
10163           CreateElementFromChange(ex, ey, target_element);
10164
10165           something_has_changed = TRUE;
10166
10167           /* for symmetry reasons, freeze newly created border elements */
10168           if (ex != x || ey != y)
10169             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10170         }
10171       }
10172
10173       if (something_has_changed)
10174       {
10175         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10176         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10177       }
10178     }
10179   }
10180   else
10181   {
10182     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10183                                         ce_value, ce_score);
10184
10185     if (element == EL_DIAGONAL_GROWING ||
10186         element == EL_DIAGONAL_SHRINKING)
10187     {
10188       target_element = Store[x][y];
10189
10190       Store[x][y] = EL_EMPTY;
10191     }
10192
10193     CreateElementFromChange(x, y, target_element);
10194
10195     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10196     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10197   }
10198
10199   /* this uses direct change before indirect change */
10200   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10201
10202   return TRUE;
10203 }
10204
10205 static void HandleElementChange(int x, int y, int page)
10206 {
10207   int element = MovingOrBlocked2Element(x, y);
10208   struct ElementInfo *ei = &element_info[element];
10209   struct ElementChangeInfo *change = &ei->change_page[page];
10210   boolean handle_action_before_change = FALSE;
10211
10212 #ifdef DEBUG
10213   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10214       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10215   {
10216     printf("\n\n");
10217     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10218            x, y, element, element_info[element].token_name);
10219     printf("HandleElementChange(): This should never happen!\n");
10220     printf("\n\n");
10221   }
10222 #endif
10223
10224   /* this can happen with classic bombs on walkable, changing elements */
10225   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10226   {
10227     return;
10228   }
10229
10230   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10231   {
10232     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10233
10234     if (change->can_change)
10235     {
10236       /* !!! not clear why graphic animation should be reset at all here !!! */
10237       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10238       /* when a custom element is about to change (for example by change delay),
10239          do not reset graphic animation when the custom element is moving */
10240       if (!IS_MOVING(x, y))
10241       {
10242         ResetGfxAnimation(x, y);
10243         ResetRandomAnimationValue(x, y);
10244       }
10245
10246       if (change->pre_change_function)
10247         change->pre_change_function(x, y);
10248     }
10249   }
10250
10251   ChangeDelay[x][y]--;
10252
10253   if (ChangeDelay[x][y] != 0)           /* continue element change */
10254   {
10255     if (change->can_change)
10256     {
10257       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10258
10259       if (IS_ANIMATED(graphic))
10260         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10261
10262       if (change->change_function)
10263         change->change_function(x, y);
10264     }
10265   }
10266   else                                  /* finish element change */
10267   {
10268     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10269     {
10270       page = ChangePage[x][y];
10271       ChangePage[x][y] = -1;
10272
10273       change = &ei->change_page[page];
10274     }
10275
10276     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10277     {
10278       ChangeDelay[x][y] = 1;            /* try change after next move step */
10279       ChangePage[x][y] = page;          /* remember page to use for change */
10280
10281       return;
10282     }
10283
10284     /* special case: set new level random seed before changing element */
10285     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10286       handle_action_before_change = TRUE;
10287
10288     if (change->has_action && handle_action_before_change)
10289       ExecuteCustomElementAction(x, y, element, page);
10290
10291     if (change->can_change)
10292     {
10293       if (ChangeElement(x, y, element, page))
10294       {
10295         if (change->post_change_function)
10296           change->post_change_function(x, y);
10297       }
10298     }
10299
10300     if (change->has_action && !handle_action_before_change)
10301       ExecuteCustomElementAction(x, y, element, page);
10302   }
10303 }
10304
10305 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10306                                               int trigger_element,
10307                                               int trigger_event,
10308                                               int trigger_player,
10309                                               int trigger_side,
10310                                               int trigger_page)
10311 {
10312   boolean change_done_any = FALSE;
10313   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10314   int i;
10315
10316   if (!(trigger_events[trigger_element][trigger_event]))
10317     return FALSE;
10318
10319   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10320
10321   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10322   {
10323     int element = EL_CUSTOM_START + i;
10324     boolean change_done = FALSE;
10325     int p;
10326
10327     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10328         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10329       continue;
10330
10331     for (p = 0; p < element_info[element].num_change_pages; p++)
10332     {
10333       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10334
10335       if (change->can_change_or_has_action &&
10336           change->has_event[trigger_event] &&
10337           change->trigger_side & trigger_side &&
10338           change->trigger_player & trigger_player &&
10339           change->trigger_page & trigger_page_bits &&
10340           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10341       {
10342         change->actual_trigger_element = trigger_element;
10343         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10344         change->actual_trigger_player_bits = trigger_player;
10345         change->actual_trigger_side = trigger_side;
10346         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10347         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10348
10349         if ((change->can_change && !change_done) || change->has_action)
10350         {
10351           int x, y;
10352
10353           SCAN_PLAYFIELD(x, y)
10354           {
10355             if (Feld[x][y] == element)
10356             {
10357               if (change->can_change && !change_done)
10358               {
10359                 /* if element already changed in this frame, not only prevent
10360                    another element change (checked in ChangeElement()), but
10361                    also prevent additional element actions for this element */
10362
10363                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10364                     !level.use_action_after_change_bug)
10365                   continue;
10366
10367                 ChangeDelay[x][y] = 1;
10368                 ChangeEvent[x][y] = trigger_event;
10369
10370                 HandleElementChange(x, y, p);
10371               }
10372               else if (change->has_action)
10373               {
10374                 /* if element already changed in this frame, not only prevent
10375                    another element change (checked in ChangeElement()), but
10376                    also prevent additional element actions for this element */
10377
10378                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10379                     !level.use_action_after_change_bug)
10380                   continue;
10381
10382                 ExecuteCustomElementAction(x, y, element, p);
10383                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10384               }
10385             }
10386           }
10387
10388           if (change->can_change)
10389           {
10390             change_done = TRUE;
10391             change_done_any = TRUE;
10392           }
10393         }
10394       }
10395     }
10396   }
10397
10398   RECURSION_LOOP_DETECTION_END();
10399
10400   return change_done_any;
10401 }
10402
10403 static boolean CheckElementChangeExt(int x, int y,
10404                                      int element,
10405                                      int trigger_element,
10406                                      int trigger_event,
10407                                      int trigger_player,
10408                                      int trigger_side)
10409 {
10410   boolean change_done = FALSE;
10411   int p;
10412
10413   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10414       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10415     return FALSE;
10416
10417   if (Feld[x][y] == EL_BLOCKED)
10418   {
10419     Blocked2Moving(x, y, &x, &y);
10420     element = Feld[x][y];
10421   }
10422
10423   /* check if element has already changed or is about to change after moving */
10424   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10425        Feld[x][y] != element) ||
10426
10427       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10428        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10429         ChangePage[x][y] != -1)))
10430     return FALSE;
10431
10432   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10433
10434   for (p = 0; p < element_info[element].num_change_pages; p++)
10435   {
10436     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10437
10438     /* check trigger element for all events where the element that is checked
10439        for changing interacts with a directly adjacent element -- this is
10440        different to element changes that affect other elements to change on the
10441        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10442     boolean check_trigger_element =
10443       (trigger_event == CE_TOUCHING_X ||
10444        trigger_event == CE_HITTING_X ||
10445        trigger_event == CE_HIT_BY_X ||
10446        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10447
10448     if (change->can_change_or_has_action &&
10449         change->has_event[trigger_event] &&
10450         change->trigger_side & trigger_side &&
10451         change->trigger_player & trigger_player &&
10452         (!check_trigger_element ||
10453          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10454     {
10455       change->actual_trigger_element = trigger_element;
10456       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10457       change->actual_trigger_player_bits = trigger_player;
10458       change->actual_trigger_side = trigger_side;
10459       change->actual_trigger_ce_value = CustomValue[x][y];
10460       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10461
10462       /* special case: trigger element not at (x,y) position for some events */
10463       if (check_trigger_element)
10464       {
10465         static struct
10466         {
10467           int dx, dy;
10468         } move_xy[] =
10469           {
10470             {  0,  0 },
10471             { -1,  0 },
10472             { +1,  0 },
10473             {  0,  0 },
10474             {  0, -1 },
10475             {  0,  0 }, { 0, 0 }, { 0, 0 },
10476             {  0, +1 }
10477           };
10478
10479         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10480         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10481
10482         change->actual_trigger_ce_value = CustomValue[xx][yy];
10483         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10484       }
10485
10486       if (change->can_change && !change_done)
10487       {
10488         ChangeDelay[x][y] = 1;
10489         ChangeEvent[x][y] = trigger_event;
10490
10491         HandleElementChange(x, y, p);
10492
10493         change_done = TRUE;
10494       }
10495       else if (change->has_action)
10496       {
10497         ExecuteCustomElementAction(x, y, element, p);
10498         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10499       }
10500     }
10501   }
10502
10503   RECURSION_LOOP_DETECTION_END();
10504
10505   return change_done;
10506 }
10507
10508 static void PlayPlayerSound(struct PlayerInfo *player)
10509 {
10510   int jx = player->jx, jy = player->jy;
10511   int sound_element = player->artwork_element;
10512   int last_action = player->last_action_waiting;
10513   int action = player->action_waiting;
10514
10515   if (player->is_waiting)
10516   {
10517     if (action != last_action)
10518       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10519     else
10520       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10521   }
10522   else
10523   {
10524     if (action != last_action)
10525       StopSound(element_info[sound_element].sound[last_action]);
10526
10527     if (last_action == ACTION_SLEEPING)
10528       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10529   }
10530 }
10531
10532 static void PlayAllPlayersSound()
10533 {
10534   int i;
10535
10536   for (i = 0; i < MAX_PLAYERS; i++)
10537     if (stored_player[i].active)
10538       PlayPlayerSound(&stored_player[i]);
10539 }
10540
10541 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10542 {
10543   boolean last_waiting = player->is_waiting;
10544   int move_dir = player->MovDir;
10545
10546   player->dir_waiting = move_dir;
10547   player->last_action_waiting = player->action_waiting;
10548
10549   if (is_waiting)
10550   {
10551     if (!last_waiting)          /* not waiting -> waiting */
10552     {
10553       player->is_waiting = TRUE;
10554
10555       player->frame_counter_bored =
10556         FrameCounter +
10557         game.player_boring_delay_fixed +
10558         GetSimpleRandom(game.player_boring_delay_random);
10559       player->frame_counter_sleeping =
10560         FrameCounter +
10561         game.player_sleeping_delay_fixed +
10562         GetSimpleRandom(game.player_sleeping_delay_random);
10563
10564       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10565     }
10566
10567     if (game.player_sleeping_delay_fixed +
10568         game.player_sleeping_delay_random > 0 &&
10569         player->anim_delay_counter == 0 &&
10570         player->post_delay_counter == 0 &&
10571         FrameCounter >= player->frame_counter_sleeping)
10572       player->is_sleeping = TRUE;
10573     else if (game.player_boring_delay_fixed +
10574              game.player_boring_delay_random > 0 &&
10575              FrameCounter >= player->frame_counter_bored)
10576       player->is_bored = TRUE;
10577
10578     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10579                               player->is_bored ? ACTION_BORING :
10580                               ACTION_WAITING);
10581
10582     if (player->is_sleeping && player->use_murphy)
10583     {
10584       /* special case for sleeping Murphy when leaning against non-free tile */
10585
10586       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10587           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10588            !IS_MOVING(player->jx - 1, player->jy)))
10589         move_dir = MV_LEFT;
10590       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10591                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10592                 !IS_MOVING(player->jx + 1, player->jy)))
10593         move_dir = MV_RIGHT;
10594       else
10595         player->is_sleeping = FALSE;
10596
10597       player->dir_waiting = move_dir;
10598     }
10599
10600     if (player->is_sleeping)
10601     {
10602       if (player->num_special_action_sleeping > 0)
10603       {
10604         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10605         {
10606           int last_special_action = player->special_action_sleeping;
10607           int num_special_action = player->num_special_action_sleeping;
10608           int special_action =
10609             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10610              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10611              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10612              last_special_action + 1 : ACTION_SLEEPING);
10613           int special_graphic =
10614             el_act_dir2img(player->artwork_element, special_action, move_dir);
10615
10616           player->anim_delay_counter =
10617             graphic_info[special_graphic].anim_delay_fixed +
10618             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10619           player->post_delay_counter =
10620             graphic_info[special_graphic].post_delay_fixed +
10621             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10622
10623           player->special_action_sleeping = special_action;
10624         }
10625
10626         if (player->anim_delay_counter > 0)
10627         {
10628           player->action_waiting = player->special_action_sleeping;
10629           player->anim_delay_counter--;
10630         }
10631         else if (player->post_delay_counter > 0)
10632         {
10633           player->post_delay_counter--;
10634         }
10635       }
10636     }
10637     else if (player->is_bored)
10638     {
10639       if (player->num_special_action_bored > 0)
10640       {
10641         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10642         {
10643           int special_action =
10644             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10645           int special_graphic =
10646             el_act_dir2img(player->artwork_element, special_action, move_dir);
10647
10648           player->anim_delay_counter =
10649             graphic_info[special_graphic].anim_delay_fixed +
10650             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10651           player->post_delay_counter =
10652             graphic_info[special_graphic].post_delay_fixed +
10653             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10654
10655           player->special_action_bored = special_action;
10656         }
10657
10658         if (player->anim_delay_counter > 0)
10659         {
10660           player->action_waiting = player->special_action_bored;
10661           player->anim_delay_counter--;
10662         }
10663         else if (player->post_delay_counter > 0)
10664         {
10665           player->post_delay_counter--;
10666         }
10667       }
10668     }
10669   }
10670   else if (last_waiting)        /* waiting -> not waiting */
10671   {
10672     player->is_waiting = FALSE;
10673     player->is_bored = FALSE;
10674     player->is_sleeping = FALSE;
10675
10676     player->frame_counter_bored = -1;
10677     player->frame_counter_sleeping = -1;
10678
10679     player->anim_delay_counter = 0;
10680     player->post_delay_counter = 0;
10681
10682     player->dir_waiting = player->MovDir;
10683     player->action_waiting = ACTION_DEFAULT;
10684
10685     player->special_action_bored = ACTION_DEFAULT;
10686     player->special_action_sleeping = ACTION_DEFAULT;
10687   }
10688 }
10689
10690 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10691 {
10692   static boolean player_was_moving = FALSE;
10693   static boolean player_was_snapping = FALSE;
10694   static boolean player_was_dropping = FALSE;
10695
10696   if ((!player->is_moving  && player_was_moving) ||
10697       (player->MovPos == 0 && player_was_moving) ||
10698       (player->is_snapping && !player_was_snapping) ||
10699       (player->is_dropping && !player_was_dropping))
10700   {
10701     if (!SaveEngineSnapshotToList())
10702       return;
10703
10704     player_was_moving = FALSE;
10705     player_was_snapping = TRUE;
10706     player_was_dropping = TRUE;
10707   }
10708   else
10709   {
10710     if (player->is_moving)
10711       player_was_moving = TRUE;
10712
10713     if (!player->is_snapping)
10714       player_was_snapping = FALSE;
10715
10716     if (!player->is_dropping)
10717       player_was_dropping = FALSE;
10718   }
10719 }
10720
10721 static void CheckSingleStepMode(struct PlayerInfo *player)
10722 {
10723   if (tape.single_step && tape.recording && !tape.pausing)
10724   {
10725     /* as it is called "single step mode", just return to pause mode when the
10726        player stopped moving after one tile (or never starts moving at all) */
10727     if (!player->is_moving && !player->is_pushing)
10728     {
10729       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10730       SnapField(player, 0, 0);                  /* stop snapping */
10731     }
10732   }
10733
10734   CheckSaveEngineSnapshot(player);
10735 }
10736
10737 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10738 {
10739   int left      = player_action & JOY_LEFT;
10740   int right     = player_action & JOY_RIGHT;
10741   int up        = player_action & JOY_UP;
10742   int down      = player_action & JOY_DOWN;
10743   int button1   = player_action & JOY_BUTTON_1;
10744   int button2   = player_action & JOY_BUTTON_2;
10745   int dx        = (left ? -1 : right ? 1 : 0);
10746   int dy        = (up   ? -1 : down  ? 1 : 0);
10747
10748   if (!player->active || tape.pausing)
10749     return 0;
10750
10751   if (player_action)
10752   {
10753     if (button1)
10754       SnapField(player, dx, dy);
10755     else
10756     {
10757       if (button2)
10758         DropElement(player);
10759
10760       MovePlayer(player, dx, dy);
10761     }
10762
10763     CheckSingleStepMode(player);
10764
10765     SetPlayerWaiting(player, FALSE);
10766
10767     return player_action;
10768   }
10769   else
10770   {
10771     /* no actions for this player (no input at player's configured device) */
10772
10773     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10774     SnapField(player, 0, 0);
10775     CheckGravityMovementWhenNotMoving(player);
10776
10777     if (player->MovPos == 0)
10778       SetPlayerWaiting(player, TRUE);
10779
10780     if (player->MovPos == 0)    /* needed for tape.playing */
10781       player->is_moving = FALSE;
10782
10783     player->is_dropping = FALSE;
10784     player->is_dropping_pressed = FALSE;
10785     player->drop_pressed_delay = 0;
10786
10787     CheckSingleStepMode(player);
10788
10789     return 0;
10790   }
10791 }
10792
10793 static void CheckLevelTime()
10794 {
10795   int i;
10796
10797   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10798   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10799   {
10800     if (level.native_em_level->lev->home == 0)  /* all players at home */
10801     {
10802       PlayerWins(local_player);
10803
10804       AllPlayersGone = TRUE;
10805
10806       level.native_em_level->lev->home = -1;
10807     }
10808
10809     if (level.native_em_level->ply[0]->alive == 0 &&
10810         level.native_em_level->ply[1]->alive == 0 &&
10811         level.native_em_level->ply[2]->alive == 0 &&
10812         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10813       AllPlayersGone = TRUE;
10814   }
10815   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10816   {
10817     if (game_sp.LevelSolved &&
10818         !game_sp.GameOver)                              /* game won */
10819     {
10820       PlayerWins(local_player);
10821
10822       game_sp.GameOver = TRUE;
10823
10824       AllPlayersGone = TRUE;
10825     }
10826
10827     if (game_sp.GameOver)                               /* game lost */
10828       AllPlayersGone = TRUE;
10829   }
10830
10831   if (TimeFrames >= FRAMES_PER_SECOND)
10832   {
10833     TimeFrames = 0;
10834     TapeTime++;
10835
10836     for (i = 0; i < MAX_PLAYERS; i++)
10837     {
10838       struct PlayerInfo *player = &stored_player[i];
10839
10840       if (SHIELD_ON(player))
10841       {
10842         player->shield_normal_time_left--;
10843
10844         if (player->shield_deadly_time_left > 0)
10845           player->shield_deadly_time_left--;
10846       }
10847     }
10848
10849     if (!local_player->LevelSolved && !level.use_step_counter)
10850     {
10851       TimePlayed++;
10852
10853       if (TimeLeft > 0)
10854       {
10855         TimeLeft--;
10856
10857         if (TimeLeft <= 10 && setup.time_limit)
10858           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10859
10860         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10861            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10862
10863         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10864
10865         if (!TimeLeft && setup.time_limit)
10866         {
10867           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10868             level.native_em_level->lev->killed_out_of_time = TRUE;
10869           else
10870             for (i = 0; i < MAX_PLAYERS; i++)
10871               KillPlayer(&stored_player[i]);
10872         }
10873       }
10874       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10875       {
10876         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10877       }
10878
10879       level.native_em_level->lev->time =
10880         (game.no_time_limit ? TimePlayed : TimeLeft);
10881     }
10882
10883     if (tape.recording || tape.playing)
10884       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10885   }
10886
10887   if (tape.recording || tape.playing)
10888     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10889
10890   UpdateAndDisplayGameControlValues();
10891 }
10892
10893 void AdvanceFrameAndPlayerCounters(int player_nr)
10894 {
10895   int i;
10896
10897   /* advance frame counters (global frame counter and time frame counter) */
10898   FrameCounter++;
10899   TimeFrames++;
10900
10901   /* advance player counters (counters for move delay, move animation etc.) */
10902   for (i = 0; i < MAX_PLAYERS; i++)
10903   {
10904     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10905     int move_delay_value = stored_player[i].move_delay_value;
10906     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10907
10908     if (!advance_player_counters)       /* not all players may be affected */
10909       continue;
10910
10911     if (move_frames == 0)       /* less than one move per game frame */
10912     {
10913       int stepsize = TILEX / move_delay_value;
10914       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10915       int count = (stored_player[i].is_moving ?
10916                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10917
10918       if (count % delay == 0)
10919         move_frames = 1;
10920     }
10921
10922     stored_player[i].Frame += move_frames;
10923
10924     if (stored_player[i].MovPos != 0)
10925       stored_player[i].StepFrame += move_frames;
10926
10927     if (stored_player[i].move_delay > 0)
10928       stored_player[i].move_delay--;
10929
10930     /* due to bugs in previous versions, counter must count up, not down */
10931     if (stored_player[i].push_delay != -1)
10932       stored_player[i].push_delay++;
10933
10934     if (stored_player[i].drop_delay > 0)
10935       stored_player[i].drop_delay--;
10936
10937     if (stored_player[i].is_dropping_pressed)
10938       stored_player[i].drop_pressed_delay++;
10939   }
10940 }
10941
10942 void StartGameActions(boolean init_network_game, boolean record_tape,
10943                       int random_seed)
10944 {
10945   unsigned int new_random_seed = InitRND(random_seed);
10946
10947   if (record_tape)
10948     TapeStartRecording(new_random_seed);
10949
10950 #if defined(NETWORK_AVALIABLE)
10951   if (init_network_game)
10952   {
10953     SendToServer_StartPlaying();
10954
10955     return;
10956   }
10957 #endif
10958
10959   InitGame();
10960 }
10961
10962 void GameActions()
10963 {
10964   static unsigned int game_frame_delay = 0;
10965   unsigned int game_frame_delay_value;
10966   byte *recorded_player_action;
10967   byte summarized_player_action = 0;
10968   byte tape_action[MAX_PLAYERS];
10969   int i;
10970
10971   for (i = 0; i < MAX_PLAYERS; i++)
10972   {
10973     struct PlayerInfo *player = &stored_player[i];
10974
10975     // allow engine snapshot if movement attempt was stopped
10976     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10977         (player->action & KEY_MOTION) == 0)
10978       game.snapshot.changed_action = TRUE;
10979
10980     // allow engine snapshot in case of snapping/dropping attempt
10981     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10982         (player->action & KEY_BUTTON) != 0)
10983       game.snapshot.changed_action = TRUE;
10984
10985     game.snapshot.last_action[i] = player->action;
10986   }
10987
10988   /* detect endless loops, caused by custom element programming */
10989   if (recursion_loop_detected && recursion_loop_depth == 0)
10990   {
10991     char *message = getStringCat3("Internal Error! Element ",
10992                                   EL_NAME(recursion_loop_element),
10993                                   " caused endless loop! Quit the game?");
10994
10995     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10996           EL_NAME(recursion_loop_element));
10997
10998     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10999
11000     recursion_loop_detected = FALSE;    /* if game should be continued */
11001
11002     free(message);
11003
11004     return;
11005   }
11006
11007   if (game.restart_level)
11008     StartGameActions(options.network, setup.autorecord, level.random_seed);
11009
11010   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11011   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11012   {
11013     if (level.native_em_level->lev->home == 0)  /* all players at home */
11014     {
11015       PlayerWins(local_player);
11016
11017       AllPlayersGone = TRUE;
11018
11019       level.native_em_level->lev->home = -1;
11020     }
11021
11022     if (level.native_em_level->ply[0]->alive == 0 &&
11023         level.native_em_level->ply[1]->alive == 0 &&
11024         level.native_em_level->ply[2]->alive == 0 &&
11025         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11026       AllPlayersGone = TRUE;
11027   }
11028   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11029   {
11030     if (game_sp.LevelSolved &&
11031         !game_sp.GameOver)                              /* game won */
11032     {
11033       PlayerWins(local_player);
11034
11035       game_sp.GameOver = TRUE;
11036
11037       AllPlayersGone = TRUE;
11038     }
11039
11040     if (game_sp.GameOver)                               /* game lost */
11041       AllPlayersGone = TRUE;
11042   }
11043
11044   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11045     GameWon();
11046
11047   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11048     TapeStop();
11049
11050   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11051     return;
11052
11053   game_frame_delay_value =
11054     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11055
11056   if (tape.playing && tape.warp_forward && !tape.pausing)
11057     game_frame_delay_value = 0;
11058
11059   /* ---------- main game synchronization point ---------- */
11060
11061   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11062
11063   if (network_playing && !network_player_action_received)
11064   {
11065     /* try to get network player actions in time */
11066
11067 #if defined(NETWORK_AVALIABLE)
11068     /* last chance to get network player actions without main loop delay */
11069     HandleNetworking();
11070 #endif
11071
11072     /* game was quit by network peer */
11073     if (game_status != GAME_MODE_PLAYING)
11074       return;
11075
11076     if (!network_player_action_received)
11077       return;           /* failed to get network player actions in time */
11078
11079     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11080   }
11081
11082   if (tape.pausing)
11083     return;
11084
11085   /* at this point we know that we really continue executing the game */
11086
11087   network_player_action_received = FALSE;
11088
11089   /* when playing tape, read previously recorded player input from tape data */
11090   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11091
11092   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11093   if (tape.pausing)
11094     return;
11095
11096   if (tape.set_centered_player)
11097   {
11098     game.centered_player_nr_next = tape.centered_player_nr_next;
11099     game.set_centered_player = TRUE;
11100   }
11101
11102   for (i = 0; i < MAX_PLAYERS; i++)
11103   {
11104     summarized_player_action |= stored_player[i].action;
11105
11106     if (!network_playing && (game.team_mode || tape.playing))
11107       stored_player[i].effective_action = stored_player[i].action;
11108   }
11109
11110 #if defined(NETWORK_AVALIABLE)
11111   if (network_playing)
11112     SendToServer_MovePlayer(summarized_player_action);
11113 #endif
11114
11115   if (!options.network && !game.team_mode)
11116     local_player->effective_action = summarized_player_action;
11117
11118   if (tape.recording &&
11119       setup.team_mode &&
11120       setup.input_on_focus &&
11121       game.centered_player_nr != -1)
11122   {
11123     for (i = 0; i < MAX_PLAYERS; i++)
11124       stored_player[i].effective_action =
11125         (i == game.centered_player_nr ? summarized_player_action : 0);
11126   }
11127
11128   if (recorded_player_action != NULL)
11129     for (i = 0; i < MAX_PLAYERS; i++)
11130       stored_player[i].effective_action = recorded_player_action[i];
11131
11132   for (i = 0; i < MAX_PLAYERS; i++)
11133   {
11134     tape_action[i] = stored_player[i].effective_action;
11135
11136     /* (this may happen in the RND game engine if a player was not present on
11137        the playfield on level start, but appeared later from a custom element */
11138     if (setup.team_mode &&
11139         tape.recording &&
11140         tape_action[i] &&
11141         !tape.player_participates[i])
11142       tape.player_participates[i] = TRUE;
11143   }
11144
11145   /* only record actions from input devices, but not programmed actions */
11146   if (tape.recording)
11147     TapeRecordAction(tape_action);
11148
11149 #if USE_NEW_PLAYER_ASSIGNMENTS
11150   // !!! also map player actions in single player mode !!!
11151   // if (game.team_mode)
11152   {
11153     byte mapped_action[MAX_PLAYERS];
11154
11155 #if DEBUG_PLAYER_ACTIONS
11156     printf(":::");
11157     for (i = 0; i < MAX_PLAYERS; i++)
11158       printf(" %d, ", stored_player[i].effective_action);
11159 #endif
11160
11161     for (i = 0; i < MAX_PLAYERS; i++)
11162       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11163
11164     for (i = 0; i < MAX_PLAYERS; i++)
11165       stored_player[i].effective_action = mapped_action[i];
11166
11167 #if DEBUG_PLAYER_ACTIONS
11168     printf(" =>");
11169     for (i = 0; i < MAX_PLAYERS; i++)
11170       printf(" %d, ", stored_player[i].effective_action);
11171     printf("\n");
11172 #endif
11173   }
11174 #if DEBUG_PLAYER_ACTIONS
11175   else
11176   {
11177     printf(":::");
11178     for (i = 0; i < MAX_PLAYERS; i++)
11179       printf(" %d, ", stored_player[i].effective_action);
11180     printf("\n");
11181   }
11182 #endif
11183 #endif
11184
11185   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11186   {
11187     GameActions_EM_Main();
11188   }
11189   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11190   {
11191     GameActions_SP_Main();
11192   }
11193   else
11194   {
11195     GameActions_RND_Main();
11196   }
11197
11198   BlitScreenToBitmap(backbuffer);
11199
11200   CheckLevelTime();
11201
11202   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11203
11204   if (options.debug)                    /* calculate frames per second */
11205   {
11206     static unsigned int fps_counter = 0;
11207     static int fps_frames = 0;
11208     unsigned int fps_delay_ms = Counter() - fps_counter;
11209
11210     fps_frames++;
11211
11212     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11213     {
11214       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11215
11216       fps_frames = 0;
11217       fps_counter = Counter();
11218     }
11219
11220     redraw_mask |= REDRAW_FPS;
11221   }
11222 }
11223
11224 void GameActions_EM_Main()
11225 {
11226   byte effective_action[MAX_PLAYERS];
11227   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11228   int i;
11229
11230   for (i = 0; i < MAX_PLAYERS; i++)
11231     effective_action[i] = stored_player[i].effective_action;
11232
11233   GameActions_EM(effective_action, warp_mode);
11234 }
11235
11236 void GameActions_SP_Main()
11237 {
11238   byte effective_action[MAX_PLAYERS];
11239   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11240   int i;
11241
11242   for (i = 0; i < MAX_PLAYERS; i++)
11243     effective_action[i] = stored_player[i].effective_action;
11244
11245   GameActions_SP(effective_action, warp_mode);
11246 }
11247
11248 void GameActions_RND_Main()
11249 {
11250   GameActions_RND();
11251 }
11252
11253 void GameActions_RND()
11254 {
11255   int magic_wall_x = 0, magic_wall_y = 0;
11256   int i, x, y, element, graphic;
11257
11258   InitPlayfieldScanModeVars();
11259
11260   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11261   {
11262     SCAN_PLAYFIELD(x, y)
11263     {
11264       ChangeCount[x][y] = 0;
11265       ChangeEvent[x][y] = -1;
11266     }
11267   }
11268
11269   if (game.set_centered_player)
11270   {
11271     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11272
11273     /* switching to "all players" only possible if all players fit to screen */
11274     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11275     {
11276       game.centered_player_nr_next = game.centered_player_nr;
11277       game.set_centered_player = FALSE;
11278     }
11279
11280     /* do not switch focus to non-existing (or non-active) player */
11281     if (game.centered_player_nr_next >= 0 &&
11282         !stored_player[game.centered_player_nr_next].active)
11283     {
11284       game.centered_player_nr_next = game.centered_player_nr;
11285       game.set_centered_player = FALSE;
11286     }
11287   }
11288
11289   if (game.set_centered_player &&
11290       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11291   {
11292     int sx, sy;
11293
11294     if (game.centered_player_nr_next == -1)
11295     {
11296       setScreenCenteredToAllPlayers(&sx, &sy);
11297     }
11298     else
11299     {
11300       sx = stored_player[game.centered_player_nr_next].jx;
11301       sy = stored_player[game.centered_player_nr_next].jy;
11302     }
11303
11304     game.centered_player_nr = game.centered_player_nr_next;
11305     game.set_centered_player = FALSE;
11306
11307     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11308     DrawGameDoorValues();
11309   }
11310
11311   for (i = 0; i < MAX_PLAYERS; i++)
11312   {
11313     int actual_player_action = stored_player[i].effective_action;
11314
11315 #if 1
11316     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11317        - rnd_equinox_tetrachloride 048
11318        - rnd_equinox_tetrachloride_ii 096
11319        - rnd_emanuel_schmieg 002
11320        - doctor_sloan_ww 001, 020
11321     */
11322     if (stored_player[i].MovPos == 0)
11323       CheckGravityMovement(&stored_player[i]);
11324 #endif
11325
11326     /* overwrite programmed action with tape action */
11327     if (stored_player[i].programmed_action)
11328       actual_player_action = stored_player[i].programmed_action;
11329
11330     PlayerActions(&stored_player[i], actual_player_action);
11331
11332     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11333   }
11334
11335   ScrollScreen(NULL, SCROLL_GO_ON);
11336
11337   /* for backwards compatibility, the following code emulates a fixed bug that
11338      occured when pushing elements (causing elements that just made their last
11339      pushing step to already (if possible) make their first falling step in the
11340      same game frame, which is bad); this code is also needed to use the famous
11341      "spring push bug" which is used in older levels and might be wanted to be
11342      used also in newer levels, but in this case the buggy pushing code is only
11343      affecting the "spring" element and no other elements */
11344
11345   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11346   {
11347     for (i = 0; i < MAX_PLAYERS; i++)
11348     {
11349       struct PlayerInfo *player = &stored_player[i];
11350       int x = player->jx;
11351       int y = player->jy;
11352
11353       if (player->active && player->is_pushing && player->is_moving &&
11354           IS_MOVING(x, y) &&
11355           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11356            Feld[x][y] == EL_SPRING))
11357       {
11358         ContinueMoving(x, y);
11359
11360         /* continue moving after pushing (this is actually a bug) */
11361         if (!IS_MOVING(x, y))
11362           Stop[x][y] = FALSE;
11363       }
11364     }
11365   }
11366
11367   SCAN_PLAYFIELD(x, y)
11368   {
11369     ChangeCount[x][y] = 0;
11370     ChangeEvent[x][y] = -1;
11371
11372     /* this must be handled before main playfield loop */
11373     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11374     {
11375       MovDelay[x][y]--;
11376       if (MovDelay[x][y] <= 0)
11377         RemoveField(x, y);
11378     }
11379
11380     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11381     {
11382       MovDelay[x][y]--;
11383       if (MovDelay[x][y] <= 0)
11384       {
11385         RemoveField(x, y);
11386         TEST_DrawLevelField(x, y);
11387
11388         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11389       }
11390     }
11391
11392 #if DEBUG
11393     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11394     {
11395       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11396       printf("GameActions(): This should never happen!\n");
11397
11398       ChangePage[x][y] = -1;
11399     }
11400 #endif
11401
11402     Stop[x][y] = FALSE;
11403     if (WasJustMoving[x][y] > 0)
11404       WasJustMoving[x][y]--;
11405     if (WasJustFalling[x][y] > 0)
11406       WasJustFalling[x][y]--;
11407     if (CheckCollision[x][y] > 0)
11408       CheckCollision[x][y]--;
11409     if (CheckImpact[x][y] > 0)
11410       CheckImpact[x][y]--;
11411
11412     GfxFrame[x][y]++;
11413
11414     /* reset finished pushing action (not done in ContinueMoving() to allow
11415        continuous pushing animation for elements with zero push delay) */
11416     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11417     {
11418       ResetGfxAnimation(x, y);
11419       TEST_DrawLevelField(x, y);
11420     }
11421
11422 #if DEBUG
11423     if (IS_BLOCKED(x, y))
11424     {
11425       int oldx, oldy;
11426
11427       Blocked2Moving(x, y, &oldx, &oldy);
11428       if (!IS_MOVING(oldx, oldy))
11429       {
11430         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11431         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11432         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11433         printf("GameActions(): This should never happen!\n");
11434       }
11435     }
11436 #endif
11437   }
11438
11439   SCAN_PLAYFIELD(x, y)
11440   {
11441     element = Feld[x][y];
11442     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11443
11444     ResetGfxFrame(x, y, TRUE);
11445
11446     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11447         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11448       ResetRandomAnimationValue(x, y);
11449
11450     SetRandomAnimationValue(x, y);
11451
11452     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11453
11454     if (IS_INACTIVE(element))
11455     {
11456       if (IS_ANIMATED(graphic))
11457         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11458
11459       continue;
11460     }
11461
11462     /* this may take place after moving, so 'element' may have changed */
11463     if (IS_CHANGING(x, y) &&
11464         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11465     {
11466       int page = element_info[element].event_page_nr[CE_DELAY];
11467
11468       HandleElementChange(x, y, page);
11469
11470       element = Feld[x][y];
11471       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11472     }
11473
11474     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11475     {
11476       StartMoving(x, y);
11477
11478       element = Feld[x][y];
11479       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11480
11481       if (IS_ANIMATED(graphic) &&
11482           !IS_MOVING(x, y) &&
11483           !Stop[x][y])
11484         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11485
11486       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11487         TEST_DrawTwinkleOnField(x, y);
11488     }
11489     else if ((element == EL_ACID ||
11490               element == EL_EXIT_OPEN ||
11491               element == EL_EM_EXIT_OPEN ||
11492               element == EL_SP_EXIT_OPEN ||
11493               element == EL_STEEL_EXIT_OPEN ||
11494               element == EL_EM_STEEL_EXIT_OPEN ||
11495               element == EL_SP_TERMINAL ||
11496               element == EL_SP_TERMINAL_ACTIVE ||
11497               element == EL_EXTRA_TIME ||
11498               element == EL_SHIELD_NORMAL ||
11499               element == EL_SHIELD_DEADLY) &&
11500              IS_ANIMATED(graphic))
11501       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11502     else if (IS_MOVING(x, y))
11503       ContinueMoving(x, y);
11504     else if (IS_ACTIVE_BOMB(element))
11505       CheckDynamite(x, y);
11506     else if (element == EL_AMOEBA_GROWING)
11507       AmoebeWaechst(x, y);
11508     else if (element == EL_AMOEBA_SHRINKING)
11509       AmoebaDisappearing(x, y);
11510
11511 #if !USE_NEW_AMOEBA_CODE
11512     else if (IS_AMOEBALIVE(element))
11513       AmoebeAbleger(x, y);
11514 #endif
11515
11516     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11517       Life(x, y);
11518     else if (element == EL_EXIT_CLOSED)
11519       CheckExit(x, y);
11520     else if (element == EL_EM_EXIT_CLOSED)
11521       CheckExitEM(x, y);
11522     else if (element == EL_STEEL_EXIT_CLOSED)
11523       CheckExitSteel(x, y);
11524     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11525       CheckExitSteelEM(x, y);
11526     else if (element == EL_SP_EXIT_CLOSED)
11527       CheckExitSP(x, y);
11528     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11529              element == EL_EXPANDABLE_STEELWALL_GROWING)
11530       MauerWaechst(x, y);
11531     else if (element == EL_EXPANDABLE_WALL ||
11532              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11533              element == EL_EXPANDABLE_WALL_VERTICAL ||
11534              element == EL_EXPANDABLE_WALL_ANY ||
11535              element == EL_BD_EXPANDABLE_WALL)
11536       MauerAbleger(x, y);
11537     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11538              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11539              element == EL_EXPANDABLE_STEELWALL_ANY)
11540       MauerAblegerStahl(x, y);
11541     else if (element == EL_FLAMES)
11542       CheckForDragon(x, y);
11543     else if (element == EL_EXPLOSION)
11544       ; /* drawing of correct explosion animation is handled separately */
11545     else if (element == EL_ELEMENT_SNAPPING ||
11546              element == EL_DIAGONAL_SHRINKING ||
11547              element == EL_DIAGONAL_GROWING)
11548     {
11549       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11550
11551       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11552     }
11553     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11554       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11555
11556     if (IS_BELT_ACTIVE(element))
11557       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11558
11559     if (game.magic_wall_active)
11560     {
11561       int jx = local_player->jx, jy = local_player->jy;
11562
11563       /* play the element sound at the position nearest to the player */
11564       if ((element == EL_MAGIC_WALL_FULL ||
11565            element == EL_MAGIC_WALL_ACTIVE ||
11566            element == EL_MAGIC_WALL_EMPTYING ||
11567            element == EL_BD_MAGIC_WALL_FULL ||
11568            element == EL_BD_MAGIC_WALL_ACTIVE ||
11569            element == EL_BD_MAGIC_WALL_EMPTYING ||
11570            element == EL_DC_MAGIC_WALL_FULL ||
11571            element == EL_DC_MAGIC_WALL_ACTIVE ||
11572            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11573           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11574       {
11575         magic_wall_x = x;
11576         magic_wall_y = y;
11577       }
11578     }
11579   }
11580
11581 #if USE_NEW_AMOEBA_CODE
11582   /* new experimental amoeba growth stuff */
11583   if (!(FrameCounter % 8))
11584   {
11585     static unsigned int random = 1684108901;
11586
11587     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11588     {
11589       x = RND(lev_fieldx);
11590       y = RND(lev_fieldy);
11591       element = Feld[x][y];
11592
11593       if (!IS_PLAYER(x,y) &&
11594           (element == EL_EMPTY ||
11595            CAN_GROW_INTO(element) ||
11596            element == EL_QUICKSAND_EMPTY ||
11597            element == EL_QUICKSAND_FAST_EMPTY ||
11598            element == EL_ACID_SPLASH_LEFT ||
11599            element == EL_ACID_SPLASH_RIGHT))
11600       {
11601         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11602             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11603             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11604             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11605           Feld[x][y] = EL_AMOEBA_DROP;
11606       }
11607
11608       random = random * 129 + 1;
11609     }
11610   }
11611 #endif
11612
11613   game.explosions_delayed = FALSE;
11614
11615   SCAN_PLAYFIELD(x, y)
11616   {
11617     element = Feld[x][y];
11618
11619     if (ExplodeField[x][y])
11620       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11621     else if (element == EL_EXPLOSION)
11622       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11623
11624     ExplodeField[x][y] = EX_TYPE_NONE;
11625   }
11626
11627   game.explosions_delayed = TRUE;
11628
11629   if (game.magic_wall_active)
11630   {
11631     if (!(game.magic_wall_time_left % 4))
11632     {
11633       int element = Feld[magic_wall_x][magic_wall_y];
11634
11635       if (element == EL_BD_MAGIC_WALL_FULL ||
11636           element == EL_BD_MAGIC_WALL_ACTIVE ||
11637           element == EL_BD_MAGIC_WALL_EMPTYING)
11638         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11639       else if (element == EL_DC_MAGIC_WALL_FULL ||
11640                element == EL_DC_MAGIC_WALL_ACTIVE ||
11641                element == EL_DC_MAGIC_WALL_EMPTYING)
11642         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11643       else
11644         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11645     }
11646
11647     if (game.magic_wall_time_left > 0)
11648     {
11649       game.magic_wall_time_left--;
11650
11651       if (!game.magic_wall_time_left)
11652       {
11653         SCAN_PLAYFIELD(x, y)
11654         {
11655           element = Feld[x][y];
11656
11657           if (element == EL_MAGIC_WALL_ACTIVE ||
11658               element == EL_MAGIC_WALL_FULL)
11659           {
11660             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11661             TEST_DrawLevelField(x, y);
11662           }
11663           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11664                    element == EL_BD_MAGIC_WALL_FULL)
11665           {
11666             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11667             TEST_DrawLevelField(x, y);
11668           }
11669           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11670                    element == EL_DC_MAGIC_WALL_FULL)
11671           {
11672             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11673             TEST_DrawLevelField(x, y);
11674           }
11675         }
11676
11677         game.magic_wall_active = FALSE;
11678       }
11679     }
11680   }
11681
11682   if (game.light_time_left > 0)
11683   {
11684     game.light_time_left--;
11685
11686     if (game.light_time_left == 0)
11687       RedrawAllLightSwitchesAndInvisibleElements();
11688   }
11689
11690   if (game.timegate_time_left > 0)
11691   {
11692     game.timegate_time_left--;
11693
11694     if (game.timegate_time_left == 0)
11695       CloseAllOpenTimegates();
11696   }
11697
11698   if (game.lenses_time_left > 0)
11699   {
11700     game.lenses_time_left--;
11701
11702     if (game.lenses_time_left == 0)
11703       RedrawAllInvisibleElementsForLenses();
11704   }
11705
11706   if (game.magnify_time_left > 0)
11707   {
11708     game.magnify_time_left--;
11709
11710     if (game.magnify_time_left == 0)
11711       RedrawAllInvisibleElementsForMagnifier();
11712   }
11713
11714   for (i = 0; i < MAX_PLAYERS; i++)
11715   {
11716     struct PlayerInfo *player = &stored_player[i];
11717
11718     if (SHIELD_ON(player))
11719     {
11720       if (player->shield_deadly_time_left)
11721         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11722       else if (player->shield_normal_time_left)
11723         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11724     }
11725   }
11726
11727 #if USE_DELAYED_GFX_REDRAW
11728   SCAN_PLAYFIELD(x, y)
11729   {
11730     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11731     {
11732       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11733          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11734
11735       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11736         DrawLevelField(x, y);
11737
11738       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11739         DrawLevelFieldCrumbled(x, y);
11740
11741       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11742         DrawLevelFieldCrumbledNeighbours(x, y);
11743
11744       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11745         DrawTwinkleOnField(x, y);
11746     }
11747
11748     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11749   }
11750 #endif
11751
11752   DrawAllPlayers();
11753   PlayAllPlayersSound();
11754
11755   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11756   {
11757     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11758
11759     local_player->show_envelope = 0;
11760   }
11761
11762   /* use random number generator in every frame to make it less predictable */
11763   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11764     RND(1);
11765 }
11766
11767 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11768 {
11769   int min_x = x, min_y = y, max_x = x, max_y = y;
11770   int i;
11771
11772   for (i = 0; i < MAX_PLAYERS; i++)
11773   {
11774     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11775
11776     if (!stored_player[i].active || &stored_player[i] == player)
11777       continue;
11778
11779     min_x = MIN(min_x, jx);
11780     min_y = MIN(min_y, jy);
11781     max_x = MAX(max_x, jx);
11782     max_y = MAX(max_y, jy);
11783   }
11784
11785   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11786 }
11787
11788 static boolean AllPlayersInVisibleScreen()
11789 {
11790   int i;
11791
11792   for (i = 0; i < MAX_PLAYERS; i++)
11793   {
11794     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11795
11796     if (!stored_player[i].active)
11797       continue;
11798
11799     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11800       return FALSE;
11801   }
11802
11803   return TRUE;
11804 }
11805
11806 void ScrollLevel(int dx, int dy)
11807 {
11808   int scroll_offset = 2 * TILEX_VAR;
11809   int x, y;
11810
11811   BlitBitmap(drawto_field, drawto_field,
11812              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11813              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11814              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11815              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11816              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11817              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11818
11819   if (dx != 0)
11820   {
11821     x = (dx == 1 ? BX1 : BX2);
11822     for (y = BY1; y <= BY2; y++)
11823       DrawScreenField(x, y);
11824   }
11825
11826   if (dy != 0)
11827   {
11828     y = (dy == 1 ? BY1 : BY2);
11829     for (x = BX1; x <= BX2; x++)
11830       DrawScreenField(x, y);
11831   }
11832
11833   redraw_mask |= REDRAW_FIELD;
11834 }
11835
11836 static boolean canFallDown(struct PlayerInfo *player)
11837 {
11838   int jx = player->jx, jy = player->jy;
11839
11840   return (IN_LEV_FIELD(jx, jy + 1) &&
11841           (IS_FREE(jx, jy + 1) ||
11842            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11843           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11844           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11845 }
11846
11847 static boolean canPassField(int x, int y, int move_dir)
11848 {
11849   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11850   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11851   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11852   int nextx = x + dx;
11853   int nexty = y + dy;
11854   int element = Feld[x][y];
11855
11856   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11857           !CAN_MOVE(element) &&
11858           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11859           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11860           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11861 }
11862
11863 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11864 {
11865   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11866   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11867   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11868   int newx = x + dx;
11869   int newy = y + dy;
11870
11871   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11872           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11873           (IS_DIGGABLE(Feld[newx][newy]) ||
11874            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11875            canPassField(newx, newy, move_dir)));
11876 }
11877
11878 static void CheckGravityMovement(struct PlayerInfo *player)
11879 {
11880   if (player->gravity && !player->programmed_action)
11881   {
11882     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11883     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11884     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11885     int jx = player->jx, jy = player->jy;
11886     boolean player_is_moving_to_valid_field =
11887       (!player_is_snapping &&
11888        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11889         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11890     boolean player_can_fall_down = canFallDown(player);
11891
11892     if (player_can_fall_down &&
11893         !player_is_moving_to_valid_field)
11894       player->programmed_action = MV_DOWN;
11895   }
11896 }
11897
11898 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11899 {
11900   return CheckGravityMovement(player);
11901
11902   if (player->gravity && !player->programmed_action)
11903   {
11904     int jx = player->jx, jy = player->jy;
11905     boolean field_under_player_is_free =
11906       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11907     boolean player_is_standing_on_valid_field =
11908       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11909        (IS_WALKABLE(Feld[jx][jy]) &&
11910         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11911
11912     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11913       player->programmed_action = MV_DOWN;
11914   }
11915 }
11916
11917 /*
11918   MovePlayerOneStep()
11919   -----------------------------------------------------------------------------
11920   dx, dy:               direction (non-diagonal) to try to move the player to
11921   real_dx, real_dy:     direction as read from input device (can be diagonal)
11922 */
11923
11924 boolean MovePlayerOneStep(struct PlayerInfo *player,
11925                           int dx, int dy, int real_dx, int real_dy)
11926 {
11927   int jx = player->jx, jy = player->jy;
11928   int new_jx = jx + dx, new_jy = jy + dy;
11929   int can_move;
11930   boolean player_can_move = !player->cannot_move;
11931
11932   if (!player->active || (!dx && !dy))
11933     return MP_NO_ACTION;
11934
11935   player->MovDir = (dx < 0 ? MV_LEFT :
11936                     dx > 0 ? MV_RIGHT :
11937                     dy < 0 ? MV_UP :
11938                     dy > 0 ? MV_DOWN :  MV_NONE);
11939
11940   if (!IN_LEV_FIELD(new_jx, new_jy))
11941     return MP_NO_ACTION;
11942
11943   if (!player_can_move)
11944   {
11945     if (player->MovPos == 0)
11946     {
11947       player->is_moving = FALSE;
11948       player->is_digging = FALSE;
11949       player->is_collecting = FALSE;
11950       player->is_snapping = FALSE;
11951       player->is_pushing = FALSE;
11952     }
11953   }
11954
11955   if (!options.network && game.centered_player_nr == -1 &&
11956       !AllPlayersInSight(player, new_jx, new_jy))
11957     return MP_NO_ACTION;
11958
11959   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11960   if (can_move != MP_MOVING)
11961     return can_move;
11962
11963   /* check if DigField() has caused relocation of the player */
11964   if (player->jx != jx || player->jy != jy)
11965     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11966
11967   StorePlayer[jx][jy] = 0;
11968   player->last_jx = jx;
11969   player->last_jy = jy;
11970   player->jx = new_jx;
11971   player->jy = new_jy;
11972   StorePlayer[new_jx][new_jy] = player->element_nr;
11973
11974   if (player->move_delay_value_next != -1)
11975   {
11976     player->move_delay_value = player->move_delay_value_next;
11977     player->move_delay_value_next = -1;
11978   }
11979
11980   player->MovPos =
11981     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11982
11983   player->step_counter++;
11984
11985   PlayerVisit[jx][jy] = FrameCounter;
11986
11987   player->is_moving = TRUE;
11988
11989 #if 1
11990   /* should better be called in MovePlayer(), but this breaks some tapes */
11991   ScrollPlayer(player, SCROLL_INIT);
11992 #endif
11993
11994   return MP_MOVING;
11995 }
11996
11997 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11998 {
11999   int jx = player->jx, jy = player->jy;
12000   int old_jx = jx, old_jy = jy;
12001   int moved = MP_NO_ACTION;
12002
12003   if (!player->active)
12004     return FALSE;
12005
12006   if (!dx && !dy)
12007   {
12008     if (player->MovPos == 0)
12009     {
12010       player->is_moving = FALSE;
12011       player->is_digging = FALSE;
12012       player->is_collecting = FALSE;
12013       player->is_snapping = FALSE;
12014       player->is_pushing = FALSE;
12015     }
12016
12017     return FALSE;
12018   }
12019
12020   if (player->move_delay > 0)
12021     return FALSE;
12022
12023   player->move_delay = -1;              /* set to "uninitialized" value */
12024
12025   /* store if player is automatically moved to next field */
12026   player->is_auto_moving = (player->programmed_action != MV_NONE);
12027
12028   /* remove the last programmed player action */
12029   player->programmed_action = 0;
12030
12031   if (player->MovPos)
12032   {
12033     /* should only happen if pre-1.2 tape recordings are played */
12034     /* this is only for backward compatibility */
12035
12036     int original_move_delay_value = player->move_delay_value;
12037
12038 #if DEBUG
12039     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12040            tape.counter);
12041 #endif
12042
12043     /* scroll remaining steps with finest movement resolution */
12044     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12045
12046     while (player->MovPos)
12047     {
12048       ScrollPlayer(player, SCROLL_GO_ON);
12049       ScrollScreen(NULL, SCROLL_GO_ON);
12050
12051       AdvanceFrameAndPlayerCounters(player->index_nr);
12052
12053       DrawAllPlayers();
12054       BackToFront();
12055     }
12056
12057     player->move_delay_value = original_move_delay_value;
12058   }
12059
12060   player->is_active = FALSE;
12061
12062   if (player->last_move_dir & MV_HORIZONTAL)
12063   {
12064     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12065       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12066   }
12067   else
12068   {
12069     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12070       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12071   }
12072
12073   if (!moved && !player->is_active)
12074   {
12075     player->is_moving = FALSE;
12076     player->is_digging = FALSE;
12077     player->is_collecting = FALSE;
12078     player->is_snapping = FALSE;
12079     player->is_pushing = FALSE;
12080   }
12081
12082   jx = player->jx;
12083   jy = player->jy;
12084
12085   if (moved & MP_MOVING && !ScreenMovPos &&
12086       (player->index_nr == game.centered_player_nr ||
12087        game.centered_player_nr == -1))
12088   {
12089     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12090     int offset = game.scroll_delay_value;
12091
12092     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12093     {
12094       /* actual player has left the screen -- scroll in that direction */
12095       if (jx != old_jx)         /* player has moved horizontally */
12096         scroll_x += (jx - old_jx);
12097       else                      /* player has moved vertically */
12098         scroll_y += (jy - old_jy);
12099     }
12100     else
12101     {
12102       if (jx != old_jx)         /* player has moved horizontally */
12103       {
12104         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12105             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12106           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12107
12108         /* don't scroll over playfield boundaries */
12109         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12110           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12111
12112         /* don't scroll more than one field at a time */
12113         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12114
12115         /* don't scroll against the player's moving direction */
12116         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12117             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12118           scroll_x = old_scroll_x;
12119       }
12120       else                      /* player has moved vertically */
12121       {
12122         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12123             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12124           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12125
12126         /* don't scroll over playfield boundaries */
12127         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12128           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12129
12130         /* don't scroll more than one field at a time */
12131         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12132
12133         /* don't scroll against the player's moving direction */
12134         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12135             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12136           scroll_y = old_scroll_y;
12137       }
12138     }
12139
12140     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12141     {
12142       if (!options.network && game.centered_player_nr == -1 &&
12143           !AllPlayersInVisibleScreen())
12144       {
12145         scroll_x = old_scroll_x;
12146         scroll_y = old_scroll_y;
12147       }
12148       else
12149       {
12150         ScrollScreen(player, SCROLL_INIT);
12151         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12152       }
12153     }
12154   }
12155
12156   player->StepFrame = 0;
12157
12158   if (moved & MP_MOVING)
12159   {
12160     if (old_jx != jx && old_jy == jy)
12161       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12162     else if (old_jx == jx && old_jy != jy)
12163       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12164
12165     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12166
12167     player->last_move_dir = player->MovDir;
12168     player->is_moving = TRUE;
12169     player->is_snapping = FALSE;
12170     player->is_switching = FALSE;
12171     player->is_dropping = FALSE;
12172     player->is_dropping_pressed = FALSE;
12173     player->drop_pressed_delay = 0;
12174
12175 #if 0
12176     /* should better be called here than above, but this breaks some tapes */
12177     ScrollPlayer(player, SCROLL_INIT);
12178 #endif
12179   }
12180   else
12181   {
12182     CheckGravityMovementWhenNotMoving(player);
12183
12184     player->is_moving = FALSE;
12185
12186     /* at this point, the player is allowed to move, but cannot move right now
12187        (e.g. because of something blocking the way) -- ensure that the player
12188        is also allowed to move in the next frame (in old versions before 3.1.1,
12189        the player was forced to wait again for eight frames before next try) */
12190
12191     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12192       player->move_delay = 0;   /* allow direct movement in the next frame */
12193   }
12194
12195   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12196     player->move_delay = player->move_delay_value;
12197
12198   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12199   {
12200     TestIfPlayerTouchesBadThing(jx, jy);
12201     TestIfPlayerTouchesCustomElement(jx, jy);
12202   }
12203
12204   if (!player->active)
12205     RemovePlayer(player);
12206
12207   return moved;
12208 }
12209
12210 void ScrollPlayer(struct PlayerInfo *player, int mode)
12211 {
12212   int jx = player->jx, jy = player->jy;
12213   int last_jx = player->last_jx, last_jy = player->last_jy;
12214   int move_stepsize = TILEX / player->move_delay_value;
12215
12216   if (!player->active)
12217     return;
12218
12219   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12220     return;
12221
12222   if (mode == SCROLL_INIT)
12223   {
12224     player->actual_frame_counter = FrameCounter;
12225     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12226
12227     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12228         Feld[last_jx][last_jy] == EL_EMPTY)
12229     {
12230       int last_field_block_delay = 0;   /* start with no blocking at all */
12231       int block_delay_adjustment = player->block_delay_adjustment;
12232
12233       /* if player blocks last field, add delay for exactly one move */
12234       if (player->block_last_field)
12235       {
12236         last_field_block_delay += player->move_delay_value;
12237
12238         /* when blocking enabled, prevent moving up despite gravity */
12239         if (player->gravity && player->MovDir == MV_UP)
12240           block_delay_adjustment = -1;
12241       }
12242
12243       /* add block delay adjustment (also possible when not blocking) */
12244       last_field_block_delay += block_delay_adjustment;
12245
12246       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12247       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12248     }
12249
12250     if (player->MovPos != 0)    /* player has not yet reached destination */
12251       return;
12252   }
12253   else if (!FrameReached(&player->actual_frame_counter, 1))
12254     return;
12255
12256   if (player->MovPos != 0)
12257   {
12258     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12259     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12260
12261     /* before DrawPlayer() to draw correct player graphic for this case */
12262     if (player->MovPos == 0)
12263       CheckGravityMovement(player);
12264   }
12265
12266   if (player->MovPos == 0)      /* player reached destination field */
12267   {
12268     if (player->move_delay_reset_counter > 0)
12269     {
12270       player->move_delay_reset_counter--;
12271
12272       if (player->move_delay_reset_counter == 0)
12273       {
12274         /* continue with normal speed after quickly moving through gate */
12275         HALVE_PLAYER_SPEED(player);
12276
12277         /* be able to make the next move without delay */
12278         player->move_delay = 0;
12279       }
12280     }
12281
12282     player->last_jx = jx;
12283     player->last_jy = jy;
12284
12285     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12286         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12287         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12288         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12289         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12290         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12291         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12292         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12293     {
12294       DrawPlayer(player);       /* needed here only to cleanup last field */
12295       RemovePlayer(player);
12296
12297       if (local_player->friends_still_needed == 0 ||
12298           IS_SP_ELEMENT(Feld[jx][jy]))
12299         PlayerWins(player);
12300     }
12301
12302     /* this breaks one level: "machine", level 000 */
12303     {
12304       int move_direction = player->MovDir;
12305       int enter_side = MV_DIR_OPPOSITE(move_direction);
12306       int leave_side = move_direction;
12307       int old_jx = last_jx;
12308       int old_jy = last_jy;
12309       int old_element = Feld[old_jx][old_jy];
12310       int new_element = Feld[jx][jy];
12311
12312       if (IS_CUSTOM_ELEMENT(old_element))
12313         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12314                                    CE_LEFT_BY_PLAYER,
12315                                    player->index_bit, leave_side);
12316
12317       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12318                                           CE_PLAYER_LEAVES_X,
12319                                           player->index_bit, leave_side);
12320
12321       if (IS_CUSTOM_ELEMENT(new_element))
12322         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12323                                    player->index_bit, enter_side);
12324
12325       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12326                                           CE_PLAYER_ENTERS_X,
12327                                           player->index_bit, enter_side);
12328
12329       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12330                                         CE_MOVE_OF_X, move_direction);
12331     }
12332
12333     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12334     {
12335       TestIfPlayerTouchesBadThing(jx, jy);
12336       TestIfPlayerTouchesCustomElement(jx, jy);
12337
12338       /* needed because pushed element has not yet reached its destination,
12339          so it would trigger a change event at its previous field location */
12340       if (!player->is_pushing)
12341         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12342
12343       if (!player->active)
12344         RemovePlayer(player);
12345     }
12346
12347     if (!local_player->LevelSolved && level.use_step_counter)
12348     {
12349       int i;
12350
12351       TimePlayed++;
12352
12353       if (TimeLeft > 0)
12354       {
12355         TimeLeft--;
12356
12357         if (TimeLeft <= 10 && setup.time_limit)
12358           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12359
12360         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12361
12362         DisplayGameControlValues();
12363
12364         if (!TimeLeft && setup.time_limit)
12365           for (i = 0; i < MAX_PLAYERS; i++)
12366             KillPlayer(&stored_player[i]);
12367       }
12368       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12369       {
12370         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12371
12372         DisplayGameControlValues();
12373       }
12374     }
12375
12376     if (tape.single_step && tape.recording && !tape.pausing &&
12377         !player->programmed_action)
12378       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12379
12380     if (!player->programmed_action)
12381       CheckSaveEngineSnapshot(player);
12382   }
12383 }
12384
12385 void ScrollScreen(struct PlayerInfo *player, int mode)
12386 {
12387   static unsigned int screen_frame_counter = 0;
12388
12389   if (mode == SCROLL_INIT)
12390   {
12391     /* set scrolling step size according to actual player's moving speed */
12392     ScrollStepSize = TILEX / player->move_delay_value;
12393
12394     screen_frame_counter = FrameCounter;
12395     ScreenMovDir = player->MovDir;
12396     ScreenMovPos = player->MovPos;
12397     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12398     return;
12399   }
12400   else if (!FrameReached(&screen_frame_counter, 1))
12401     return;
12402
12403   if (ScreenMovPos)
12404   {
12405     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12406     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12407     redraw_mask |= REDRAW_FIELD;
12408   }
12409   else
12410     ScreenMovDir = MV_NONE;
12411 }
12412
12413 void TestIfPlayerTouchesCustomElement(int x, int y)
12414 {
12415   static int xy[4][2] =
12416   {
12417     { 0, -1 },
12418     { -1, 0 },
12419     { +1, 0 },
12420     { 0, +1 }
12421   };
12422   static int trigger_sides[4][2] =
12423   {
12424     /* center side       border side */
12425     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12426     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12427     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12428     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12429   };
12430   static int touch_dir[4] =
12431   {
12432     MV_LEFT | MV_RIGHT,
12433     MV_UP   | MV_DOWN,
12434     MV_UP   | MV_DOWN,
12435     MV_LEFT | MV_RIGHT
12436   };
12437   int center_element = Feld[x][y];      /* should always be non-moving! */
12438   int i;
12439
12440   for (i = 0; i < NUM_DIRECTIONS; i++)
12441   {
12442     int xx = x + xy[i][0];
12443     int yy = y + xy[i][1];
12444     int center_side = trigger_sides[i][0];
12445     int border_side = trigger_sides[i][1];
12446     int border_element;
12447
12448     if (!IN_LEV_FIELD(xx, yy))
12449       continue;
12450
12451     if (IS_PLAYER(x, y))                /* player found at center element */
12452     {
12453       struct PlayerInfo *player = PLAYERINFO(x, y);
12454
12455       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12456         border_element = Feld[xx][yy];          /* may be moving! */
12457       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12458         border_element = Feld[xx][yy];
12459       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12460         border_element = MovingOrBlocked2Element(xx, yy);
12461       else
12462         continue;               /* center and border element do not touch */
12463
12464       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12465                                  player->index_bit, border_side);
12466       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12467                                           CE_PLAYER_TOUCHES_X,
12468                                           player->index_bit, border_side);
12469
12470       {
12471         /* use player element that is initially defined in the level playfield,
12472            not the player element that corresponds to the runtime player number
12473            (example: a level that contains EL_PLAYER_3 as the only player would
12474            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12475         int player_element = PLAYERINFO(x, y)->initial_element;
12476
12477         CheckElementChangeBySide(xx, yy, border_element, player_element,
12478                                  CE_TOUCHING_X, border_side);
12479       }
12480     }
12481     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12482     {
12483       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12484
12485       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12486       {
12487         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12488           continue;             /* center and border element do not touch */
12489       }
12490
12491       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12492                                  player->index_bit, center_side);
12493       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12494                                           CE_PLAYER_TOUCHES_X,
12495                                           player->index_bit, center_side);
12496
12497       {
12498         /* use player element that is initially defined in the level playfield,
12499            not the player element that corresponds to the runtime player number
12500            (example: a level that contains EL_PLAYER_3 as the only player would
12501            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12502         int player_element = PLAYERINFO(xx, yy)->initial_element;
12503
12504         CheckElementChangeBySide(x, y, center_element, player_element,
12505                                  CE_TOUCHING_X, center_side);
12506       }
12507
12508       break;
12509     }
12510   }
12511 }
12512
12513 void TestIfElementTouchesCustomElement(int x, int y)
12514 {
12515   static int xy[4][2] =
12516   {
12517     { 0, -1 },
12518     { -1, 0 },
12519     { +1, 0 },
12520     { 0, +1 }
12521   };
12522   static int trigger_sides[4][2] =
12523   {
12524     /* center side      border side */
12525     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12526     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12527     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12528     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12529   };
12530   static int touch_dir[4] =
12531   {
12532     MV_LEFT | MV_RIGHT,
12533     MV_UP   | MV_DOWN,
12534     MV_UP   | MV_DOWN,
12535     MV_LEFT | MV_RIGHT
12536   };
12537   boolean change_center_element = FALSE;
12538   int center_element = Feld[x][y];      /* should always be non-moving! */
12539   int border_element_old[NUM_DIRECTIONS];
12540   int i;
12541
12542   for (i = 0; i < NUM_DIRECTIONS; i++)
12543   {
12544     int xx = x + xy[i][0];
12545     int yy = y + xy[i][1];
12546     int border_element;
12547
12548     border_element_old[i] = -1;
12549
12550     if (!IN_LEV_FIELD(xx, yy))
12551       continue;
12552
12553     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12554       border_element = Feld[xx][yy];    /* may be moving! */
12555     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12556       border_element = Feld[xx][yy];
12557     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12558       border_element = MovingOrBlocked2Element(xx, yy);
12559     else
12560       continue;                 /* center and border element do not touch */
12561
12562     border_element_old[i] = border_element;
12563   }
12564
12565   for (i = 0; i < NUM_DIRECTIONS; i++)
12566   {
12567     int xx = x + xy[i][0];
12568     int yy = y + xy[i][1];
12569     int center_side = trigger_sides[i][0];
12570     int border_element = border_element_old[i];
12571
12572     if (border_element == -1)
12573       continue;
12574
12575     /* check for change of border element */
12576     CheckElementChangeBySide(xx, yy, border_element, center_element,
12577                              CE_TOUCHING_X, center_side);
12578
12579     /* (center element cannot be player, so we dont have to check this here) */
12580   }
12581
12582   for (i = 0; i < NUM_DIRECTIONS; i++)
12583   {
12584     int xx = x + xy[i][0];
12585     int yy = y + xy[i][1];
12586     int border_side = trigger_sides[i][1];
12587     int border_element = border_element_old[i];
12588
12589     if (border_element == -1)
12590       continue;
12591
12592     /* check for change of center element (but change it only once) */
12593     if (!change_center_element)
12594       change_center_element =
12595         CheckElementChangeBySide(x, y, center_element, border_element,
12596                                  CE_TOUCHING_X, border_side);
12597
12598     if (IS_PLAYER(xx, yy))
12599     {
12600       /* use player element that is initially defined in the level playfield,
12601          not the player element that corresponds to the runtime player number
12602          (example: a level that contains EL_PLAYER_3 as the only player would
12603          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12604       int player_element = PLAYERINFO(xx, yy)->initial_element;
12605
12606       CheckElementChangeBySide(x, y, center_element, player_element,
12607                                CE_TOUCHING_X, border_side);
12608     }
12609   }
12610 }
12611
12612 void TestIfElementHitsCustomElement(int x, int y, int direction)
12613 {
12614   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12615   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12616   int hitx = x + dx, hity = y + dy;
12617   int hitting_element = Feld[x][y];
12618   int touched_element;
12619
12620   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12621     return;
12622
12623   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12624                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12625
12626   if (IN_LEV_FIELD(hitx, hity))
12627   {
12628     int opposite_direction = MV_DIR_OPPOSITE(direction);
12629     int hitting_side = direction;
12630     int touched_side = opposite_direction;
12631     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12632                           MovDir[hitx][hity] != direction ||
12633                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12634
12635     object_hit = TRUE;
12636
12637     if (object_hit)
12638     {
12639       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12640                                CE_HITTING_X, touched_side);
12641
12642       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12643                                CE_HIT_BY_X, hitting_side);
12644
12645       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12646                                CE_HIT_BY_SOMETHING, opposite_direction);
12647
12648       if (IS_PLAYER(hitx, hity))
12649       {
12650         /* use player element that is initially defined in the level playfield,
12651            not the player element that corresponds to the runtime player number
12652            (example: a level that contains EL_PLAYER_3 as the only player would
12653            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12654         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12655
12656         CheckElementChangeBySide(x, y, hitting_element, player_element,
12657                                  CE_HITTING_X, touched_side);
12658       }
12659     }
12660   }
12661
12662   /* "hitting something" is also true when hitting the playfield border */
12663   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12664                            CE_HITTING_SOMETHING, direction);
12665 }
12666
12667 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12668 {
12669   int i, kill_x = -1, kill_y = -1;
12670
12671   int bad_element = -1;
12672   static int test_xy[4][2] =
12673   {
12674     { 0, -1 },
12675     { -1, 0 },
12676     { +1, 0 },
12677     { 0, +1 }
12678   };
12679   static int test_dir[4] =
12680   {
12681     MV_UP,
12682     MV_LEFT,
12683     MV_RIGHT,
12684     MV_DOWN
12685   };
12686
12687   for (i = 0; i < NUM_DIRECTIONS; i++)
12688   {
12689     int test_x, test_y, test_move_dir, test_element;
12690
12691     test_x = good_x + test_xy[i][0];
12692     test_y = good_y + test_xy[i][1];
12693
12694     if (!IN_LEV_FIELD(test_x, test_y))
12695       continue;
12696
12697     test_move_dir =
12698       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12699
12700     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12701
12702     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12703        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12704     */
12705     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12706         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12707     {
12708       kill_x = test_x;
12709       kill_y = test_y;
12710       bad_element = test_element;
12711
12712       break;
12713     }
12714   }
12715
12716   if (kill_x != -1 || kill_y != -1)
12717   {
12718     if (IS_PLAYER(good_x, good_y))
12719     {
12720       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12721
12722       if (player->shield_deadly_time_left > 0 &&
12723           !IS_INDESTRUCTIBLE(bad_element))
12724         Bang(kill_x, kill_y);
12725       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12726         KillPlayer(player);
12727     }
12728     else
12729       Bang(good_x, good_y);
12730   }
12731 }
12732
12733 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12734 {
12735   int i, kill_x = -1, kill_y = -1;
12736   int bad_element = Feld[bad_x][bad_y];
12737   static int test_xy[4][2] =
12738   {
12739     { 0, -1 },
12740     { -1, 0 },
12741     { +1, 0 },
12742     { 0, +1 }
12743   };
12744   static int touch_dir[4] =
12745   {
12746     MV_LEFT | MV_RIGHT,
12747     MV_UP   | MV_DOWN,
12748     MV_UP   | MV_DOWN,
12749     MV_LEFT | MV_RIGHT
12750   };
12751   static int test_dir[4] =
12752   {
12753     MV_UP,
12754     MV_LEFT,
12755     MV_RIGHT,
12756     MV_DOWN
12757   };
12758
12759   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12760     return;
12761
12762   for (i = 0; i < NUM_DIRECTIONS; i++)
12763   {
12764     int test_x, test_y, test_move_dir, test_element;
12765
12766     test_x = bad_x + test_xy[i][0];
12767     test_y = bad_y + test_xy[i][1];
12768
12769     if (!IN_LEV_FIELD(test_x, test_y))
12770       continue;
12771
12772     test_move_dir =
12773       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12774
12775     test_element = Feld[test_x][test_y];
12776
12777     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12778        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12779     */
12780     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12781         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12782     {
12783       /* good thing is player or penguin that does not move away */
12784       if (IS_PLAYER(test_x, test_y))
12785       {
12786         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12787
12788         if (bad_element == EL_ROBOT && player->is_moving)
12789           continue;     /* robot does not kill player if he is moving */
12790
12791         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12792         {
12793           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12794             continue;           /* center and border element do not touch */
12795         }
12796
12797         kill_x = test_x;
12798         kill_y = test_y;
12799
12800         break;
12801       }
12802       else if (test_element == EL_PENGUIN)
12803       {
12804         kill_x = test_x;
12805         kill_y = test_y;
12806
12807         break;
12808       }
12809     }
12810   }
12811
12812   if (kill_x != -1 || kill_y != -1)
12813   {
12814     if (IS_PLAYER(kill_x, kill_y))
12815     {
12816       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12817
12818       if (player->shield_deadly_time_left > 0 &&
12819           !IS_INDESTRUCTIBLE(bad_element))
12820         Bang(bad_x, bad_y);
12821       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12822         KillPlayer(player);
12823     }
12824     else
12825       Bang(kill_x, kill_y);
12826   }
12827 }
12828
12829 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12830 {
12831   int bad_element = Feld[bad_x][bad_y];
12832   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12833   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12834   int test_x = bad_x + dx, test_y = bad_y + dy;
12835   int test_move_dir, test_element;
12836   int kill_x = -1, kill_y = -1;
12837
12838   if (!IN_LEV_FIELD(test_x, test_y))
12839     return;
12840
12841   test_move_dir =
12842     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12843
12844   test_element = Feld[test_x][test_y];
12845
12846   if (test_move_dir != bad_move_dir)
12847   {
12848     /* good thing can be player or penguin that does not move away */
12849     if (IS_PLAYER(test_x, test_y))
12850     {
12851       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12852
12853       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12854          player as being hit when he is moving towards the bad thing, because
12855          the "get hit by" condition would be lost after the player stops) */
12856       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12857         return;         /* player moves away from bad thing */
12858
12859       kill_x = test_x;
12860       kill_y = test_y;
12861     }
12862     else if (test_element == EL_PENGUIN)
12863     {
12864       kill_x = test_x;
12865       kill_y = test_y;
12866     }
12867   }
12868
12869   if (kill_x != -1 || kill_y != -1)
12870   {
12871     if (IS_PLAYER(kill_x, kill_y))
12872     {
12873       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12874
12875       if (player->shield_deadly_time_left > 0 &&
12876           !IS_INDESTRUCTIBLE(bad_element))
12877         Bang(bad_x, bad_y);
12878       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12879         KillPlayer(player);
12880     }
12881     else
12882       Bang(kill_x, kill_y);
12883   }
12884 }
12885
12886 void TestIfPlayerTouchesBadThing(int x, int y)
12887 {
12888   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12889 }
12890
12891 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12892 {
12893   TestIfGoodThingHitsBadThing(x, y, move_dir);
12894 }
12895
12896 void TestIfBadThingTouchesPlayer(int x, int y)
12897 {
12898   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12899 }
12900
12901 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12902 {
12903   TestIfBadThingHitsGoodThing(x, y, move_dir);
12904 }
12905
12906 void TestIfFriendTouchesBadThing(int x, int y)
12907 {
12908   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12909 }
12910
12911 void TestIfBadThingTouchesFriend(int x, int y)
12912 {
12913   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12914 }
12915
12916 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12917 {
12918   int i, kill_x = bad_x, kill_y = bad_y;
12919   static int xy[4][2] =
12920   {
12921     { 0, -1 },
12922     { -1, 0 },
12923     { +1, 0 },
12924     { 0, +1 }
12925   };
12926
12927   for (i = 0; i < NUM_DIRECTIONS; i++)
12928   {
12929     int x, y, element;
12930
12931     x = bad_x + xy[i][0];
12932     y = bad_y + xy[i][1];
12933     if (!IN_LEV_FIELD(x, y))
12934       continue;
12935
12936     element = Feld[x][y];
12937     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12938         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12939     {
12940       kill_x = x;
12941       kill_y = y;
12942       break;
12943     }
12944   }
12945
12946   if (kill_x != bad_x || kill_y != bad_y)
12947     Bang(bad_x, bad_y);
12948 }
12949
12950 void KillPlayer(struct PlayerInfo *player)
12951 {
12952   int jx = player->jx, jy = player->jy;
12953
12954   if (!player->active)
12955     return;
12956
12957 #if 0
12958   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12959          player->killed, player->active, player->reanimated);
12960 #endif
12961
12962   /* the following code was introduced to prevent an infinite loop when calling
12963      -> Bang()
12964      -> CheckTriggeredElementChangeExt()
12965      -> ExecuteCustomElementAction()
12966      -> KillPlayer()
12967      -> (infinitely repeating the above sequence of function calls)
12968      which occurs when killing the player while having a CE with the setting
12969      "kill player X when explosion of <player X>"; the solution using a new
12970      field "player->killed" was chosen for backwards compatibility, although
12971      clever use of the fields "player->active" etc. would probably also work */
12972 #if 1
12973   if (player->killed)
12974     return;
12975 #endif
12976
12977   player->killed = TRUE;
12978
12979   /* remove accessible field at the player's position */
12980   Feld[jx][jy] = EL_EMPTY;
12981
12982   /* deactivate shield (else Bang()/Explode() would not work right) */
12983   player->shield_normal_time_left = 0;
12984   player->shield_deadly_time_left = 0;
12985
12986 #if 0
12987   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12988          player->killed, player->active, player->reanimated);
12989 #endif
12990
12991   Bang(jx, jy);
12992
12993 #if 0
12994   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12995          player->killed, player->active, player->reanimated);
12996 #endif
12997
12998   if (player->reanimated)       /* killed player may have been reanimated */
12999     player->killed = player->reanimated = FALSE;
13000   else
13001     BuryPlayer(player);
13002 }
13003
13004 static void KillPlayerUnlessEnemyProtected(int x, int y)
13005 {
13006   if (!PLAYER_ENEMY_PROTECTED(x, y))
13007     KillPlayer(PLAYERINFO(x, y));
13008 }
13009
13010 static void KillPlayerUnlessExplosionProtected(int x, int y)
13011 {
13012   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13013     KillPlayer(PLAYERINFO(x, y));
13014 }
13015
13016 void BuryPlayer(struct PlayerInfo *player)
13017 {
13018   int jx = player->jx, jy = player->jy;
13019
13020   if (!player->active)
13021     return;
13022
13023   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13024   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13025
13026   player->GameOver = TRUE;
13027   RemovePlayer(player);
13028 }
13029
13030 void RemovePlayer(struct PlayerInfo *player)
13031 {
13032   int jx = player->jx, jy = player->jy;
13033   int i, found = FALSE;
13034
13035   player->present = FALSE;
13036   player->active = FALSE;
13037
13038   if (!ExplodeField[jx][jy])
13039     StorePlayer[jx][jy] = 0;
13040
13041   if (player->is_moving)
13042     TEST_DrawLevelField(player->last_jx, player->last_jy);
13043
13044   for (i = 0; i < MAX_PLAYERS; i++)
13045     if (stored_player[i].active)
13046       found = TRUE;
13047
13048   if (!found)
13049     AllPlayersGone = TRUE;
13050
13051   ExitX = ZX = jx;
13052   ExitY = ZY = jy;
13053 }
13054
13055 static void setFieldForSnapping(int x, int y, int element, int direction)
13056 {
13057   struct ElementInfo *ei = &element_info[element];
13058   int direction_bit = MV_DIR_TO_BIT(direction);
13059   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13060   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13061                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13062
13063   Feld[x][y] = EL_ELEMENT_SNAPPING;
13064   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13065
13066   ResetGfxAnimation(x, y);
13067
13068   GfxElement[x][y] = element;
13069   GfxAction[x][y] = action;
13070   GfxDir[x][y] = direction;
13071   GfxFrame[x][y] = -1;
13072 }
13073
13074 /*
13075   =============================================================================
13076   checkDiagonalPushing()
13077   -----------------------------------------------------------------------------
13078   check if diagonal input device direction results in pushing of object
13079   (by checking if the alternative direction is walkable, diggable, ...)
13080   =============================================================================
13081 */
13082
13083 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13084                                     int x, int y, int real_dx, int real_dy)
13085 {
13086   int jx, jy, dx, dy, xx, yy;
13087
13088   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13089     return TRUE;
13090
13091   /* diagonal direction: check alternative direction */
13092   jx = player->jx;
13093   jy = player->jy;
13094   dx = x - jx;
13095   dy = y - jy;
13096   xx = jx + (dx == 0 ? real_dx : 0);
13097   yy = jy + (dy == 0 ? real_dy : 0);
13098
13099   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13100 }
13101
13102 /*
13103   =============================================================================
13104   DigField()
13105   -----------------------------------------------------------------------------
13106   x, y:                 field next to player (non-diagonal) to try to dig to
13107   real_dx, real_dy:     direction as read from input device (can be diagonal)
13108   =============================================================================
13109 */
13110
13111 static int DigField(struct PlayerInfo *player,
13112                     int oldx, int oldy, int x, int y,
13113                     int real_dx, int real_dy, int mode)
13114 {
13115   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13116   boolean player_was_pushing = player->is_pushing;
13117   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13118   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13119   int jx = oldx, jy = oldy;
13120   int dx = x - jx, dy = y - jy;
13121   int nextx = x + dx, nexty = y + dy;
13122   int move_direction = (dx == -1 ? MV_LEFT  :
13123                         dx == +1 ? MV_RIGHT :
13124                         dy == -1 ? MV_UP    :
13125                         dy == +1 ? MV_DOWN  : MV_NONE);
13126   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13127   int dig_side = MV_DIR_OPPOSITE(move_direction);
13128   int old_element = Feld[jx][jy];
13129   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13130   int collect_count;
13131
13132   if (is_player)                /* function can also be called by EL_PENGUIN */
13133   {
13134     if (player->MovPos == 0)
13135     {
13136       player->is_digging = FALSE;
13137       player->is_collecting = FALSE;
13138     }
13139
13140     if (player->MovPos == 0)    /* last pushing move finished */
13141       player->is_pushing = FALSE;
13142
13143     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13144     {
13145       player->is_switching = FALSE;
13146       player->push_delay = -1;
13147
13148       return MP_NO_ACTION;
13149     }
13150   }
13151
13152   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13153     old_element = Back[jx][jy];
13154
13155   /* in case of element dropped at player position, check background */
13156   else if (Back[jx][jy] != EL_EMPTY &&
13157            game.engine_version >= VERSION_IDENT(2,2,0,0))
13158     old_element = Back[jx][jy];
13159
13160   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13161     return MP_NO_ACTION;        /* field has no opening in this direction */
13162
13163   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13164     return MP_NO_ACTION;        /* field has no opening in this direction */
13165
13166   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13167   {
13168     SplashAcid(x, y);
13169
13170     Feld[jx][jy] = player->artwork_element;
13171     InitMovingField(jx, jy, MV_DOWN);
13172     Store[jx][jy] = EL_ACID;
13173     ContinueMoving(jx, jy);
13174     BuryPlayer(player);
13175
13176     return MP_DONT_RUN_INTO;
13177   }
13178
13179   if (player_can_move && DONT_RUN_INTO(element))
13180   {
13181     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13182
13183     return MP_DONT_RUN_INTO;
13184   }
13185
13186   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13187     return MP_NO_ACTION;
13188
13189   collect_count = element_info[element].collect_count_initial;
13190
13191   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13192     return MP_NO_ACTION;
13193
13194   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13195     player_can_move = player_can_move_or_snap;
13196
13197   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13198       game.engine_version >= VERSION_IDENT(2,2,0,0))
13199   {
13200     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13201                                player->index_bit, dig_side);
13202     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13203                                         player->index_bit, dig_side);
13204
13205     if (element == EL_DC_LANDMINE)
13206       Bang(x, y);
13207
13208     if (Feld[x][y] != element)          /* field changed by snapping */
13209       return MP_ACTION;
13210
13211     return MP_NO_ACTION;
13212   }
13213
13214   if (player->gravity && is_player && !player->is_auto_moving &&
13215       canFallDown(player) && move_direction != MV_DOWN &&
13216       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13217     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13218
13219   if (player_can_move &&
13220       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13221   {
13222     int sound_element = SND_ELEMENT(element);
13223     int sound_action = ACTION_WALKING;
13224
13225     if (IS_RND_GATE(element))
13226     {
13227       if (!player->key[RND_GATE_NR(element)])
13228         return MP_NO_ACTION;
13229     }
13230     else if (IS_RND_GATE_GRAY(element))
13231     {
13232       if (!player->key[RND_GATE_GRAY_NR(element)])
13233         return MP_NO_ACTION;
13234     }
13235     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13236     {
13237       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13238         return MP_NO_ACTION;
13239     }
13240     else if (element == EL_EXIT_OPEN ||
13241              element == EL_EM_EXIT_OPEN ||
13242              element == EL_EM_EXIT_OPENING ||
13243              element == EL_STEEL_EXIT_OPEN ||
13244              element == EL_EM_STEEL_EXIT_OPEN ||
13245              element == EL_EM_STEEL_EXIT_OPENING ||
13246              element == EL_SP_EXIT_OPEN ||
13247              element == EL_SP_EXIT_OPENING)
13248     {
13249       sound_action = ACTION_PASSING;    /* player is passing exit */
13250     }
13251     else if (element == EL_EMPTY)
13252     {
13253       sound_action = ACTION_MOVING;             /* nothing to walk on */
13254     }
13255
13256     /* play sound from background or player, whatever is available */
13257     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13258       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13259     else
13260       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13261   }
13262   else if (player_can_move &&
13263            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13264   {
13265     if (!ACCESS_FROM(element, opposite_direction))
13266       return MP_NO_ACTION;      /* field not accessible from this direction */
13267
13268     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13269       return MP_NO_ACTION;
13270
13271     if (IS_EM_GATE(element))
13272     {
13273       if (!player->key[EM_GATE_NR(element)])
13274         return MP_NO_ACTION;
13275     }
13276     else if (IS_EM_GATE_GRAY(element))
13277     {
13278       if (!player->key[EM_GATE_GRAY_NR(element)])
13279         return MP_NO_ACTION;
13280     }
13281     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13282     {
13283       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13284         return MP_NO_ACTION;
13285     }
13286     else if (IS_EMC_GATE(element))
13287     {
13288       if (!player->key[EMC_GATE_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (IS_EMC_GATE_GRAY(element))
13292     {
13293       if (!player->key[EMC_GATE_GRAY_NR(element)])
13294         return MP_NO_ACTION;
13295     }
13296     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13297     {
13298       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13299         return MP_NO_ACTION;
13300     }
13301     else if (element == EL_DC_GATE_WHITE ||
13302              element == EL_DC_GATE_WHITE_GRAY ||
13303              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13304     {
13305       if (player->num_white_keys == 0)
13306         return MP_NO_ACTION;
13307
13308       player->num_white_keys--;
13309     }
13310     else if (IS_SP_PORT(element))
13311     {
13312       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13313           element == EL_SP_GRAVITY_PORT_RIGHT ||
13314           element == EL_SP_GRAVITY_PORT_UP ||
13315           element == EL_SP_GRAVITY_PORT_DOWN)
13316         player->gravity = !player->gravity;
13317       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13318                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13319                element == EL_SP_GRAVITY_ON_PORT_UP ||
13320                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13321         player->gravity = TRUE;
13322       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13323                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13324                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13325                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13326         player->gravity = FALSE;
13327     }
13328
13329     /* automatically move to the next field with double speed */
13330     player->programmed_action = move_direction;
13331
13332     if (player->move_delay_reset_counter == 0)
13333     {
13334       player->move_delay_reset_counter = 2;     /* two double speed steps */
13335
13336       DOUBLE_PLAYER_SPEED(player);
13337     }
13338
13339     PlayLevelSoundAction(x, y, ACTION_PASSING);
13340   }
13341   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13342   {
13343     RemoveField(x, y);
13344
13345     if (mode != DF_SNAP)
13346     {
13347       GfxElement[x][y] = GFX_ELEMENT(element);
13348       player->is_digging = TRUE;
13349     }
13350
13351     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13352
13353     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13354                                         player->index_bit, dig_side);
13355
13356     if (mode == DF_SNAP)
13357     {
13358       if (level.block_snap_field)
13359         setFieldForSnapping(x, y, element, move_direction);
13360       else
13361         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13362
13363       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13364                                           player->index_bit, dig_side);
13365     }
13366   }
13367   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13368   {
13369     RemoveField(x, y);
13370
13371     if (is_player && mode != DF_SNAP)
13372     {
13373       GfxElement[x][y] = element;
13374       player->is_collecting = TRUE;
13375     }
13376
13377     if (element == EL_SPEED_PILL)
13378     {
13379       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13380     }
13381     else if (element == EL_EXTRA_TIME && level.time > 0)
13382     {
13383       TimeLeft += level.extra_time;
13384
13385       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13386
13387       DisplayGameControlValues();
13388     }
13389     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13390     {
13391       player->shield_normal_time_left += level.shield_normal_time;
13392       if (element == EL_SHIELD_DEADLY)
13393         player->shield_deadly_time_left += level.shield_deadly_time;
13394     }
13395     else if (element == EL_DYNAMITE ||
13396              element == EL_EM_DYNAMITE ||
13397              element == EL_SP_DISK_RED)
13398     {
13399       if (player->inventory_size < MAX_INVENTORY_SIZE)
13400         player->inventory_element[player->inventory_size++] = element;
13401
13402       DrawGameDoorValues();
13403     }
13404     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13405     {
13406       player->dynabomb_count++;
13407       player->dynabombs_left++;
13408     }
13409     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13410     {
13411       player->dynabomb_size++;
13412     }
13413     else if (element == EL_DYNABOMB_INCREASE_POWER)
13414     {
13415       player->dynabomb_xl = TRUE;
13416     }
13417     else if (IS_KEY(element))
13418     {
13419       player->key[KEY_NR(element)] = TRUE;
13420
13421       DrawGameDoorValues();
13422     }
13423     else if (element == EL_DC_KEY_WHITE)
13424     {
13425       player->num_white_keys++;
13426
13427       /* display white keys? */
13428       /* DrawGameDoorValues(); */
13429     }
13430     else if (IS_ENVELOPE(element))
13431     {
13432       player->show_envelope = element;
13433     }
13434     else if (element == EL_EMC_LENSES)
13435     {
13436       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13437
13438       RedrawAllInvisibleElementsForLenses();
13439     }
13440     else if (element == EL_EMC_MAGNIFIER)
13441     {
13442       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13443
13444       RedrawAllInvisibleElementsForMagnifier();
13445     }
13446     else if (IS_DROPPABLE(element) ||
13447              IS_THROWABLE(element))     /* can be collected and dropped */
13448     {
13449       int i;
13450
13451       if (collect_count == 0)
13452         player->inventory_infinite_element = element;
13453       else
13454         for (i = 0; i < collect_count; i++)
13455           if (player->inventory_size < MAX_INVENTORY_SIZE)
13456             player->inventory_element[player->inventory_size++] = element;
13457
13458       DrawGameDoorValues();
13459     }
13460     else if (collect_count > 0)
13461     {
13462       local_player->gems_still_needed -= collect_count;
13463       if (local_player->gems_still_needed < 0)
13464         local_player->gems_still_needed = 0;
13465
13466       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13467
13468       DisplayGameControlValues();
13469     }
13470
13471     RaiseScoreElement(element);
13472     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13473
13474     if (is_player)
13475       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13476                                           player->index_bit, dig_side);
13477
13478     if (mode == DF_SNAP)
13479     {
13480       if (level.block_snap_field)
13481         setFieldForSnapping(x, y, element, move_direction);
13482       else
13483         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13484
13485       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13486                                           player->index_bit, dig_side);
13487     }
13488   }
13489   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13490   {
13491     if (mode == DF_SNAP && element != EL_BD_ROCK)
13492       return MP_NO_ACTION;
13493
13494     if (CAN_FALL(element) && dy)
13495       return MP_NO_ACTION;
13496
13497     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13498         !(element == EL_SPRING && level.use_spring_bug))
13499       return MP_NO_ACTION;
13500
13501     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13502         ((move_direction & MV_VERTICAL &&
13503           ((element_info[element].move_pattern & MV_LEFT &&
13504             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13505            (element_info[element].move_pattern & MV_RIGHT &&
13506             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13507          (move_direction & MV_HORIZONTAL &&
13508           ((element_info[element].move_pattern & MV_UP &&
13509             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13510            (element_info[element].move_pattern & MV_DOWN &&
13511             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13512       return MP_NO_ACTION;
13513
13514     /* do not push elements already moving away faster than player */
13515     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13516         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13517       return MP_NO_ACTION;
13518
13519     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13520     {
13521       if (player->push_delay_value == -1 || !player_was_pushing)
13522         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13523     }
13524     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13525     {
13526       if (player->push_delay_value == -1)
13527         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13528     }
13529     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13530     {
13531       if (!player->is_pushing)
13532         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13533     }
13534
13535     player->is_pushing = TRUE;
13536     player->is_active = TRUE;
13537
13538     if (!(IN_LEV_FIELD(nextx, nexty) &&
13539           (IS_FREE(nextx, nexty) ||
13540            (IS_SB_ELEMENT(element) &&
13541             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13542            (IS_CUSTOM_ELEMENT(element) &&
13543             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13544       return MP_NO_ACTION;
13545
13546     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13547       return MP_NO_ACTION;
13548
13549     if (player->push_delay == -1)       /* new pushing; restart delay */
13550       player->push_delay = 0;
13551
13552     if (player->push_delay < player->push_delay_value &&
13553         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13554         element != EL_SPRING && element != EL_BALLOON)
13555     {
13556       /* make sure that there is no move delay before next try to push */
13557       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13558         player->move_delay = 0;
13559
13560       return MP_NO_ACTION;
13561     }
13562
13563     if (IS_CUSTOM_ELEMENT(element) &&
13564         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13565     {
13566       if (!DigFieldByCE(nextx, nexty, element))
13567         return MP_NO_ACTION;
13568     }
13569
13570     if (IS_SB_ELEMENT(element))
13571     {
13572       if (element == EL_SOKOBAN_FIELD_FULL)
13573       {
13574         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13575         local_player->sokobanfields_still_needed++;
13576       }
13577
13578       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13579       {
13580         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13581         local_player->sokobanfields_still_needed--;
13582       }
13583
13584       Feld[x][y] = EL_SOKOBAN_OBJECT;
13585
13586       if (Back[x][y] == Back[nextx][nexty])
13587         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13588       else if (Back[x][y] != 0)
13589         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13590                                     ACTION_EMPTYING);
13591       else
13592         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13593                                     ACTION_FILLING);
13594
13595       if (local_player->sokobanfields_still_needed == 0 &&
13596           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13597       {
13598         PlayerWins(player);
13599
13600         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13601       }
13602     }
13603     else
13604       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13605
13606     InitMovingField(x, y, move_direction);
13607     GfxAction[x][y] = ACTION_PUSHING;
13608
13609     if (mode == DF_SNAP)
13610       ContinueMoving(x, y);
13611     else
13612       MovPos[x][y] = (dx != 0 ? dx : dy);
13613
13614     Pushed[x][y] = TRUE;
13615     Pushed[nextx][nexty] = TRUE;
13616
13617     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13618       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13619     else
13620       player->push_delay_value = -1;    /* get new value later */
13621
13622     /* check for element change _after_ element has been pushed */
13623     if (game.use_change_when_pushing_bug)
13624     {
13625       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13626                                  player->index_bit, dig_side);
13627       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13628                                           player->index_bit, dig_side);
13629     }
13630   }
13631   else if (IS_SWITCHABLE(element))
13632   {
13633     if (PLAYER_SWITCHING(player, x, y))
13634     {
13635       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13636                                           player->index_bit, dig_side);
13637
13638       return MP_ACTION;
13639     }
13640
13641     player->is_switching = TRUE;
13642     player->switch_x = x;
13643     player->switch_y = y;
13644
13645     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13646
13647     if (element == EL_ROBOT_WHEEL)
13648     {
13649       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13650       ZX = x;
13651       ZY = y;
13652
13653       game.robot_wheel_active = TRUE;
13654
13655       TEST_DrawLevelField(x, y);
13656     }
13657     else if (element == EL_SP_TERMINAL)
13658     {
13659       int xx, yy;
13660
13661       SCAN_PLAYFIELD(xx, yy)
13662       {
13663         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13664           Bang(xx, yy);
13665         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13666           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13667       }
13668     }
13669     else if (IS_BELT_SWITCH(element))
13670     {
13671       ToggleBeltSwitch(x, y);
13672     }
13673     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13674              element == EL_SWITCHGATE_SWITCH_DOWN ||
13675              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13676              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13677     {
13678       ToggleSwitchgateSwitch(x, y);
13679     }
13680     else if (element == EL_LIGHT_SWITCH ||
13681              element == EL_LIGHT_SWITCH_ACTIVE)
13682     {
13683       ToggleLightSwitch(x, y);
13684     }
13685     else if (element == EL_TIMEGATE_SWITCH ||
13686              element == EL_DC_TIMEGATE_SWITCH)
13687     {
13688       ActivateTimegateSwitch(x, y);
13689     }
13690     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13691              element == EL_BALLOON_SWITCH_RIGHT ||
13692              element == EL_BALLOON_SWITCH_UP    ||
13693              element == EL_BALLOON_SWITCH_DOWN  ||
13694              element == EL_BALLOON_SWITCH_NONE  ||
13695              element == EL_BALLOON_SWITCH_ANY)
13696     {
13697       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13698                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13699                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13700                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13701                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13702                              move_direction);
13703     }
13704     else if (element == EL_LAMP)
13705     {
13706       Feld[x][y] = EL_LAMP_ACTIVE;
13707       local_player->lights_still_needed--;
13708
13709       ResetGfxAnimation(x, y);
13710       TEST_DrawLevelField(x, y);
13711     }
13712     else if (element == EL_TIME_ORB_FULL)
13713     {
13714       Feld[x][y] = EL_TIME_ORB_EMPTY;
13715
13716       if (level.time > 0 || level.use_time_orb_bug)
13717       {
13718         TimeLeft += level.time_orb_time;
13719         game.no_time_limit = FALSE;
13720
13721         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13722
13723         DisplayGameControlValues();
13724       }
13725
13726       ResetGfxAnimation(x, y);
13727       TEST_DrawLevelField(x, y);
13728     }
13729     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13730              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13731     {
13732       int xx, yy;
13733
13734       game.ball_state = !game.ball_state;
13735
13736       SCAN_PLAYFIELD(xx, yy)
13737       {
13738         int e = Feld[xx][yy];
13739
13740         if (game.ball_state)
13741         {
13742           if (e == EL_EMC_MAGIC_BALL)
13743             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13744           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13745             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13746         }
13747         else
13748         {
13749           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13750             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13751           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13752             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13753         }
13754       }
13755     }
13756
13757     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13758                                         player->index_bit, dig_side);
13759
13760     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13761                                         player->index_bit, dig_side);
13762
13763     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13764                                         player->index_bit, dig_side);
13765
13766     return MP_ACTION;
13767   }
13768   else
13769   {
13770     if (!PLAYER_SWITCHING(player, x, y))
13771     {
13772       player->is_switching = TRUE;
13773       player->switch_x = x;
13774       player->switch_y = y;
13775
13776       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13777                                  player->index_bit, dig_side);
13778       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13779                                           player->index_bit, dig_side);
13780
13781       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13782                                  player->index_bit, dig_side);
13783       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13784                                           player->index_bit, dig_side);
13785     }
13786
13787     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13788                                player->index_bit, dig_side);
13789     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13790                                         player->index_bit, dig_side);
13791
13792     return MP_NO_ACTION;
13793   }
13794
13795   player->push_delay = -1;
13796
13797   if (is_player)                /* function can also be called by EL_PENGUIN */
13798   {
13799     if (Feld[x][y] != element)          /* really digged/collected something */
13800     {
13801       player->is_collecting = !player->is_digging;
13802       player->is_active = TRUE;
13803     }
13804   }
13805
13806   return MP_MOVING;
13807 }
13808
13809 static boolean DigFieldByCE(int x, int y, int digging_element)
13810 {
13811   int element = Feld[x][y];
13812
13813   if (!IS_FREE(x, y))
13814   {
13815     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13816                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13817                   ACTION_BREAKING);
13818
13819     /* no element can dig solid indestructible elements */
13820     if (IS_INDESTRUCTIBLE(element) &&
13821         !IS_DIGGABLE(element) &&
13822         !IS_COLLECTIBLE(element))
13823       return FALSE;
13824
13825     if (AmoebaNr[x][y] &&
13826         (element == EL_AMOEBA_FULL ||
13827          element == EL_BD_AMOEBA ||
13828          element == EL_AMOEBA_GROWING))
13829     {
13830       AmoebaCnt[AmoebaNr[x][y]]--;
13831       AmoebaCnt2[AmoebaNr[x][y]]--;
13832     }
13833
13834     if (IS_MOVING(x, y))
13835       RemoveMovingField(x, y);
13836     else
13837     {
13838       RemoveField(x, y);
13839       TEST_DrawLevelField(x, y);
13840     }
13841
13842     /* if digged element was about to explode, prevent the explosion */
13843     ExplodeField[x][y] = EX_TYPE_NONE;
13844
13845     PlayLevelSoundAction(x, y, action);
13846   }
13847
13848   Store[x][y] = EL_EMPTY;
13849
13850   /* this makes it possible to leave the removed element again */
13851   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13852     Store[x][y] = element;
13853
13854   return TRUE;
13855 }
13856
13857 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13858 {
13859   int jx = player->jx, jy = player->jy;
13860   int x = jx + dx, y = jy + dy;
13861   int snap_direction = (dx == -1 ? MV_LEFT  :
13862                         dx == +1 ? MV_RIGHT :
13863                         dy == -1 ? MV_UP    :
13864                         dy == +1 ? MV_DOWN  : MV_NONE);
13865   boolean can_continue_snapping = (level.continuous_snapping &&
13866                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13867
13868   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13869     return FALSE;
13870
13871   if (!player->active || !IN_LEV_FIELD(x, y))
13872     return FALSE;
13873
13874   if (dx && dy)
13875     return FALSE;
13876
13877   if (!dx && !dy)
13878   {
13879     if (player->MovPos == 0)
13880       player->is_pushing = FALSE;
13881
13882     player->is_snapping = FALSE;
13883
13884     if (player->MovPos == 0)
13885     {
13886       player->is_moving = FALSE;
13887       player->is_digging = FALSE;
13888       player->is_collecting = FALSE;
13889     }
13890
13891     return FALSE;
13892   }
13893
13894   /* prevent snapping with already pressed snap key when not allowed */
13895   if (player->is_snapping && !can_continue_snapping)
13896     return FALSE;
13897
13898   player->MovDir = snap_direction;
13899
13900   if (player->MovPos == 0)
13901   {
13902     player->is_moving = FALSE;
13903     player->is_digging = FALSE;
13904     player->is_collecting = FALSE;
13905   }
13906
13907   player->is_dropping = FALSE;
13908   player->is_dropping_pressed = FALSE;
13909   player->drop_pressed_delay = 0;
13910
13911   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13912     return FALSE;
13913
13914   player->is_snapping = TRUE;
13915   player->is_active = TRUE;
13916
13917   if (player->MovPos == 0)
13918   {
13919     player->is_moving = FALSE;
13920     player->is_digging = FALSE;
13921     player->is_collecting = FALSE;
13922   }
13923
13924   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13925     TEST_DrawLevelField(player->last_jx, player->last_jy);
13926
13927   TEST_DrawLevelField(x, y);
13928
13929   return TRUE;
13930 }
13931
13932 static boolean DropElement(struct PlayerInfo *player)
13933 {
13934   int old_element, new_element;
13935   int dropx = player->jx, dropy = player->jy;
13936   int drop_direction = player->MovDir;
13937   int drop_side = drop_direction;
13938   int drop_element = get_next_dropped_element(player);
13939
13940   player->is_dropping_pressed = TRUE;
13941
13942   /* do not drop an element on top of another element; when holding drop key
13943      pressed without moving, dropped element must move away before the next
13944      element can be dropped (this is especially important if the next element
13945      is dynamite, which can be placed on background for historical reasons) */
13946   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13947     return MP_ACTION;
13948
13949   if (IS_THROWABLE(drop_element))
13950   {
13951     dropx += GET_DX_FROM_DIR(drop_direction);
13952     dropy += GET_DY_FROM_DIR(drop_direction);
13953
13954     if (!IN_LEV_FIELD(dropx, dropy))
13955       return FALSE;
13956   }
13957
13958   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13959   new_element = drop_element;           /* default: no change when dropping */
13960
13961   /* check if player is active, not moving and ready to drop */
13962   if (!player->active || player->MovPos || player->drop_delay > 0)
13963     return FALSE;
13964
13965   /* check if player has anything that can be dropped */
13966   if (new_element == EL_UNDEFINED)
13967     return FALSE;
13968
13969   /* check if drop key was pressed long enough for EM style dynamite */
13970   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13971     return FALSE;
13972
13973   /* check if anything can be dropped at the current position */
13974   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13975     return FALSE;
13976
13977   /* collected custom elements can only be dropped on empty fields */
13978   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13979     return FALSE;
13980
13981   if (old_element != EL_EMPTY)
13982     Back[dropx][dropy] = old_element;   /* store old element on this field */
13983
13984   ResetGfxAnimation(dropx, dropy);
13985   ResetRandomAnimationValue(dropx, dropy);
13986
13987   if (player->inventory_size > 0 ||
13988       player->inventory_infinite_element != EL_UNDEFINED)
13989   {
13990     if (player->inventory_size > 0)
13991     {
13992       player->inventory_size--;
13993
13994       DrawGameDoorValues();
13995
13996       if (new_element == EL_DYNAMITE)
13997         new_element = EL_DYNAMITE_ACTIVE;
13998       else if (new_element == EL_EM_DYNAMITE)
13999         new_element = EL_EM_DYNAMITE_ACTIVE;
14000       else if (new_element == EL_SP_DISK_RED)
14001         new_element = EL_SP_DISK_RED_ACTIVE;
14002     }
14003
14004     Feld[dropx][dropy] = new_element;
14005
14006     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14007       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14008                           el2img(Feld[dropx][dropy]), 0);
14009
14010     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14011
14012     /* needed if previous element just changed to "empty" in the last frame */
14013     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14014
14015     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14016                                player->index_bit, drop_side);
14017     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14018                                         CE_PLAYER_DROPS_X,
14019                                         player->index_bit, drop_side);
14020
14021     TestIfElementTouchesCustomElement(dropx, dropy);
14022   }
14023   else          /* player is dropping a dyna bomb */
14024   {
14025     player->dynabombs_left--;
14026
14027     Feld[dropx][dropy] = new_element;
14028
14029     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14030       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14031                           el2img(Feld[dropx][dropy]), 0);
14032
14033     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14034   }
14035
14036   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14037     InitField_WithBug1(dropx, dropy, FALSE);
14038
14039   new_element = Feld[dropx][dropy];     /* element might have changed */
14040
14041   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14042       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14043   {
14044     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14045       MovDir[dropx][dropy] = drop_direction;
14046
14047     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14048
14049     /* do not cause impact style collision by dropping elements that can fall */
14050     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14051   }
14052
14053   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14054   player->is_dropping = TRUE;
14055
14056   player->drop_pressed_delay = 0;
14057   player->is_dropping_pressed = FALSE;
14058
14059   player->drop_x = dropx;
14060   player->drop_y = dropy;
14061
14062   return TRUE;
14063 }
14064
14065 /* ------------------------------------------------------------------------- */
14066 /* game sound playing functions                                              */
14067 /* ------------------------------------------------------------------------- */
14068
14069 static int *loop_sound_frame = NULL;
14070 static int *loop_sound_volume = NULL;
14071
14072 void InitPlayLevelSound()
14073 {
14074   int num_sounds = getSoundListSize();
14075
14076   checked_free(loop_sound_frame);
14077   checked_free(loop_sound_volume);
14078
14079   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14080   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14081 }
14082
14083 static void PlayLevelSound(int x, int y, int nr)
14084 {
14085   int sx = SCREENX(x), sy = SCREENY(y);
14086   int volume, stereo_position;
14087   int max_distance = 8;
14088   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14089
14090   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14091       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14092     return;
14093
14094   if (!IN_LEV_FIELD(x, y) ||
14095       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14096       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14097     return;
14098
14099   volume = SOUND_MAX_VOLUME;
14100
14101   if (!IN_SCR_FIELD(sx, sy))
14102   {
14103     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14104     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14105
14106     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14107   }
14108
14109   stereo_position = (SOUND_MAX_LEFT +
14110                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14111                      (SCR_FIELDX + 2 * max_distance));
14112
14113   if (IS_LOOP_SOUND(nr))
14114   {
14115     /* This assures that quieter loop sounds do not overwrite louder ones,
14116        while restarting sound volume comparison with each new game frame. */
14117
14118     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14119       return;
14120
14121     loop_sound_volume[nr] = volume;
14122     loop_sound_frame[nr] = FrameCounter;
14123   }
14124
14125   PlaySoundExt(nr, volume, stereo_position, type);
14126 }
14127
14128 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14129 {
14130   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14131                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14132                  y < LEVELY(BY1) ? LEVELY(BY1) :
14133                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14134                  sound_action);
14135 }
14136
14137 static void PlayLevelSoundAction(int x, int y, int action)
14138 {
14139   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14140 }
14141
14142 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14143 {
14144   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14145
14146   if (sound_effect != SND_UNDEFINED)
14147     PlayLevelSound(x, y, sound_effect);
14148 }
14149
14150 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14151                                               int action)
14152 {
14153   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14154
14155   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14156     PlayLevelSound(x, y, sound_effect);
14157 }
14158
14159 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14160 {
14161   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14162
14163   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14164     PlayLevelSound(x, y, sound_effect);
14165 }
14166
14167 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14168 {
14169   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14170
14171   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14172     StopSound(sound_effect);
14173 }
14174
14175 static void PlayLevelMusic()
14176 {
14177   if (levelset.music[level_nr] != MUS_UNDEFINED)
14178     PlayMusic(levelset.music[level_nr]);        /* from config file */
14179   else
14180     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14181 }
14182
14183 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14184 {
14185   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14186   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14187   int x = xx - 1 - offset;
14188   int y = yy - 1 - offset;
14189
14190   switch (sample)
14191   {
14192     case SAMPLE_blank:
14193       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14194       break;
14195
14196     case SAMPLE_roll:
14197       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14198       break;
14199
14200     case SAMPLE_stone:
14201       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14202       break;
14203
14204     case SAMPLE_nut:
14205       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14206       break;
14207
14208     case SAMPLE_crack:
14209       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14210       break;
14211
14212     case SAMPLE_bug:
14213       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14214       break;
14215
14216     case SAMPLE_tank:
14217       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14218       break;
14219
14220     case SAMPLE_android_clone:
14221       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14222       break;
14223
14224     case SAMPLE_android_move:
14225       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14226       break;
14227
14228     case SAMPLE_spring:
14229       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14230       break;
14231
14232     case SAMPLE_slurp:
14233       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14234       break;
14235
14236     case SAMPLE_eater:
14237       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14238       break;
14239
14240     case SAMPLE_eater_eat:
14241       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14242       break;
14243
14244     case SAMPLE_alien:
14245       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14246       break;
14247
14248     case SAMPLE_collect:
14249       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14250       break;
14251
14252     case SAMPLE_diamond:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14254       break;
14255
14256     case SAMPLE_squash:
14257       /* !!! CHECK THIS !!! */
14258 #if 1
14259       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14260 #else
14261       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14262 #endif
14263       break;
14264
14265     case SAMPLE_wonderfall:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14267       break;
14268
14269     case SAMPLE_drip:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14271       break;
14272
14273     case SAMPLE_push:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14275       break;
14276
14277     case SAMPLE_dirt:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14279       break;
14280
14281     case SAMPLE_acid:
14282       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14283       break;
14284
14285     case SAMPLE_ball:
14286       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14287       break;
14288
14289     case SAMPLE_grow:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14291       break;
14292
14293     case SAMPLE_wonder:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14295       break;
14296
14297     case SAMPLE_door:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14299       break;
14300
14301     case SAMPLE_exit_open:
14302       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14303       break;
14304
14305     case SAMPLE_exit_leave:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14307       break;
14308
14309     case SAMPLE_dynamite:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14311       break;
14312
14313     case SAMPLE_tick:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14315       break;
14316
14317     case SAMPLE_press:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14319       break;
14320
14321     case SAMPLE_wheel:
14322       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14323       break;
14324
14325     case SAMPLE_boom:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14327       break;
14328
14329     case SAMPLE_die:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14331       break;
14332
14333     case SAMPLE_time:
14334       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14335       break;
14336
14337     default:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14339       break;
14340   }
14341 }
14342
14343 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14344 {
14345   int element = map_element_SP_to_RND(element_sp);
14346   int action = map_action_SP_to_RND(action_sp);
14347   int offset = (setup.sp_show_border_elements ? 0 : 1);
14348   int x = xx - offset;
14349   int y = yy - offset;
14350
14351   PlayLevelSoundElementAction(x, y, element, action);
14352 }
14353
14354 void RaiseScore(int value)
14355 {
14356   local_player->score += value;
14357
14358   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14359
14360   DisplayGameControlValues();
14361 }
14362
14363 void RaiseScoreElement(int element)
14364 {
14365   switch (element)
14366   {
14367     case EL_EMERALD:
14368     case EL_BD_DIAMOND:
14369     case EL_EMERALD_YELLOW:
14370     case EL_EMERALD_RED:
14371     case EL_EMERALD_PURPLE:
14372     case EL_SP_INFOTRON:
14373       RaiseScore(level.score[SC_EMERALD]);
14374       break;
14375     case EL_DIAMOND:
14376       RaiseScore(level.score[SC_DIAMOND]);
14377       break;
14378     case EL_CRYSTAL:
14379       RaiseScore(level.score[SC_CRYSTAL]);
14380       break;
14381     case EL_PEARL:
14382       RaiseScore(level.score[SC_PEARL]);
14383       break;
14384     case EL_BUG:
14385     case EL_BD_BUTTERFLY:
14386     case EL_SP_ELECTRON:
14387       RaiseScore(level.score[SC_BUG]);
14388       break;
14389     case EL_SPACESHIP:
14390     case EL_BD_FIREFLY:
14391     case EL_SP_SNIKSNAK:
14392       RaiseScore(level.score[SC_SPACESHIP]);
14393       break;
14394     case EL_YAMYAM:
14395     case EL_DARK_YAMYAM:
14396       RaiseScore(level.score[SC_YAMYAM]);
14397       break;
14398     case EL_ROBOT:
14399       RaiseScore(level.score[SC_ROBOT]);
14400       break;
14401     case EL_PACMAN:
14402       RaiseScore(level.score[SC_PACMAN]);
14403       break;
14404     case EL_NUT:
14405       RaiseScore(level.score[SC_NUT]);
14406       break;
14407     case EL_DYNAMITE:
14408     case EL_EM_DYNAMITE:
14409     case EL_SP_DISK_RED:
14410     case EL_DYNABOMB_INCREASE_NUMBER:
14411     case EL_DYNABOMB_INCREASE_SIZE:
14412     case EL_DYNABOMB_INCREASE_POWER:
14413       RaiseScore(level.score[SC_DYNAMITE]);
14414       break;
14415     case EL_SHIELD_NORMAL:
14416     case EL_SHIELD_DEADLY:
14417       RaiseScore(level.score[SC_SHIELD]);
14418       break;
14419     case EL_EXTRA_TIME:
14420       RaiseScore(level.extra_time_score);
14421       break;
14422     case EL_KEY_1:
14423     case EL_KEY_2:
14424     case EL_KEY_3:
14425     case EL_KEY_4:
14426     case EL_EM_KEY_1:
14427     case EL_EM_KEY_2:
14428     case EL_EM_KEY_3:
14429     case EL_EM_KEY_4:
14430     case EL_EMC_KEY_5:
14431     case EL_EMC_KEY_6:
14432     case EL_EMC_KEY_7:
14433     case EL_EMC_KEY_8:
14434     case EL_DC_KEY_WHITE:
14435       RaiseScore(level.score[SC_KEY]);
14436       break;
14437     default:
14438       RaiseScore(element_info[element].collect_score);
14439       break;
14440   }
14441 }
14442
14443 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14444 {
14445   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14446   {
14447     /* closing door required in case of envelope style request dialogs */
14448     if (!skip_request)
14449       CloseDoor(DOOR_CLOSE_1);
14450
14451 #if defined(NETWORK_AVALIABLE)
14452     if (options.network)
14453       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14454     else
14455 #endif
14456     {
14457       if (quick_quit)
14458       {
14459         FadeSkipNextFadeIn();
14460
14461         game_status = GAME_MODE_MAIN;
14462
14463         DrawAndFadeInMainMenu(REDRAW_FIELD);
14464       }
14465       else
14466       {
14467         game_status = GAME_MODE_MAIN;
14468
14469         DrawAndFadeInMainMenu(REDRAW_FIELD);
14470       }
14471     }
14472   }
14473   else          /* continue playing the game */
14474   {
14475     if (tape.playing && tape.deactivate_display)
14476       TapeDeactivateDisplayOff(TRUE);
14477
14478     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14479
14480     if (tape.playing && tape.deactivate_display)
14481       TapeDeactivateDisplayOn();
14482   }
14483 }
14484
14485 void RequestQuitGame(boolean ask_if_really_quit)
14486 {
14487   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14488   boolean skip_request = AllPlayersGone || quick_quit;
14489
14490   RequestQuitGameExt(skip_request, quick_quit,
14491                      "Do you really want to quit the game?");
14492 }
14493
14494
14495 /* ------------------------------------------------------------------------- */
14496 /* random generator functions                                                */
14497 /* ------------------------------------------------------------------------- */
14498
14499 unsigned int InitEngineRandom_RND(int seed)
14500 {
14501   game.num_random_calls = 0;
14502
14503   return InitEngineRandom(seed);
14504 }
14505
14506 unsigned int RND(int max)
14507 {
14508   if (max > 0)
14509   {
14510     game.num_random_calls++;
14511
14512     return GetEngineRandom(max);
14513   }
14514
14515   return 0;
14516 }
14517
14518
14519 /* ------------------------------------------------------------------------- */
14520 /* game engine snapshot handling functions                                   */
14521 /* ------------------------------------------------------------------------- */
14522
14523 struct EngineSnapshotInfo
14524 {
14525   /* runtime values for custom element collect score */
14526   int collect_score[NUM_CUSTOM_ELEMENTS];
14527
14528   /* runtime values for group element choice position */
14529   int choice_pos[NUM_GROUP_ELEMENTS];
14530
14531   /* runtime values for belt position animations */
14532   int belt_graphic[4][NUM_BELT_PARTS];
14533   int belt_anim_mode[4][NUM_BELT_PARTS];
14534 };
14535
14536 static struct EngineSnapshotInfo engine_snapshot_rnd;
14537 static char *snapshot_level_identifier = NULL;
14538 static int snapshot_level_nr = -1;
14539
14540 static void SaveEngineSnapshotValues_RND()
14541 {
14542   static int belt_base_active_element[4] =
14543   {
14544     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14545     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14546     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14547     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14548   };
14549   int i, j;
14550
14551   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14552   {
14553     int element = EL_CUSTOM_START + i;
14554
14555     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14556   }
14557
14558   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14559   {
14560     int element = EL_GROUP_START + i;
14561
14562     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14563   }
14564
14565   for (i = 0; i < 4; i++)
14566   {
14567     for (j = 0; j < NUM_BELT_PARTS; j++)
14568     {
14569       int element = belt_base_active_element[i] + j;
14570       int graphic = el2img(element);
14571       int anim_mode = graphic_info[graphic].anim_mode;
14572
14573       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14574       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14575     }
14576   }
14577 }
14578
14579 static void LoadEngineSnapshotValues_RND()
14580 {
14581   unsigned int num_random_calls = game.num_random_calls;
14582   int i, j;
14583
14584   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14585   {
14586     int element = EL_CUSTOM_START + i;
14587
14588     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14589   }
14590
14591   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14592   {
14593     int element = EL_GROUP_START + i;
14594
14595     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14596   }
14597
14598   for (i = 0; i < 4; i++)
14599   {
14600     for (j = 0; j < NUM_BELT_PARTS; j++)
14601     {
14602       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14603       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14604
14605       graphic_info[graphic].anim_mode = anim_mode;
14606     }
14607   }
14608
14609   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14610   {
14611     InitRND(tape.random_seed);
14612     for (i = 0; i < num_random_calls; i++)
14613       RND(1);
14614   }
14615
14616   if (game.num_random_calls != num_random_calls)
14617   {
14618     Error(ERR_INFO, "number of random calls out of sync");
14619     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14620     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14621     Error(ERR_EXIT, "this should not happen -- please debug");
14622   }
14623 }
14624
14625 void FreeEngineSnapshotSingle()
14626 {
14627   FreeSnapshotSingle();
14628
14629   setString(&snapshot_level_identifier, NULL);
14630   snapshot_level_nr = -1;
14631 }
14632
14633 void FreeEngineSnapshotList()
14634 {
14635   FreeSnapshotList();
14636 }
14637
14638 ListNode *SaveEngineSnapshotBuffers()
14639 {
14640   ListNode *buffers = NULL;
14641
14642   /* copy some special values to a structure better suited for the snapshot */
14643
14644   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14645     SaveEngineSnapshotValues_RND();
14646   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14647     SaveEngineSnapshotValues_EM();
14648   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14649     SaveEngineSnapshotValues_SP(&buffers);
14650
14651   /* save values stored in special snapshot structure */
14652
14653   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14654     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14655   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14656     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14657   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14658     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14659
14660   /* save further RND engine values */
14661
14662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14665
14666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14670
14671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14676
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14680
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14682
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14684
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14687
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14706
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14709
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14716
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14722
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14725
14726 #if 0
14727   ListNode *node = engine_snapshot_list_rnd;
14728   int num_bytes = 0;
14729
14730   while (node != NULL)
14731   {
14732     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14733
14734     node = node->next;
14735   }
14736
14737   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14738 #endif
14739
14740   return buffers;
14741 }
14742
14743 void SaveEngineSnapshotSingle()
14744 {
14745   ListNode *buffers = SaveEngineSnapshotBuffers();
14746
14747   /* finally save all snapshot buffers to single snapshot */
14748   SaveSnapshotSingle(buffers);
14749
14750   /* save level identification information */
14751   setString(&snapshot_level_identifier, leveldir_current->identifier);
14752   snapshot_level_nr = level_nr;
14753 }
14754
14755 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14756 {
14757   boolean save_snapshot =
14758     (initial_snapshot ||
14759      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14760      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14761       game.snapshot.changed_action));
14762
14763   game.snapshot.changed_action = FALSE;
14764
14765   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14766       tape.quick_resume ||
14767       !save_snapshot)
14768     return FALSE;
14769
14770   ListNode *buffers = SaveEngineSnapshotBuffers();
14771
14772   /* finally save all snapshot buffers to snapshot list */
14773   SaveSnapshotToList(buffers);
14774
14775   return TRUE;
14776 }
14777
14778 boolean SaveEngineSnapshotToList()
14779 {
14780   return SaveEngineSnapshotToListExt(FALSE);
14781 }
14782
14783 void SaveEngineSnapshotToListInitial()
14784 {
14785   FreeEngineSnapshotList();
14786
14787   SaveEngineSnapshotToListExt(TRUE);
14788 }
14789
14790 void LoadEngineSnapshotValues()
14791 {
14792   /* restore special values from snapshot structure */
14793
14794   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14795     LoadEngineSnapshotValues_RND();
14796   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14797     LoadEngineSnapshotValues_EM();
14798   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14799     LoadEngineSnapshotValues_SP();
14800 }
14801
14802 void LoadEngineSnapshotSingle()
14803 {
14804   LoadSnapshotSingle();
14805
14806   LoadEngineSnapshotValues();
14807 }
14808
14809 void LoadEngineSnapshot_Undo(int steps)
14810 {
14811   LoadSnapshotFromList_Older(steps);
14812
14813   LoadEngineSnapshotValues();
14814 }
14815
14816 void LoadEngineSnapshot_Redo(int steps)
14817 {
14818   LoadSnapshotFromList_Newer(steps);
14819
14820   LoadEngineSnapshotValues();
14821 }
14822
14823 boolean CheckEngineSnapshotSingle()
14824 {
14825   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14826           snapshot_level_nr == level_nr);
14827 }
14828
14829 boolean CheckEngineSnapshotList()
14830 {
14831   return CheckSnapshotList();
14832 }
14833
14834
14835 /* ---------- new game button stuff ---------------------------------------- */
14836
14837 static struct
14838 {
14839   int graphic;
14840   struct XY *pos;
14841   int gadget_id;
14842   char *infotext;
14843 } gamebutton_info[NUM_GAME_BUTTONS] =
14844 {
14845   {
14846     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14847     GAME_CTRL_ID_STOP,                  "stop game"
14848   },
14849   {
14850     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14851     GAME_CTRL_ID_PAUSE,                 "pause game"
14852   },
14853   {
14854     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14855     GAME_CTRL_ID_PLAY,                  "play game"
14856   },
14857   {
14858     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14859     GAME_CTRL_ID_UNDO,                  "undo step"
14860   },
14861   {
14862     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14863     GAME_CTRL_ID_REDO,                  "redo step"
14864   },
14865   {
14866     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14867     GAME_CTRL_ID_SAVE,                  "save game"
14868   },
14869   {
14870     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14871     GAME_CTRL_ID_LOAD,                  "load game"
14872   },
14873   {
14874     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14875     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14876   },
14877   {
14878     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14879     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14880   },
14881   {
14882     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14883     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14884   }
14885 };
14886
14887 void CreateGameButtons()
14888 {
14889   int i;
14890
14891   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14892   {
14893     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14894     struct XY *pos = gamebutton_info[i].pos;
14895     struct GadgetInfo *gi;
14896     int button_type;
14897     boolean checked;
14898     unsigned int event_mask;
14899     int base_x = (tape.show_game_buttons ? VX : DX);
14900     int base_y = (tape.show_game_buttons ? VY : DY);
14901     int gd_x   = gfx->src_x;
14902     int gd_y   = gfx->src_y;
14903     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14904     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14905     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14906     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14907     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14908     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14909     int id = i;
14910
14911     if (gfx->bitmap == NULL)
14912     {
14913       game_gadget[id] = NULL;
14914
14915       continue;
14916     }
14917
14918     if (id == GAME_CTRL_ID_STOP ||
14919         id == GAME_CTRL_ID_PAUSE ||
14920         id == GAME_CTRL_ID_PLAY ||
14921         id == GAME_CTRL_ID_SAVE ||
14922         id == GAME_CTRL_ID_LOAD)
14923     {
14924       button_type = GD_TYPE_NORMAL_BUTTON;
14925       checked = FALSE;
14926       event_mask = GD_EVENT_RELEASED;
14927     }
14928     else if (id == GAME_CTRL_ID_UNDO ||
14929              id == GAME_CTRL_ID_REDO)
14930     {
14931       button_type = GD_TYPE_NORMAL_BUTTON;
14932       checked = FALSE;
14933       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14934     }
14935     else
14936     {
14937       button_type = GD_TYPE_CHECK_BUTTON;
14938       checked =
14939         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14940          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14941          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14942       event_mask = GD_EVENT_PRESSED;
14943     }
14944
14945     gi = CreateGadget(GDI_CUSTOM_ID, id,
14946                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14947                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14948                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14949                       GDI_WIDTH, gfx->width,
14950                       GDI_HEIGHT, gfx->height,
14951                       GDI_TYPE, button_type,
14952                       GDI_STATE, GD_BUTTON_UNPRESSED,
14953                       GDI_CHECKED, checked,
14954                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14955                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14956                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14957                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14958                       GDI_DIRECT_DRAW, FALSE,
14959                       GDI_EVENT_MASK, event_mask,
14960                       GDI_CALLBACK_ACTION, HandleGameButtons,
14961                       GDI_END);
14962
14963     if (gi == NULL)
14964       Error(ERR_EXIT, "cannot create gadget");
14965
14966     game_gadget[id] = gi;
14967   }
14968 }
14969
14970 void FreeGameButtons()
14971 {
14972   int i;
14973
14974   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14975     FreeGadget(game_gadget[i]);
14976 }
14977
14978 static void MapGameButtonsAtSamePosition(int id)
14979 {
14980   int i;
14981
14982   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14983     if (i != id &&
14984         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14985         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14986       MapGadget(game_gadget[i]);
14987 }
14988
14989 static void UnmapGameButtonsAtSamePosition(int id)
14990 {
14991   int i;
14992
14993   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14994     if (i != id &&
14995         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14996         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14997       UnmapGadget(game_gadget[i]);
14998 }
14999
15000 void MapUndoRedoButtons()
15001 {
15002   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15003   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15004   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15005
15006   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15007   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15008   MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
15009 }
15010
15011 void UnmapUndoRedoButtons()
15012 {
15013   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15014   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15015   UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
15016
15017   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15018   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15019   MapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15020 }
15021
15022 void MapGameButtons()
15023 {
15024   int i;
15025
15026   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15027     if (i != GAME_CTRL_ID_UNDO &&
15028         i != GAME_CTRL_ID_REDO &&
15029         i != GAME_CTRL_ID_PLAY)
15030       MapGadget(game_gadget[i]);
15031 }
15032
15033 void UnmapGameButtons()
15034 {
15035   int i;
15036
15037   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15038     UnmapGadget(game_gadget[i]);
15039 }
15040
15041 void RedrawGameButtons()
15042 {
15043   int i;
15044
15045   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15046     RedrawGadget(game_gadget[i]);
15047
15048   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15049   redraw_mask &= ~REDRAW_ALL;
15050 }
15051
15052 void GameUndoRedoExt()
15053 {
15054   ClearPlayerAction();
15055
15056   tape.pausing = TRUE;
15057
15058   RedrawPlayfield();
15059   UpdateAndDisplayGameControlValues();
15060
15061   DrawCompleteVideoDisplay();
15062   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15063   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15064   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15065                     VIDEO_STATE_1STEP_OFF), 0);
15066
15067   BackToFront();
15068 }
15069
15070 void GameUndo(int steps)
15071 {
15072   if (!CheckEngineSnapshotList())
15073     return;
15074
15075   LoadEngineSnapshot_Undo(steps);
15076
15077   GameUndoRedoExt();
15078 }
15079
15080 void GameRedo(int steps)
15081 {
15082   if (!CheckEngineSnapshotList())
15083     return;
15084
15085   LoadEngineSnapshot_Redo(steps);
15086
15087   GameUndoRedoExt();
15088 }
15089
15090 static void HandleGameButtonsExt(int id, int button)
15091 {
15092   int steps = BUTTON_STEPSIZE(button);
15093   boolean handle_game_buttons =
15094     (game_status == GAME_MODE_PLAYING ||
15095      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15096
15097   if (!handle_game_buttons)
15098     return;
15099
15100   switch (id)
15101   {
15102     case GAME_CTRL_ID_STOP:
15103       if (game_status == GAME_MODE_MAIN)
15104         break;
15105
15106       if (tape.playing)
15107         TapeStop();
15108       else
15109         RequestQuitGame(TRUE);
15110
15111       break;
15112
15113     case GAME_CTRL_ID_PAUSE:
15114       if (options.network && game_status == GAME_MODE_PLAYING)
15115       {
15116 #if defined(NETWORK_AVALIABLE)
15117         if (tape.pausing)
15118           SendToServer_ContinuePlaying();
15119         else
15120           SendToServer_PausePlaying();
15121 #endif
15122       }
15123       else
15124         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15125       break;
15126
15127     case GAME_CTRL_ID_PLAY:
15128       if (game_status == GAME_MODE_MAIN)
15129       {
15130         StartGameActions(options.network, setup.autorecord, level.random_seed);
15131       }
15132       else if (tape.pausing)
15133       {
15134 #if defined(NETWORK_AVALIABLE)
15135         if (options.network)
15136           SendToServer_ContinuePlaying();
15137         else
15138 #endif
15139           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15140       }
15141       break;
15142
15143     case GAME_CTRL_ID_UNDO:
15144       GameUndo(steps);
15145       break;
15146
15147     case GAME_CTRL_ID_REDO:
15148       GameRedo(steps);
15149       break;
15150
15151     case GAME_CTRL_ID_SAVE:
15152       TapeQuickSave();
15153       break;
15154
15155     case GAME_CTRL_ID_LOAD:
15156       TapeQuickLoad();
15157       break;
15158
15159     case SOUND_CTRL_ID_MUSIC:
15160       if (setup.sound_music)
15161       { 
15162         setup.sound_music = FALSE;
15163
15164         FadeMusic();
15165       }
15166       else if (audio.music_available)
15167       { 
15168         setup.sound = setup.sound_music = TRUE;
15169
15170         SetAudioMode(setup.sound);
15171
15172         PlayLevelMusic();
15173       }
15174       break;
15175
15176     case SOUND_CTRL_ID_LOOPS:
15177       if (setup.sound_loops)
15178         setup.sound_loops = FALSE;
15179       else if (audio.loops_available)
15180       {
15181         setup.sound = setup.sound_loops = TRUE;
15182
15183         SetAudioMode(setup.sound);
15184       }
15185       break;
15186
15187     case SOUND_CTRL_ID_SIMPLE:
15188       if (setup.sound_simple)
15189         setup.sound_simple = FALSE;
15190       else if (audio.sound_available)
15191       {
15192         setup.sound = setup.sound_simple = TRUE;
15193
15194         SetAudioMode(setup.sound);
15195       }
15196       break;
15197
15198     default:
15199       break;
15200   }
15201 }
15202
15203 static void HandleGameButtons(struct GadgetInfo *gi)
15204 {
15205   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15206 }
15207
15208 void HandleSoundButtonKeys(Key key)
15209 {
15210
15211   if (key == setup.shortcut.sound_simple)
15212     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15213   else if (key == setup.shortcut.sound_loops)
15214     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15215   else if (key == setup.shortcut.sound_music)
15216     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15217 }