added screen redraw function for native BD engine
[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 //                  https://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 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_GEMS_TOTAL                   2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_TOTAL,
250     &game.panel.gems_total,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void InitField(int x, int y, boolean init_game)
1839 {
1840   int element = Tile[x][y];
1841
1842   switch (element)
1843   {
1844     case EL_SP_MURPHY:
1845     case EL_PLAYER_1:
1846     case EL_PLAYER_2:
1847     case EL_PLAYER_3:
1848     case EL_PLAYER_4:
1849       InitPlayerField(x, y, element, init_game);
1850       break;
1851
1852     case EL_SOKOBAN_FIELD_PLAYER:
1853       element = Tile[x][y] = EL_PLAYER_1;
1854       InitField(x, y, init_game);
1855
1856       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1857       InitField(x, y, init_game);
1858       break;
1859
1860     case EL_SOKOBAN_FIELD_EMPTY:
1861       IncrementSokobanFieldsNeeded();
1862       break;
1863
1864     case EL_SOKOBAN_OBJECT:
1865       IncrementSokobanObjectsNeeded();
1866       break;
1867
1868     case EL_STONEBLOCK:
1869       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1870         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1871       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1872         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1873       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1874         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1875       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1876         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1877       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1878         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1879       break;
1880
1881     case EL_BUG:
1882     case EL_BUG_RIGHT:
1883     case EL_BUG_UP:
1884     case EL_BUG_LEFT:
1885     case EL_BUG_DOWN:
1886     case EL_SPACESHIP:
1887     case EL_SPACESHIP_RIGHT:
1888     case EL_SPACESHIP_UP:
1889     case EL_SPACESHIP_LEFT:
1890     case EL_SPACESHIP_DOWN:
1891     case EL_BD_BUTTERFLY:
1892     case EL_BD_BUTTERFLY_RIGHT:
1893     case EL_BD_BUTTERFLY_UP:
1894     case EL_BD_BUTTERFLY_LEFT:
1895     case EL_BD_BUTTERFLY_DOWN:
1896     case EL_BD_FIREFLY:
1897     case EL_BD_FIREFLY_RIGHT:
1898     case EL_BD_FIREFLY_UP:
1899     case EL_BD_FIREFLY_LEFT:
1900     case EL_BD_FIREFLY_DOWN:
1901     case EL_PACMAN_RIGHT:
1902     case EL_PACMAN_UP:
1903     case EL_PACMAN_LEFT:
1904     case EL_PACMAN_DOWN:
1905     case EL_YAMYAM:
1906     case EL_YAMYAM_LEFT:
1907     case EL_YAMYAM_RIGHT:
1908     case EL_YAMYAM_UP:
1909     case EL_YAMYAM_DOWN:
1910     case EL_DARK_YAMYAM:
1911     case EL_ROBOT:
1912     case EL_PACMAN:
1913     case EL_SP_SNIKSNAK:
1914     case EL_SP_ELECTRON:
1915     case EL_MOLE:
1916     case EL_MOLE_LEFT:
1917     case EL_MOLE_RIGHT:
1918     case EL_MOLE_UP:
1919     case EL_MOLE_DOWN:
1920     case EL_SPRING_LEFT:
1921     case EL_SPRING_RIGHT:
1922       InitMovDir(x, y);
1923       break;
1924
1925     case EL_AMOEBA_FULL:
1926     case EL_BD_AMOEBA:
1927       InitAmoebaNr(x, y);
1928       break;
1929
1930     case EL_AMOEBA_DROP:
1931       if (y == lev_fieldy - 1)
1932       {
1933         Tile[x][y] = EL_AMOEBA_GROWING;
1934         Store[x][y] = EL_AMOEBA_WET;
1935       }
1936       break;
1937
1938     case EL_DYNAMITE_ACTIVE:
1939     case EL_SP_DISK_RED_ACTIVE:
1940     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1941     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1942     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1943     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1944       MovDelay[x][y] = 96;
1945       break;
1946
1947     case EL_EM_DYNAMITE_ACTIVE:
1948       MovDelay[x][y] = 32;
1949       break;
1950
1951     case EL_LAMP:
1952       game.lights_still_needed++;
1953       break;
1954
1955     case EL_PENGUIN:
1956       game.friends_still_needed++;
1957       break;
1958
1959     case EL_PIG:
1960     case EL_DRAGON:
1961       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1962       break;
1963
1964     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1965     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1966     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1967     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1968     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1969     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1970     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1971     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1972     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1973     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1974     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1975     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1976       if (init_game)
1977       {
1978         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1979         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1980         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1981
1982         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1983         {
1984           game.belt_dir[belt_nr] = belt_dir;
1985           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1986         }
1987         else    // more than one switch -- set it like the first switch
1988         {
1989           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1990         }
1991       }
1992       break;
1993
1994     case EL_LIGHT_SWITCH_ACTIVE:
1995       if (init_game)
1996         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1997       break;
1998
1999     case EL_INVISIBLE_STEELWALL:
2000     case EL_INVISIBLE_WALL:
2001     case EL_INVISIBLE_SAND:
2002       if (game.light_time_left > 0 ||
2003           game.lenses_time_left > 0)
2004         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2005       break;
2006
2007     case EL_EMC_MAGIC_BALL:
2008       if (game.ball_active)
2009         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2010       break;
2011
2012     case EL_EMC_MAGIC_BALL_SWITCH:
2013       if (game.ball_active)
2014         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2015       break;
2016
2017     case EL_TRIGGER_PLAYER:
2018     case EL_TRIGGER_ELEMENT:
2019     case EL_TRIGGER_CE_VALUE:
2020     case EL_TRIGGER_CE_SCORE:
2021     case EL_SELF:
2022     case EL_ANY_ELEMENT:
2023     case EL_CURRENT_CE_VALUE:
2024     case EL_CURRENT_CE_SCORE:
2025     case EL_PREV_CE_1:
2026     case EL_PREV_CE_2:
2027     case EL_PREV_CE_3:
2028     case EL_PREV_CE_4:
2029     case EL_PREV_CE_5:
2030     case EL_PREV_CE_6:
2031     case EL_PREV_CE_7:
2032     case EL_PREV_CE_8:
2033     case EL_NEXT_CE_1:
2034     case EL_NEXT_CE_2:
2035     case EL_NEXT_CE_3:
2036     case EL_NEXT_CE_4:
2037     case EL_NEXT_CE_5:
2038     case EL_NEXT_CE_6:
2039     case EL_NEXT_CE_7:
2040     case EL_NEXT_CE_8:
2041       // reference elements should not be used on the playfield
2042       Tile[x][y] = EL_EMPTY;
2043       break;
2044
2045     default:
2046       if (IS_CUSTOM_ELEMENT(element))
2047       {
2048         if (CAN_MOVE(element))
2049           InitMovDir(x, y);
2050
2051         if (!element_info[element].use_last_ce_value || init_game)
2052           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2053       }
2054       else if (IS_GROUP_ELEMENT(element))
2055       {
2056         Tile[x][y] = GetElementFromGroupElement(element);
2057
2058         InitField(x, y, init_game);
2059       }
2060       else if (IS_EMPTY_ELEMENT(element))
2061       {
2062         GfxElementEmpty[x][y] = element;
2063         Tile[x][y] = EL_EMPTY;
2064
2065         if (element_info[element].use_gfx_element)
2066           game.use_masked_elements = TRUE;
2067       }
2068
2069       break;
2070   }
2071
2072   if (!init_game)
2073     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2074 }
2075
2076 static void InitField_WithBug1(int x, int y, boolean init_game)
2077 {
2078   InitField(x, y, init_game);
2079
2080   // not needed to call InitMovDir() -- already done by InitField()!
2081   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2082       CAN_MOVE(Tile[x][y]))
2083     InitMovDir(x, y);
2084 }
2085
2086 static void InitField_WithBug2(int x, int y, boolean init_game)
2087 {
2088   int old_element = Tile[x][y];
2089
2090   InitField(x, y, init_game);
2091
2092   // not needed to call InitMovDir() -- already done by InitField()!
2093   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2094       CAN_MOVE(old_element) &&
2095       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2096     InitMovDir(x, y);
2097
2098   /* this case is in fact a combination of not less than three bugs:
2099      first, it calls InitMovDir() for elements that can move, although this is
2100      already done by InitField(); then, it checks the element that was at this
2101      field _before_ the call to InitField() (which can change it); lastly, it
2102      was not called for "mole with direction" elements, which were treated as
2103      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2104   */
2105 }
2106
2107 static int get_key_element_from_nr(int key_nr)
2108 {
2109   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2110                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2111                           EL_EM_KEY_1 : EL_KEY_1);
2112
2113   return key_base_element + key_nr;
2114 }
2115
2116 static int get_next_dropped_element(struct PlayerInfo *player)
2117 {
2118   return (player->inventory_size > 0 ?
2119           player->inventory_element[player->inventory_size - 1] :
2120           player->inventory_infinite_element != EL_UNDEFINED ?
2121           player->inventory_infinite_element :
2122           player->dynabombs_left > 0 ?
2123           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2124           EL_UNDEFINED);
2125 }
2126
2127 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2128 {
2129   // pos >= 0: get element from bottom of the stack;
2130   // pos <  0: get element from top of the stack
2131
2132   if (pos < 0)
2133   {
2134     int min_inventory_size = -pos;
2135     int inventory_pos = player->inventory_size - min_inventory_size;
2136     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2137
2138     return (player->inventory_size >= min_inventory_size ?
2139             player->inventory_element[inventory_pos] :
2140             player->inventory_infinite_element != EL_UNDEFINED ?
2141             player->inventory_infinite_element :
2142             player->dynabombs_left >= min_dynabombs_left ?
2143             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2144             EL_UNDEFINED);
2145   }
2146   else
2147   {
2148     int min_dynabombs_left = pos + 1;
2149     int min_inventory_size = pos + 1 - player->dynabombs_left;
2150     int inventory_pos = pos - player->dynabombs_left;
2151
2152     return (player->inventory_infinite_element != EL_UNDEFINED ?
2153             player->inventory_infinite_element :
2154             player->dynabombs_left >= min_dynabombs_left ?
2155             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2156             player->inventory_size >= min_inventory_size ?
2157             player->inventory_element[inventory_pos] :
2158             EL_UNDEFINED);
2159   }
2160 }
2161
2162 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2163 {
2164   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2165   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2166   int compare_result;
2167
2168   if (gpo1->sort_priority != gpo2->sort_priority)
2169     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2170   else
2171     compare_result = gpo1->nr - gpo2->nr;
2172
2173   return compare_result;
2174 }
2175
2176 int getPlayerInventorySize(int player_nr)
2177 {
2178   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179     return game_em.ply[player_nr]->dynamite;
2180   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2181     return game_sp.red_disk_count;
2182   else
2183     return stored_player[player_nr].inventory_size;
2184 }
2185
2186 static void InitGameControlValues(void)
2187 {
2188   int i;
2189
2190   for (i = 0; game_panel_controls[i].nr != -1; i++)
2191   {
2192     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2193     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2194     struct TextPosInfo *pos = gpc->pos;
2195     int nr = gpc->nr;
2196     int type = gpc->type;
2197
2198     if (nr != i)
2199     {
2200       Error("'game_panel_controls' structure corrupted at %d", i);
2201
2202       Fail("this should not happen -- please debug");
2203     }
2204
2205     // force update of game controls after initialization
2206     gpc->value = gpc->last_value = -1;
2207     gpc->frame = gpc->last_frame = -1;
2208     gpc->gfx_frame = -1;
2209
2210     // determine panel value width for later calculation of alignment
2211     if (type == TYPE_INTEGER || type == TYPE_STRING)
2212     {
2213       pos->width = pos->size * getFontWidth(pos->font);
2214       pos->height = getFontHeight(pos->font);
2215     }
2216     else if (type == TYPE_ELEMENT)
2217     {
2218       pos->width = pos->size;
2219       pos->height = pos->size;
2220     }
2221
2222     // fill structure for game panel draw order
2223     gpo->nr = gpc->nr;
2224     gpo->sort_priority = pos->sort_priority;
2225   }
2226
2227   // sort game panel controls according to sort_priority and control number
2228   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2229         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2230 }
2231
2232 static void UpdatePlayfieldElementCount(void)
2233 {
2234   boolean use_element_count = FALSE;
2235   int i, j, x, y;
2236
2237   // first check if it is needed at all to calculate playfield element count
2238   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2239     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2240       use_element_count = TRUE;
2241
2242   if (!use_element_count)
2243     return;
2244
2245   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2246     element_info[i].element_count = 0;
2247
2248   SCAN_PLAYFIELD(x, y)
2249   {
2250     element_info[Tile[x][y]].element_count++;
2251   }
2252
2253   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2254     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2255       if (IS_IN_GROUP(j, i))
2256         element_info[EL_GROUP_START + i].element_count +=
2257           element_info[j].element_count;
2258 }
2259
2260 static void UpdateGameControlValues(void)
2261 {
2262   int i, k;
2263   int time = (game.LevelSolved ?
2264               game.LevelSolved_CountingTime :
2265               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2266               game_bd.time_played :
2267               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268               game_em.lev->time :
2269               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270               game_sp.time_played :
2271               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272               game_mm.energy_left :
2273               game.no_level_time_limit ? TimePlayed : TimeLeft);
2274   int score = (game.LevelSolved ?
2275                game.LevelSolved_CountingScore :
2276                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2277                game_bd.score :
2278                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2279                game_em.lev->score :
2280                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2281                game_sp.score :
2282                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2283                game_mm.score :
2284                game.score);
2285   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2286               game_bd.gems_still_needed :
2287               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2288               game_em.lev->gems_needed :
2289               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2290               game_sp.infotrons_still_needed :
2291               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2292               game_mm.kettles_still_needed :
2293               game.gems_still_needed);
2294   int gems_total = level.gems_needed;
2295   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2296                         game_bd.game->cave->diamonds_collected :
2297                         gems_total - gems);
2298   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2299                     game_bd.game->cave->diamond_value :
2300                     level.score[SC_EMERALD]);
2301   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2302                      game_bd.gems_still_needed > 0 :
2303                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2304                      game_em.lev->gems_needed > 0 :
2305                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2306                      game_sp.infotrons_still_needed > 0 :
2307                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2308                      game_mm.kettles_still_needed > 0 ||
2309                      game_mm.lights_still_needed > 0 :
2310                      game.gems_still_needed > 0 ||
2311                      game.sokoban_fields_still_needed > 0 ||
2312                      game.sokoban_objects_still_needed > 0 ||
2313                      game.lights_still_needed > 0);
2314   int health = (game.LevelSolved ?
2315                 game.LevelSolved_CountingHealth :
2316                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2317                 MM_HEALTH(game_mm.laser_overload_value) :
2318                 game.health);
2319   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2320
2321   UpdatePlayfieldElementCount();
2322
2323   // update game panel control values
2324
2325   // used instead of "level_nr" (for network games)
2326   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2327   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2328   game_panel_controls[GAME_PANEL_GEMS_TOTAL].value = gems_total;
2329   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2330   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2331
2332   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2333   for (i = 0; i < MAX_NUM_KEYS; i++)
2334     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2335   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2336   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2337
2338   if (game.centered_player_nr == -1)
2339   {
2340     for (i = 0; i < MAX_PLAYERS; i++)
2341     {
2342       // only one player in Supaplex game engine
2343       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2344         break;
2345
2346       for (k = 0; k < MAX_NUM_KEYS; k++)
2347       {
2348         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2349         {
2350           if (game_em.ply[i]->keys & (1 << k))
2351             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2352               get_key_element_from_nr(k);
2353         }
2354         else if (stored_player[i].key[k])
2355           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2356             get_key_element_from_nr(k);
2357       }
2358
2359       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2360         getPlayerInventorySize(i);
2361
2362       if (stored_player[i].num_white_keys > 0)
2363         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2364           EL_DC_KEY_WHITE;
2365
2366       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2367         stored_player[i].num_white_keys;
2368     }
2369   }
2370   else
2371   {
2372     int player_nr = game.centered_player_nr;
2373
2374     for (k = 0; k < MAX_NUM_KEYS; k++)
2375     {
2376       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2377       {
2378         if (game_em.ply[player_nr]->keys & (1 << k))
2379           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2380             get_key_element_from_nr(k);
2381       }
2382       else if (stored_player[player_nr].key[k])
2383         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2384           get_key_element_from_nr(k);
2385     }
2386
2387     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2388       getPlayerInventorySize(player_nr);
2389
2390     if (stored_player[player_nr].num_white_keys > 0)
2391       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2392
2393     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2394       stored_player[player_nr].num_white_keys;
2395   }
2396
2397   // re-arrange keys on game panel, if needed or if defined by style settings
2398   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2399   {
2400     int nr = GAME_PANEL_KEY_1 + i;
2401     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2402     struct TextPosInfo *pos = gpc->pos;
2403
2404     // skip check if key is not in the player's inventory
2405     if (gpc->value == EL_EMPTY)
2406       continue;
2407
2408     // check if keys should be arranged on panel from left to right
2409     if (pos->style == STYLE_LEFTMOST_POSITION)
2410     {
2411       // check previous key positions (left from current key)
2412       for (k = 0; k < i; k++)
2413       {
2414         int nr_new = GAME_PANEL_KEY_1 + k;
2415
2416         if (game_panel_controls[nr_new].value == EL_EMPTY)
2417         {
2418           game_panel_controls[nr_new].value = gpc->value;
2419           gpc->value = EL_EMPTY;
2420
2421           break;
2422         }
2423       }
2424     }
2425
2426     // check if "undefined" keys can be placed at some other position
2427     if (pos->x == -1 && pos->y == -1)
2428     {
2429       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2430
2431       // 1st try: display key at the same position as normal or EM keys
2432       if (game_panel_controls[nr_new].value == EL_EMPTY)
2433       {
2434         game_panel_controls[nr_new].value = gpc->value;
2435       }
2436       else
2437       {
2438         // 2nd try: display key at the next free position in the key panel
2439         for (k = 0; k < STD_NUM_KEYS; k++)
2440         {
2441           nr_new = GAME_PANEL_KEY_1 + k;
2442
2443           if (game_panel_controls[nr_new].value == EL_EMPTY)
2444           {
2445             game_panel_controls[nr_new].value = gpc->value;
2446
2447             break;
2448           }
2449         }
2450       }
2451     }
2452   }
2453
2454   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2455   {
2456     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2457       get_inventory_element_from_pos(local_player, i);
2458     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2459       get_inventory_element_from_pos(local_player, -i - 1);
2460   }
2461
2462   game_panel_controls[GAME_PANEL_SCORE].value = score;
2463   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2464
2465   game_panel_controls[GAME_PANEL_TIME].value = time;
2466
2467   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2468   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2469   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2470
2471   if (level.time == 0)
2472     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2473   else
2474     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2475
2476   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2477   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2478
2479   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2480
2481   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2482     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2483      EL_EMPTY);
2484   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2485     local_player->shield_normal_time_left;
2486   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2487     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2488      EL_EMPTY);
2489   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2490     local_player->shield_deadly_time_left;
2491
2492   game_panel_controls[GAME_PANEL_EXIT].value =
2493     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2494
2495   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2496     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2497   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2498     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2499      EL_EMC_MAGIC_BALL_SWITCH);
2500
2501   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2502     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2503   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2504     game.light_time_left;
2505
2506   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2507     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2508   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2509     game.timegate_time_left;
2510
2511   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2512     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2513
2514   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2515     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2516   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2517     game.lenses_time_left;
2518
2519   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2520     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2521   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2522     game.magnify_time_left;
2523
2524   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2525     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2526      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2527      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2528      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2529      EL_BALLOON_SWITCH_NONE);
2530
2531   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2532     local_player->dynabomb_count;
2533   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2534     local_player->dynabomb_size;
2535   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2536     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2537
2538   game_panel_controls[GAME_PANEL_PENGUINS].value =
2539     game.friends_still_needed;
2540
2541   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2542     game.sokoban_objects_still_needed;
2543   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2544     game.sokoban_fields_still_needed;
2545
2546   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2547     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2548
2549   for (i = 0; i < NUM_BELTS; i++)
2550   {
2551     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2552       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2553        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2554     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2555       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2556   }
2557
2558   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2559     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2560   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2561     game.magic_wall_time_left;
2562
2563   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2564     local_player->gravity;
2565
2566   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2567     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2568
2569   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2570     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2571       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2572        game.panel.element[i].id : EL_UNDEFINED);
2573
2574   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2575     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2576       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2577        element_info[game.panel.element_count[i].id].element_count : 0);
2578
2579   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2580     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2581       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2582        element_info[game.panel.ce_score[i].id].collect_score : 0);
2583
2584   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2585     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2586       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2587        element_info[game.panel.ce_score_element[i].id].collect_score :
2588        EL_UNDEFINED);
2589
2590   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2591   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2592   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2593
2594   // update game panel control frames
2595
2596   for (i = 0; game_panel_controls[i].nr != -1; i++)
2597   {
2598     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2599
2600     if (gpc->type == TYPE_ELEMENT)
2601     {
2602       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2603       {
2604         int last_anim_random_frame = gfx.anim_random_frame;
2605         int element = gpc->value;
2606         int graphic = el2panelimg(element);
2607         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2608                                sync_random_frame :
2609                                graphic_info[graphic].anim_global_anim_sync ?
2610                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2611
2612         if (gpc->value != gpc->last_value)
2613         {
2614           gpc->gfx_frame = 0;
2615           gpc->gfx_random = init_gfx_random;
2616         }
2617         else
2618         {
2619           gpc->gfx_frame++;
2620
2621           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2622               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2623             gpc->gfx_random = init_gfx_random;
2624         }
2625
2626         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2627           gfx.anim_random_frame = gpc->gfx_random;
2628
2629         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2630           gpc->gfx_frame = element_info[element].collect_score;
2631
2632         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2633
2634         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2635           gfx.anim_random_frame = last_anim_random_frame;
2636       }
2637     }
2638     else if (gpc->type == TYPE_GRAPHIC)
2639     {
2640       if (gpc->graphic != IMG_UNDEFINED)
2641       {
2642         int last_anim_random_frame = gfx.anim_random_frame;
2643         int graphic = gpc->graphic;
2644         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2645                                sync_random_frame :
2646                                graphic_info[graphic].anim_global_anim_sync ?
2647                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2648
2649         if (gpc->value != gpc->last_value)
2650         {
2651           gpc->gfx_frame = 0;
2652           gpc->gfx_random = init_gfx_random;
2653         }
2654         else
2655         {
2656           gpc->gfx_frame++;
2657
2658           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2659               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2660             gpc->gfx_random = init_gfx_random;
2661         }
2662
2663         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2664           gfx.anim_random_frame = gpc->gfx_random;
2665
2666         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2667
2668         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2669           gfx.anim_random_frame = last_anim_random_frame;
2670       }
2671     }
2672   }
2673 }
2674
2675 static void DisplayGameControlValues(void)
2676 {
2677   boolean redraw_panel = FALSE;
2678   int i;
2679
2680   for (i = 0; game_panel_controls[i].nr != -1; i++)
2681   {
2682     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2683
2684     if (PANEL_DEACTIVATED(gpc->pos))
2685       continue;
2686
2687     if (gpc->value == gpc->last_value &&
2688         gpc->frame == gpc->last_frame)
2689       continue;
2690
2691     redraw_panel = TRUE;
2692   }
2693
2694   if (!redraw_panel)
2695     return;
2696
2697   // copy default game door content to main double buffer
2698
2699   // !!! CHECK AGAIN !!!
2700   SetPanelBackground();
2701   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2702   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2703
2704   // redraw game control buttons
2705   RedrawGameButtons();
2706
2707   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2708
2709   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2710   {
2711     int nr = game_panel_order[i].nr;
2712     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2713     struct TextPosInfo *pos = gpc->pos;
2714     int type = gpc->type;
2715     int value = gpc->value;
2716     int frame = gpc->frame;
2717     int size = pos->size;
2718     int font = pos->font;
2719     boolean draw_masked = pos->draw_masked;
2720     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2721
2722     if (PANEL_DEACTIVATED(pos))
2723       continue;
2724
2725     if (pos->class == get_hash_from_key("extra_panel_items") &&
2726         !setup.prefer_extra_panel_items)
2727       continue;
2728
2729     gpc->last_value = value;
2730     gpc->last_frame = frame;
2731
2732     if (type == TYPE_INTEGER)
2733     {
2734       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2735           nr == GAME_PANEL_INVENTORY_COUNT ||
2736           nr == GAME_PANEL_SCORE ||
2737           nr == GAME_PANEL_HIGHSCORE ||
2738           nr == GAME_PANEL_TIME)
2739       {
2740         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2741
2742         if (use_dynamic_size)           // use dynamic number of digits
2743         {
2744           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2745                               nr == GAME_PANEL_INVENTORY_COUNT ||
2746                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2747           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2748                           nr == GAME_PANEL_INVENTORY_COUNT ||
2749                           nr == GAME_PANEL_TIME ? 1 : 2);
2750           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2751                        nr == GAME_PANEL_INVENTORY_COUNT ||
2752                        nr == GAME_PANEL_TIME ? 3 : 5);
2753           int size2 = size1 + size_add;
2754           int font1 = pos->font;
2755           int font2 = pos->font_alt;
2756
2757           size = (value < value_change ? size1 : size2);
2758           font = (value < value_change ? font1 : font2);
2759         }
2760       }
2761
2762       // correct text size if "digits" is zero or less
2763       if (size <= 0)
2764         size = strlen(int2str(value, size));
2765
2766       // dynamically correct text alignment
2767       pos->width = size * getFontWidth(font);
2768
2769       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2770                   int2str(value, size), font, mask_mode);
2771     }
2772     else if (type == TYPE_ELEMENT)
2773     {
2774       int element, graphic;
2775       Bitmap *src_bitmap;
2776       int src_x, src_y;
2777       int width, height;
2778       int dst_x = PANEL_XPOS(pos);
2779       int dst_y = PANEL_YPOS(pos);
2780
2781       if (value != EL_UNDEFINED && value != EL_EMPTY)
2782       {
2783         element = value;
2784         graphic = el2panelimg(value);
2785
2786 #if 0
2787         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2788               element, EL_NAME(element), size);
2789 #endif
2790
2791         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2792           size = TILESIZE;
2793
2794         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2795                               &src_x, &src_y);
2796
2797         width  = graphic_info[graphic].width  * size / TILESIZE;
2798         height = graphic_info[graphic].height * size / TILESIZE;
2799
2800         if (draw_masked)
2801           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2802                            dst_x, dst_y);
2803         else
2804           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2805                      dst_x, dst_y);
2806       }
2807     }
2808     else if (type == TYPE_GRAPHIC)
2809     {
2810       int graphic        = gpc->graphic;
2811       int graphic_active = gpc->graphic_active;
2812       Bitmap *src_bitmap;
2813       int src_x, src_y;
2814       int width, height;
2815       int dst_x = PANEL_XPOS(pos);
2816       int dst_y = PANEL_YPOS(pos);
2817       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2818                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2819
2820       if (graphic != IMG_UNDEFINED && !skip)
2821       {
2822         if (pos->style == STYLE_REVERSE)
2823           value = 100 - value;
2824
2825         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2826
2827         if (pos->direction & MV_HORIZONTAL)
2828         {
2829           width  = graphic_info[graphic_active].width * value / 100;
2830           height = graphic_info[graphic_active].height;
2831
2832           if (pos->direction == MV_LEFT)
2833           {
2834             src_x += graphic_info[graphic_active].width - width;
2835             dst_x += graphic_info[graphic_active].width - width;
2836           }
2837         }
2838         else
2839         {
2840           width  = graphic_info[graphic_active].width;
2841           height = graphic_info[graphic_active].height * value / 100;
2842
2843           if (pos->direction == MV_UP)
2844           {
2845             src_y += graphic_info[graphic_active].height - height;
2846             dst_y += graphic_info[graphic_active].height - height;
2847           }
2848         }
2849
2850         if (draw_masked)
2851           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2852                            dst_x, dst_y);
2853         else
2854           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2855                      dst_x, dst_y);
2856
2857         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2858
2859         if (pos->direction & MV_HORIZONTAL)
2860         {
2861           if (pos->direction == MV_RIGHT)
2862           {
2863             src_x += width;
2864             dst_x += width;
2865           }
2866           else
2867           {
2868             dst_x = PANEL_XPOS(pos);
2869           }
2870
2871           width = graphic_info[graphic].width - width;
2872         }
2873         else
2874         {
2875           if (pos->direction == MV_DOWN)
2876           {
2877             src_y += height;
2878             dst_y += height;
2879           }
2880           else
2881           {
2882             dst_y = PANEL_YPOS(pos);
2883           }
2884
2885           height = graphic_info[graphic].height - height;
2886         }
2887
2888         if (draw_masked)
2889           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2890                            dst_x, dst_y);
2891         else
2892           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2893                      dst_x, dst_y);
2894       }
2895     }
2896     else if (type == TYPE_STRING)
2897     {
2898       boolean active = (value != 0);
2899       char *state_normal = "off";
2900       char *state_active = "on";
2901       char *state = (active ? state_active : state_normal);
2902       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2903                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2904                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2905                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2906
2907       if (nr == GAME_PANEL_GRAVITY_STATE)
2908       {
2909         int font1 = pos->font;          // (used for normal state)
2910         int font2 = pos->font_alt;      // (used for active state)
2911
2912         font = (active ? font2 : font1);
2913       }
2914
2915       if (s != NULL)
2916       {
2917         char *s_cut;
2918
2919         if (size <= 0)
2920         {
2921           // don't truncate output if "chars" is zero or less
2922           size = strlen(s);
2923
2924           // dynamically correct text alignment
2925           pos->width = size * getFontWidth(font);
2926         }
2927
2928         s_cut = getStringCopyN(s, size);
2929
2930         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2931                     s_cut, font, mask_mode);
2932
2933         free(s_cut);
2934       }
2935     }
2936
2937     redraw_mask |= REDRAW_DOOR_1;
2938   }
2939
2940   SetGameStatus(GAME_MODE_PLAYING);
2941 }
2942
2943 void UpdateAndDisplayGameControlValues(void)
2944 {
2945   if (tape.deactivate_display)
2946     return;
2947
2948   UpdateGameControlValues();
2949   DisplayGameControlValues();
2950 }
2951
2952 void UpdateGameDoorValues(void)
2953 {
2954   UpdateGameControlValues();
2955 }
2956
2957 void DrawGameDoorValues(void)
2958 {
2959   DisplayGameControlValues();
2960 }
2961
2962
2963 // ============================================================================
2964 // InitGameEngine()
2965 // ----------------------------------------------------------------------------
2966 // initialize game engine due to level / tape version number
2967 // ============================================================================
2968
2969 static void InitGameEngine(void)
2970 {
2971   int i, j, k, l, x, y;
2972
2973   // set game engine from tape file when re-playing, else from level file
2974   game.engine_version = (tape.playing ? tape.engine_version :
2975                          level.game_version);
2976
2977   // set single or multi-player game mode (needed for re-playing tapes)
2978   game.team_mode = setup.team_mode;
2979
2980   if (tape.playing)
2981   {
2982     int num_players = 0;
2983
2984     for (i = 0; i < MAX_PLAYERS; i++)
2985       if (tape.player_participates[i])
2986         num_players++;
2987
2988     // multi-player tapes contain input data for more than one player
2989     game.team_mode = (num_players > 1);
2990   }
2991
2992 #if 0
2993   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2994         level.game_version);
2995   Debug("game:init:level", "          tape.file_version   == %06d",
2996         tape.file_version);
2997   Debug("game:init:level", "          tape.game_version   == %06d",
2998         tape.game_version);
2999   Debug("game:init:level", "          tape.engine_version == %06d",
3000         tape.engine_version);
3001   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3002         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3003 #endif
3004
3005   // --------------------------------------------------------------------------
3006   // set flags for bugs and changes according to active game engine version
3007   // --------------------------------------------------------------------------
3008
3009   /*
3010     Summary of bugfix:
3011     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3012
3013     Bug was introduced in version:
3014     2.0.1
3015
3016     Bug was fixed in version:
3017     4.2.0.0
3018
3019     Description:
3020     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3021     but the property "can fall" was missing, which caused some levels to be
3022     unsolvable. This was fixed in version 4.2.0.0.
3023
3024     Affected levels/tapes:
3025     An example for a tape that was fixed by this bugfix is tape 029 from the
3026     level set "rnd_sam_bateman".
3027     The wrong behaviour will still be used for all levels or tapes that were
3028     created/recorded with it. An example for this is tape 023 from the level
3029     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3030   */
3031
3032   boolean use_amoeba_dropping_cannot_fall_bug =
3033     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3034       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3035      (tape.playing &&
3036       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3037       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3038
3039   /*
3040     Summary of bugfix/change:
3041     Fixed move speed of elements entering or leaving magic wall.
3042
3043     Fixed/changed in version:
3044     2.0.1
3045
3046     Description:
3047     Before 2.0.1, move speed of elements entering or leaving magic wall was
3048     twice as fast as it is now.
3049     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3050
3051     Affected levels/tapes:
3052     The first condition is generally needed for all levels/tapes before version
3053     2.0.1, which might use the old behaviour before it was changed; known tapes
3054     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3055     The second condition is an exception from the above case and is needed for
3056     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3057     above, but before it was known that this change would break tapes like the
3058     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3059     although the engine version while recording maybe was before 2.0.1. There
3060     are a lot of tapes that are affected by this exception, like tape 006 from
3061     the level set "rnd_conor_mancone".
3062   */
3063
3064   boolean use_old_move_stepsize_for_magic_wall =
3065     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3066      !(tape.playing &&
3067        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3068        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3069
3070   /*
3071     Summary of bugfix/change:
3072     Fixed handling for custom elements that change when pushed by the player.
3073
3074     Fixed/changed in version:
3075     3.1.0
3076
3077     Description:
3078     Before 3.1.0, custom elements that "change when pushing" changed directly
3079     after the player started pushing them (until then handled in "DigField()").
3080     Since 3.1.0, these custom elements are not changed until the "pushing"
3081     move of the element is finished (now handled in "ContinueMoving()").
3082
3083     Affected levels/tapes:
3084     The first condition is generally needed for all levels/tapes before version
3085     3.1.0, which might use the old behaviour before it was changed; known tapes
3086     that are affected are some tapes from the level set "Walpurgis Gardens" by
3087     Jamie Cullen.
3088     The second condition is an exception from the above case and is needed for
3089     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3090     above (including some development versions of 3.1.0), but before it was
3091     known that this change would break tapes like the above and was fixed in
3092     3.1.1, so that the changed behaviour was active although the engine version
3093     while recording maybe was before 3.1.0. There is at least one tape that is
3094     affected by this exception, which is the tape for the one-level set "Bug
3095     Machine" by Juergen Bonhagen.
3096   */
3097
3098   game.use_change_when_pushing_bug =
3099     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3100      !(tape.playing &&
3101        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3102        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3103
3104   /*
3105     Summary of bugfix/change:
3106     Fixed handling for blocking the field the player leaves when moving.
3107
3108     Fixed/changed in version:
3109     3.1.1
3110
3111     Description:
3112     Before 3.1.1, when "block last field when moving" was enabled, the field
3113     the player is leaving when moving was blocked for the time of the move,
3114     and was directly unblocked afterwards. This resulted in the last field
3115     being blocked for exactly one less than the number of frames of one player
3116     move. Additionally, even when blocking was disabled, the last field was
3117     blocked for exactly one frame.
3118     Since 3.1.1, due to changes in player movement handling, the last field
3119     is not blocked at all when blocking is disabled. When blocking is enabled,
3120     the last field is blocked for exactly the number of frames of one player
3121     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3122     last field is blocked for exactly one more than the number of frames of
3123     one player move.
3124
3125     Affected levels/tapes:
3126     (!!! yet to be determined -- probably many !!!)
3127   */
3128
3129   game.use_block_last_field_bug =
3130     (game.engine_version < VERSION_IDENT(3,1,1,0));
3131
3132   /* various special flags and settings for native Emerald Mine game engine */
3133
3134   game_em.use_single_button =
3135     (game.engine_version > VERSION_IDENT(4,0,0,2));
3136
3137   game_em.use_push_delay =
3138     (game.engine_version > VERSION_IDENT(4,3,7,1));
3139
3140   game_em.use_snap_key_bug =
3141     (game.engine_version < VERSION_IDENT(4,0,1,0));
3142
3143   game_em.use_random_bug =
3144     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3145
3146   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3147
3148   game_em.use_old_explosions            = use_old_em_engine;
3149   game_em.use_old_android               = use_old_em_engine;
3150   game_em.use_old_push_elements         = use_old_em_engine;
3151   game_em.use_old_push_into_acid        = use_old_em_engine;
3152
3153   game_em.use_wrap_around               = !use_old_em_engine;
3154
3155   // --------------------------------------------------------------------------
3156
3157   // set maximal allowed number of custom element changes per game frame
3158   game.max_num_changes_per_frame = 1;
3159
3160   // default scan direction: scan playfield from top/left to bottom/right
3161   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3162
3163   // dynamically adjust element properties according to game engine version
3164   InitElementPropertiesEngine(game.engine_version);
3165
3166   // ---------- initialize special element properties -------------------------
3167
3168   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3169   if (use_amoeba_dropping_cannot_fall_bug)
3170     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3171
3172   // ---------- initialize player's initial move delay ------------------------
3173
3174   // dynamically adjust player properties according to level information
3175   for (i = 0; i < MAX_PLAYERS; i++)
3176     game.initial_move_delay_value[i] =
3177       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3178
3179   // dynamically adjust player properties according to game engine version
3180   for (i = 0; i < MAX_PLAYERS; i++)
3181     game.initial_move_delay[i] =
3182       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3183        game.initial_move_delay_value[i] : 0);
3184
3185   // ---------- initialize player's initial push delay ------------------------
3186
3187   // dynamically adjust player properties according to game engine version
3188   game.initial_push_delay_value =
3189     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3190
3191   // ---------- initialize changing elements ----------------------------------
3192
3193   // initialize changing elements information
3194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3195   {
3196     struct ElementInfo *ei = &element_info[i];
3197
3198     // this pointer might have been changed in the level editor
3199     ei->change = &ei->change_page[0];
3200
3201     if (!IS_CUSTOM_ELEMENT(i))
3202     {
3203       ei->change->target_element = EL_EMPTY_SPACE;
3204       ei->change->delay_fixed = 0;
3205       ei->change->delay_random = 0;
3206       ei->change->delay_frames = 1;
3207     }
3208
3209     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3210     {
3211       ei->has_change_event[j] = FALSE;
3212
3213       ei->event_page_nr[j] = 0;
3214       ei->event_page[j] = &ei->change_page[0];
3215     }
3216   }
3217
3218   // add changing elements from pre-defined list
3219   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3220   {
3221     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3222     struct ElementInfo *ei = &element_info[ch_delay->element];
3223
3224     ei->change->target_element       = ch_delay->target_element;
3225     ei->change->delay_fixed          = ch_delay->change_delay;
3226
3227     ei->change->pre_change_function  = ch_delay->pre_change_function;
3228     ei->change->change_function      = ch_delay->change_function;
3229     ei->change->post_change_function = ch_delay->post_change_function;
3230
3231     ei->change->can_change = TRUE;
3232     ei->change->can_change_or_has_action = TRUE;
3233
3234     ei->has_change_event[CE_DELAY] = TRUE;
3235
3236     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3237     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3238   }
3239
3240   // ---------- initialize if element can trigger global animations -----------
3241
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243   {
3244     struct ElementInfo *ei = &element_info[i];
3245
3246     ei->has_anim_event = FALSE;
3247   }
3248
3249   InitGlobalAnimEventsForCustomElements();
3250
3251   // ---------- initialize internal run-time variables ------------------------
3252
3253   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3254   {
3255     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       ei->change_page[j].can_change_or_has_action =
3260         (ei->change_page[j].can_change |
3261          ei->change_page[j].has_action);
3262     }
3263   }
3264
3265   // add change events from custom element configuration
3266   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3267   {
3268     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3269
3270     for (j = 0; j < ei->num_change_pages; j++)
3271     {
3272       if (!ei->change_page[j].can_change_or_has_action)
3273         continue;
3274
3275       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3276       {
3277         // only add event page for the first page found with this event
3278         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3279         {
3280           ei->has_change_event[k] = TRUE;
3281
3282           ei->event_page_nr[k] = j;
3283           ei->event_page[k] = &ei->change_page[j];
3284         }
3285       }
3286     }
3287   }
3288
3289   // ---------- initialize reference elements in change conditions ------------
3290
3291   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3292   {
3293     int element = EL_CUSTOM_START + i;
3294     struct ElementInfo *ei = &element_info[element];
3295
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       int trigger_element = ei->change_page[j].initial_trigger_element;
3299
3300       if (trigger_element >= EL_PREV_CE_8 &&
3301           trigger_element <= EL_NEXT_CE_8)
3302         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3303
3304       ei->change_page[j].trigger_element = trigger_element;
3305     }
3306   }
3307
3308   // ---------- initialize run-time trigger player and element ----------------
3309
3310   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3311   {
3312     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3313
3314     for (j = 0; j < ei->num_change_pages; j++)
3315     {
3316       struct ElementChangeInfo *change = &ei->change_page[j];
3317
3318       change->actual_trigger_element = EL_EMPTY;
3319       change->actual_trigger_player = EL_EMPTY;
3320       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3321       change->actual_trigger_side = CH_SIDE_NONE;
3322       change->actual_trigger_ce_value = 0;
3323       change->actual_trigger_ce_score = 0;
3324       change->actual_trigger_x = -1;
3325       change->actual_trigger_y = -1;
3326     }
3327   }
3328
3329   // ---------- initialize trigger events -------------------------------------
3330
3331   // initialize trigger events information
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3334       trigger_events[i][j] = FALSE;
3335
3336   // add trigger events from element change event properties
3337   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3338   {
3339     struct ElementInfo *ei = &element_info[i];
3340
3341     for (j = 0; j < ei->num_change_pages; j++)
3342     {
3343       struct ElementChangeInfo *change = &ei->change_page[j];
3344
3345       if (!change->can_change_or_has_action)
3346         continue;
3347
3348       if (change->has_event[CE_BY_OTHER_ACTION])
3349       {
3350         int trigger_element = change->trigger_element;
3351
3352         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3353         {
3354           if (change->has_event[k])
3355           {
3356             if (IS_GROUP_ELEMENT(trigger_element))
3357             {
3358               struct ElementGroupInfo *group =
3359                 element_info[trigger_element].group;
3360
3361               for (l = 0; l < group->num_elements_resolved; l++)
3362                 trigger_events[group->element_resolved[l]][k] = TRUE;
3363             }
3364             else if (trigger_element == EL_ANY_ELEMENT)
3365               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3366                 trigger_events[l][k] = TRUE;
3367             else
3368               trigger_events[trigger_element][k] = TRUE;
3369           }
3370         }
3371       }
3372     }
3373   }
3374
3375   // ---------- initialize push delay -----------------------------------------
3376
3377   // initialize push delay values to default
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379   {
3380     if (!IS_CUSTOM_ELEMENT(i))
3381     {
3382       // set default push delay values (corrected since version 3.0.7-1)
3383       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3384       {
3385         element_info[i].push_delay_fixed = 2;
3386         element_info[i].push_delay_random = 8;
3387       }
3388       else
3389       {
3390         element_info[i].push_delay_fixed = 8;
3391         element_info[i].push_delay_random = 8;
3392       }
3393     }
3394   }
3395
3396   // set push delay value for certain elements from pre-defined list
3397   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3398   {
3399     int e = push_delay_list[i].element;
3400
3401     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3402     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3403   }
3404
3405   // set push delay value for Supaplex elements for newer engine versions
3406   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3407   {
3408     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3409     {
3410       if (IS_SP_ELEMENT(i))
3411       {
3412         // set SP push delay to just enough to push under a falling zonk
3413         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3414
3415         element_info[i].push_delay_fixed  = delay;
3416         element_info[i].push_delay_random = 0;
3417       }
3418     }
3419   }
3420
3421   // ---------- initialize move stepsize --------------------------------------
3422
3423   // initialize move stepsize values to default
3424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     if (!IS_CUSTOM_ELEMENT(i))
3426       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3427
3428   // set move stepsize value for certain elements from pre-defined list
3429   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3430   {
3431     int e = move_stepsize_list[i].element;
3432
3433     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3434
3435     // set move stepsize value for certain elements for older engine versions
3436     if (use_old_move_stepsize_for_magic_wall)
3437     {
3438       if (e == EL_MAGIC_WALL_FILLING ||
3439           e == EL_MAGIC_WALL_EMPTYING ||
3440           e == EL_BD_MAGIC_WALL_FILLING ||
3441           e == EL_BD_MAGIC_WALL_EMPTYING)
3442         element_info[e].move_stepsize *= 2;
3443     }
3444   }
3445
3446   // ---------- initialize collect score --------------------------------------
3447
3448   // initialize collect score values for custom elements from initial value
3449   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3450     if (IS_CUSTOM_ELEMENT(i))
3451       element_info[i].collect_score = element_info[i].collect_score_initial;
3452
3453   // ---------- initialize collect count --------------------------------------
3454
3455   // initialize collect count values for non-custom elements
3456   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457     if (!IS_CUSTOM_ELEMENT(i))
3458       element_info[i].collect_count_initial = 0;
3459
3460   // add collect count values for all elements from pre-defined list
3461   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3462     element_info[collect_count_list[i].element].collect_count_initial =
3463       collect_count_list[i].count;
3464
3465   // ---------- initialize access direction -----------------------------------
3466
3467   // initialize access direction values to default (access from every side)
3468   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3469     if (!IS_CUSTOM_ELEMENT(i))
3470       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3471
3472   // set access direction value for certain elements from pre-defined list
3473   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3474     element_info[access_direction_list[i].element].access_direction =
3475       access_direction_list[i].direction;
3476
3477   // ---------- initialize explosion content ----------------------------------
3478   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3479   {
3480     if (IS_CUSTOM_ELEMENT(i))
3481       continue;
3482
3483     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3484     {
3485       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3486
3487       element_info[i].content.e[x][y] =
3488         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3489          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3490          i == EL_PLAYER_3 ? EL_EMERALD :
3491          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3492          i == EL_MOLE ? EL_EMERALD_RED :
3493          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3494          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3495          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3496          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3497          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3498          i == EL_WALL_EMERALD ? EL_EMERALD :
3499          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3500          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3501          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3502          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3503          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3504          i == EL_WALL_PEARL ? EL_PEARL :
3505          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3506          EL_EMPTY);
3507     }
3508   }
3509
3510   // ---------- initialize recursion detection --------------------------------
3511   recursion_loop_depth = 0;
3512   recursion_loop_detected = FALSE;
3513   recursion_loop_element = EL_UNDEFINED;
3514
3515   // ---------- initialize graphics engine ------------------------------------
3516   game.scroll_delay_value =
3517     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3518      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3519      !setup.forced_scroll_delay           ? 0 :
3520      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3521   if (game.forced_scroll_delay_value == -1)
3522     game.scroll_delay_value =
3523       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3524
3525   // ---------- initialize game engine snapshots ------------------------------
3526   for (i = 0; i < MAX_PLAYERS; i++)
3527     game.snapshot.last_action[i] = 0;
3528   game.snapshot.changed_action = FALSE;
3529   game.snapshot.collected_item = FALSE;
3530   game.snapshot.mode =
3531     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3532      SNAPSHOT_MODE_EVERY_STEP :
3533      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3534      SNAPSHOT_MODE_EVERY_MOVE :
3535      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3536      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3537   game.snapshot.save_snapshot = FALSE;
3538
3539   // ---------- initialize level time for Supaplex engine ---------------------
3540   // Supaplex levels with time limit currently unsupported -- should be added
3541   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3542     level.time = 0;
3543
3544   // ---------- initialize flags for handling game actions --------------------
3545
3546   // set flags for game actions to default values
3547   game.use_key_actions = TRUE;
3548   game.use_mouse_actions = FALSE;
3549
3550   // when using Mirror Magic game engine, handle mouse events only
3551   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3552   {
3553     game.use_key_actions = FALSE;
3554     game.use_mouse_actions = TRUE;
3555   }
3556
3557   // check for custom elements with mouse click events
3558   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3559   {
3560     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3561     {
3562       int element = EL_CUSTOM_START + i;
3563
3564       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3565           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3566           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3567           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3568         game.use_mouse_actions = TRUE;
3569     }
3570   }
3571 }
3572
3573 static int get_num_special_action(int element, int action_first,
3574                                   int action_last)
3575 {
3576   int num_special_action = 0;
3577   int i, j;
3578
3579   for (i = action_first; i <= action_last; i++)
3580   {
3581     boolean found = FALSE;
3582
3583     for (j = 0; j < NUM_DIRECTIONS; j++)
3584       if (el_act_dir2img(element, i, j) !=
3585           el_act_dir2img(element, ACTION_DEFAULT, j))
3586         found = TRUE;
3587
3588     if (found)
3589       num_special_action++;
3590     else
3591       break;
3592   }
3593
3594   return num_special_action;
3595 }
3596
3597
3598 // ============================================================================
3599 // InitGame()
3600 // ----------------------------------------------------------------------------
3601 // initialize and start new game
3602 // ============================================================================
3603
3604 #if DEBUG_INIT_PLAYER
3605 static void DebugPrintPlayerStatus(char *message)
3606 {
3607   int i;
3608
3609   if (!options.debug)
3610     return;
3611
3612   Debug("game:init:player", "%s:", message);
3613
3614   for (i = 0; i < MAX_PLAYERS; i++)
3615   {
3616     struct PlayerInfo *player = &stored_player[i];
3617
3618     Debug("game:init:player",
3619           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3620           i + 1,
3621           player->present,
3622           player->connected,
3623           player->connected_locally,
3624           player->connected_network,
3625           player->active,
3626           (local_player == player ? " (local player)" : ""));
3627   }
3628 }
3629 #endif
3630
3631 void InitGame(void)
3632 {
3633   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3634   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3635   int fade_mask = REDRAW_FIELD;
3636   boolean restarting = (game_status == GAME_MODE_PLAYING);
3637   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3638   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3639   int initial_move_dir = MV_DOWN;
3640   int i, j, x, y;
3641
3642   // required here to update video display before fading (FIX THIS)
3643   DrawMaskedBorder(REDRAW_DOOR_2);
3644
3645   if (!game.restart_level)
3646     CloseDoor(DOOR_CLOSE_1);
3647
3648   if (restarting)
3649   {
3650     // force fading out global animations displayed during game play
3651     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3652   }
3653   else
3654   {
3655     SetGameStatus(GAME_MODE_PLAYING);
3656   }
3657
3658   if (level_editor_test_game)
3659     FadeSkipNextFadeOut();
3660   else
3661     FadeSetEnterScreen();
3662
3663   if (CheckFadeAll())
3664     fade_mask = REDRAW_ALL;
3665
3666   FadeLevelSoundsAndMusic();
3667
3668   ExpireSoundLoops(TRUE);
3669
3670   FadeOut(fade_mask);
3671
3672   if (restarting)
3673   {
3674     // force restarting global animations displayed during game play
3675     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3676
3677     // this is required for "transforming" fade modes like cross-fading
3678     // (else global animations will be stopped, but not restarted here)
3679     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3680
3681     SetGameStatus(GAME_MODE_PLAYING);
3682   }
3683
3684   if (level_editor_test_game)
3685     FadeSkipNextFadeIn();
3686
3687   // needed if different viewport properties defined for playing
3688   ChangeViewportPropertiesIfNeeded();
3689
3690   ClearField();
3691
3692   DrawCompleteVideoDisplay();
3693
3694   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3695
3696   InitGameEngine();
3697   InitGameControlValues();
3698
3699   if (tape.recording)
3700   {
3701     // initialize tape actions from game when recording tape
3702     tape.use_key_actions   = game.use_key_actions;
3703     tape.use_mouse_actions = game.use_mouse_actions;
3704
3705     // initialize visible playfield size when recording tape (for team mode)
3706     tape.scr_fieldx = SCR_FIELDX;
3707     tape.scr_fieldy = SCR_FIELDY;
3708   }
3709
3710   // don't play tapes over network
3711   network_playing = (network.enabled && !tape.playing);
3712
3713   for (i = 0; i < MAX_PLAYERS; i++)
3714   {
3715     struct PlayerInfo *player = &stored_player[i];
3716
3717     player->index_nr = i;
3718     player->index_bit = (1 << i);
3719     player->element_nr = EL_PLAYER_1 + i;
3720
3721     player->present = FALSE;
3722     player->active = FALSE;
3723     player->mapped = FALSE;
3724
3725     player->killed = FALSE;
3726     player->reanimated = FALSE;
3727     player->buried = FALSE;
3728
3729     player->action = 0;
3730     player->effective_action = 0;
3731     player->programmed_action = 0;
3732     player->snap_action = 0;
3733
3734     player->mouse_action.lx = 0;
3735     player->mouse_action.ly = 0;
3736     player->mouse_action.button = 0;
3737     player->mouse_action.button_hint = 0;
3738
3739     player->effective_mouse_action.lx = 0;
3740     player->effective_mouse_action.ly = 0;
3741     player->effective_mouse_action.button = 0;
3742     player->effective_mouse_action.button_hint = 0;
3743
3744     for (j = 0; j < MAX_NUM_KEYS; j++)
3745       player->key[j] = FALSE;
3746
3747     player->num_white_keys = 0;
3748
3749     player->dynabomb_count = 0;
3750     player->dynabomb_size = 1;
3751     player->dynabombs_left = 0;
3752     player->dynabomb_xl = FALSE;
3753
3754     player->MovDir = initial_move_dir;
3755     player->MovPos = 0;
3756     player->GfxPos = 0;
3757     player->GfxDir = initial_move_dir;
3758     player->GfxAction = ACTION_DEFAULT;
3759     player->Frame = 0;
3760     player->StepFrame = 0;
3761
3762     player->initial_element = player->element_nr;
3763     player->artwork_element =
3764       (level.use_artwork_element[i] ? level.artwork_element[i] :
3765        player->element_nr);
3766     player->use_murphy = FALSE;
3767
3768     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3769     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3770
3771     player->gravity = level.initial_player_gravity[i];
3772
3773     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3774
3775     player->actual_frame_counter.count = 0;
3776     player->actual_frame_counter.value = 1;
3777
3778     player->step_counter = 0;
3779
3780     player->last_move_dir = initial_move_dir;
3781
3782     player->is_active = FALSE;
3783
3784     player->is_waiting = FALSE;
3785     player->is_moving = FALSE;
3786     player->is_auto_moving = FALSE;
3787     player->is_digging = FALSE;
3788     player->is_snapping = FALSE;
3789     player->is_collecting = FALSE;
3790     player->is_pushing = FALSE;
3791     player->is_switching = FALSE;
3792     player->is_dropping = FALSE;
3793     player->is_dropping_pressed = FALSE;
3794
3795     player->is_bored = FALSE;
3796     player->is_sleeping = FALSE;
3797
3798     player->was_waiting = TRUE;
3799     player->was_moving = FALSE;
3800     player->was_snapping = FALSE;
3801     player->was_dropping = FALSE;
3802
3803     player->force_dropping = FALSE;
3804
3805     player->frame_counter_bored = -1;
3806     player->frame_counter_sleeping = -1;
3807
3808     player->anim_delay_counter = 0;
3809     player->post_delay_counter = 0;
3810
3811     player->dir_waiting = initial_move_dir;
3812     player->action_waiting = ACTION_DEFAULT;
3813     player->last_action_waiting = ACTION_DEFAULT;
3814     player->special_action_bored = ACTION_DEFAULT;
3815     player->special_action_sleeping = ACTION_DEFAULT;
3816
3817     player->switch_x = -1;
3818     player->switch_y = -1;
3819
3820     player->drop_x = -1;
3821     player->drop_y = -1;
3822
3823     player->show_envelope = 0;
3824
3825     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3826
3827     player->push_delay       = -1;      // initialized when pushing starts
3828     player->push_delay_value = game.initial_push_delay_value;
3829
3830     player->drop_delay = 0;
3831     player->drop_pressed_delay = 0;
3832
3833     player->last_jx = -1;
3834     player->last_jy = -1;
3835     player->jx = -1;
3836     player->jy = -1;
3837
3838     player->shield_normal_time_left = 0;
3839     player->shield_deadly_time_left = 0;
3840
3841     player->last_removed_element = EL_UNDEFINED;
3842
3843     player->inventory_infinite_element = EL_UNDEFINED;
3844     player->inventory_size = 0;
3845
3846     if (level.use_initial_inventory[i])
3847     {
3848       for (j = 0; j < level.initial_inventory_size[i]; j++)
3849       {
3850         int element = level.initial_inventory_content[i][j];
3851         int collect_count = element_info[element].collect_count_initial;
3852         int k;
3853
3854         if (!IS_CUSTOM_ELEMENT(element))
3855           collect_count = 1;
3856
3857         if (collect_count == 0)
3858           player->inventory_infinite_element = element;
3859         else
3860           for (k = 0; k < collect_count; k++)
3861             if (player->inventory_size < MAX_INVENTORY_SIZE)
3862               player->inventory_element[player->inventory_size++] = element;
3863       }
3864     }
3865
3866     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3867     SnapField(player, 0, 0);
3868
3869     map_player_action[i] = i;
3870   }
3871
3872   network_player_action_received = FALSE;
3873
3874   // initial null action
3875   if (network_playing)
3876     SendToServer_MovePlayer(MV_NONE);
3877
3878   FrameCounter = 0;
3879   TimeFrames = 0;
3880   TimePlayed = 0;
3881   TimeLeft = level.time;
3882
3883   TapeTimeFrames = 0;
3884   TapeTime = 0;
3885
3886   ScreenMovDir = MV_NONE;
3887   ScreenMovPos = 0;
3888   ScreenGfxPos = 0;
3889
3890   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3891
3892   game.robot_wheel_x = -1;
3893   game.robot_wheel_y = -1;
3894
3895   game.exit_x = -1;
3896   game.exit_y = -1;
3897
3898   game.all_players_gone = FALSE;
3899
3900   game.LevelSolved = FALSE;
3901   game.GameOver = FALSE;
3902
3903   game.GamePlayed = !tape.playing;
3904
3905   game.LevelSolved_GameWon = FALSE;
3906   game.LevelSolved_GameEnd = FALSE;
3907   game.LevelSolved_SaveTape = FALSE;
3908   game.LevelSolved_SaveScore = FALSE;
3909
3910   game.LevelSolved_CountingTime = 0;
3911   game.LevelSolved_CountingScore = 0;
3912   game.LevelSolved_CountingHealth = 0;
3913
3914   game.RestartGameRequested = FALSE;
3915
3916   game.panel.active = TRUE;
3917
3918   game.no_level_time_limit = (level.time == 0);
3919   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3920
3921   game.yamyam_content_nr = 0;
3922   game.robot_wheel_active = FALSE;
3923   game.magic_wall_active = FALSE;
3924   game.magic_wall_time_left = 0;
3925   game.light_time_left = 0;
3926   game.timegate_time_left = 0;
3927   game.switchgate_pos = 0;
3928   game.wind_direction = level.wind_direction_initial;
3929
3930   game.time_final = 0;
3931   game.score_time_final = 0;
3932
3933   game.score = 0;
3934   game.score_final = 0;
3935
3936   game.health = MAX_HEALTH;
3937   game.health_final = MAX_HEALTH;
3938
3939   game.gems_still_needed = level.gems_needed;
3940   game.sokoban_fields_still_needed = 0;
3941   game.sokoban_objects_still_needed = 0;
3942   game.lights_still_needed = 0;
3943   game.players_still_needed = 0;
3944   game.friends_still_needed = 0;
3945
3946   game.lenses_time_left = 0;
3947   game.magnify_time_left = 0;
3948
3949   game.ball_active = level.ball_active_initial;
3950   game.ball_content_nr = 0;
3951
3952   game.explosions_delayed = TRUE;
3953
3954   game.envelope_active = FALSE;
3955
3956   // special case: set custom artwork setting to initial value
3957   game.use_masked_elements = game.use_masked_elements_initial;
3958
3959   for (i = 0; i < NUM_BELTS; i++)
3960   {
3961     game.belt_dir[i] = MV_NONE;
3962     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3963   }
3964
3965   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3966     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3967
3968 #if DEBUG_INIT_PLAYER
3969   DebugPrintPlayerStatus("Player status at level initialization");
3970 #endif
3971
3972   SCAN_PLAYFIELD(x, y)
3973   {
3974     Tile[x][y] = Last[x][y] = level.field[x][y];
3975     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3976     ChangeDelay[x][y] = 0;
3977     ChangePage[x][y] = -1;
3978     CustomValue[x][y] = 0;              // initialized in InitField()
3979     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3980     AmoebaNr[x][y] = 0;
3981     WasJustMoving[x][y] = 0;
3982     WasJustFalling[x][y] = 0;
3983     CheckCollision[x][y] = 0;
3984     CheckImpact[x][y] = 0;
3985     Stop[x][y] = FALSE;
3986     Pushed[x][y] = FALSE;
3987
3988     ChangeCount[x][y] = 0;
3989     ChangeEvent[x][y] = -1;
3990
3991     ExplodePhase[x][y] = 0;
3992     ExplodeDelay[x][y] = 0;
3993     ExplodeField[x][y] = EX_TYPE_NONE;
3994
3995     RunnerVisit[x][y] = 0;
3996     PlayerVisit[x][y] = 0;
3997
3998     GfxFrame[x][y] = 0;
3999     GfxRandom[x][y] = INIT_GFX_RANDOM();
4000     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4001     GfxElement[x][y] = EL_UNDEFINED;
4002     GfxElementEmpty[x][y] = EL_EMPTY;
4003     GfxAction[x][y] = ACTION_DEFAULT;
4004     GfxDir[x][y] = MV_NONE;
4005     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4006   }
4007
4008   SCAN_PLAYFIELD(x, y)
4009   {
4010     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4011       emulate_bd = FALSE;
4012     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4013       emulate_sp = FALSE;
4014
4015     InitField(x, y, TRUE);
4016
4017     ResetGfxAnimation(x, y);
4018   }
4019
4020   InitBeltMovement();
4021
4022   // required if level does not contain any "empty space" element
4023   if (element_info[EL_EMPTY].use_gfx_element)
4024     game.use_masked_elements = TRUE;
4025
4026   for (i = 0; i < MAX_PLAYERS; i++)
4027   {
4028     struct PlayerInfo *player = &stored_player[i];
4029
4030     // set number of special actions for bored and sleeping animation
4031     player->num_special_action_bored =
4032       get_num_special_action(player->artwork_element,
4033                              ACTION_BORING_1, ACTION_BORING_LAST);
4034     player->num_special_action_sleeping =
4035       get_num_special_action(player->artwork_element,
4036                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4037   }
4038
4039   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4040                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4041
4042   // initialize type of slippery elements
4043   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4044   {
4045     if (!IS_CUSTOM_ELEMENT(i))
4046     {
4047       // default: elements slip down either to the left or right randomly
4048       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4049
4050       // SP style elements prefer to slip down on the left side
4051       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4052         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4053
4054       // BD style elements prefer to slip down on the left side
4055       if (game.emulation == EMU_BOULDERDASH)
4056         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4057     }
4058   }
4059
4060   // initialize explosion and ignition delay
4061   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4062   {
4063     if (!IS_CUSTOM_ELEMENT(i))
4064     {
4065       int num_phase = 8;
4066       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4067                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4068                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4069       int last_phase = (num_phase + 1) * delay;
4070       int half_phase = (num_phase / 2) * delay;
4071
4072       element_info[i].explosion_delay = last_phase - 1;
4073       element_info[i].ignition_delay = half_phase;
4074
4075       if (i == EL_BLACK_ORB)
4076         element_info[i].ignition_delay = 1;
4077     }
4078   }
4079
4080   // correct non-moving belts to start moving left
4081   for (i = 0; i < NUM_BELTS; i++)
4082     if (game.belt_dir[i] == MV_NONE)
4083       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4084
4085 #if USE_NEW_PLAYER_ASSIGNMENTS
4086   // use preferred player also in local single-player mode
4087   if (!network.enabled && !game.team_mode)
4088   {
4089     int new_index_nr = setup.network_player_nr;
4090
4091     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4092     {
4093       for (i = 0; i < MAX_PLAYERS; i++)
4094         stored_player[i].connected_locally = FALSE;
4095
4096       stored_player[new_index_nr].connected_locally = TRUE;
4097     }
4098   }
4099
4100   for (i = 0; i < MAX_PLAYERS; i++)
4101   {
4102     stored_player[i].connected = FALSE;
4103
4104     // in network game mode, the local player might not be the first player
4105     if (stored_player[i].connected_locally)
4106       local_player = &stored_player[i];
4107   }
4108
4109   if (!network.enabled)
4110     local_player->connected = TRUE;
4111
4112   if (tape.playing)
4113   {
4114     for (i = 0; i < MAX_PLAYERS; i++)
4115       stored_player[i].connected = tape.player_participates[i];
4116   }
4117   else if (network.enabled)
4118   {
4119     // add team mode players connected over the network (needed for correct
4120     // assignment of player figures from level to locally playing players)
4121
4122     for (i = 0; i < MAX_PLAYERS; i++)
4123       if (stored_player[i].connected_network)
4124         stored_player[i].connected = TRUE;
4125   }
4126   else if (game.team_mode)
4127   {
4128     // try to guess locally connected team mode players (needed for correct
4129     // assignment of player figures from level to locally playing players)
4130
4131     for (i = 0; i < MAX_PLAYERS; i++)
4132       if (setup.input[i].use_joystick ||
4133           setup.input[i].key.left != KSYM_UNDEFINED)
4134         stored_player[i].connected = TRUE;
4135   }
4136
4137 #if DEBUG_INIT_PLAYER
4138   DebugPrintPlayerStatus("Player status after level initialization");
4139 #endif
4140
4141 #if DEBUG_INIT_PLAYER
4142   Debug("game:init:player", "Reassigning players ...");
4143 #endif
4144
4145   // check if any connected player was not found in playfield
4146   for (i = 0; i < MAX_PLAYERS; i++)
4147   {
4148     struct PlayerInfo *player = &stored_player[i];
4149
4150     if (player->connected && !player->present)
4151     {
4152       struct PlayerInfo *field_player = NULL;
4153
4154 #if DEBUG_INIT_PLAYER
4155       Debug("game:init:player",
4156             "- looking for field player for player %d ...", i + 1);
4157 #endif
4158
4159       // assign first free player found that is present in the playfield
4160
4161       // first try: look for unmapped playfield player that is not connected
4162       for (j = 0; j < MAX_PLAYERS; j++)
4163         if (field_player == NULL &&
4164             stored_player[j].present &&
4165             !stored_player[j].mapped &&
4166             !stored_player[j].connected)
4167           field_player = &stored_player[j];
4168
4169       // second try: look for *any* unmapped playfield player
4170       for (j = 0; j < MAX_PLAYERS; j++)
4171         if (field_player == NULL &&
4172             stored_player[j].present &&
4173             !stored_player[j].mapped)
4174           field_player = &stored_player[j];
4175
4176       if (field_player != NULL)
4177       {
4178         int jx = field_player->jx, jy = field_player->jy;
4179
4180 #if DEBUG_INIT_PLAYER
4181         Debug("game:init:player", "- found player %d",
4182               field_player->index_nr + 1);
4183 #endif
4184
4185         player->present = FALSE;
4186         player->active = FALSE;
4187
4188         field_player->present = TRUE;
4189         field_player->active = TRUE;
4190
4191         /*
4192         player->initial_element = field_player->initial_element;
4193         player->artwork_element = field_player->artwork_element;
4194
4195         player->block_last_field       = field_player->block_last_field;
4196         player->block_delay_adjustment = field_player->block_delay_adjustment;
4197         */
4198
4199         StorePlayer[jx][jy] = field_player->element_nr;
4200
4201         field_player->jx = field_player->last_jx = jx;
4202         field_player->jy = field_player->last_jy = jy;
4203
4204         if (local_player == player)
4205           local_player = field_player;
4206
4207         map_player_action[field_player->index_nr] = i;
4208
4209         field_player->mapped = TRUE;
4210
4211 #if DEBUG_INIT_PLAYER
4212         Debug("game:init:player", "- map_player_action[%d] == %d",
4213               field_player->index_nr + 1, i + 1);
4214 #endif
4215       }
4216     }
4217
4218     if (player->connected && player->present)
4219       player->mapped = TRUE;
4220   }
4221
4222 #if DEBUG_INIT_PLAYER
4223   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4224 #endif
4225
4226 #else
4227
4228   // check if any connected player was not found in playfield
4229   for (i = 0; i < MAX_PLAYERS; i++)
4230   {
4231     struct PlayerInfo *player = &stored_player[i];
4232
4233     if (player->connected && !player->present)
4234     {
4235       for (j = 0; j < MAX_PLAYERS; j++)
4236       {
4237         struct PlayerInfo *field_player = &stored_player[j];
4238         int jx = field_player->jx, jy = field_player->jy;
4239
4240         // assign first free player found that is present in the playfield
4241         if (field_player->present && !field_player->connected)
4242         {
4243           player->present = TRUE;
4244           player->active = TRUE;
4245
4246           field_player->present = FALSE;
4247           field_player->active = FALSE;
4248
4249           player->initial_element = field_player->initial_element;
4250           player->artwork_element = field_player->artwork_element;
4251
4252           player->block_last_field       = field_player->block_last_field;
4253           player->block_delay_adjustment = field_player->block_delay_adjustment;
4254
4255           StorePlayer[jx][jy] = player->element_nr;
4256
4257           player->jx = player->last_jx = jx;
4258           player->jy = player->last_jy = jy;
4259
4260           break;
4261         }
4262       }
4263     }
4264   }
4265 #endif
4266
4267 #if 0
4268   Debug("game:init:player", "local_player->present == %d",
4269         local_player->present);
4270 #endif
4271
4272   // set focus to local player for network games, else to all players
4273   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4274   game.centered_player_nr_next = game.centered_player_nr;
4275   game.set_centered_player = FALSE;
4276   game.set_centered_player_wrap = FALSE;
4277
4278   if (network_playing && tape.recording)
4279   {
4280     // store client dependent player focus when recording network games
4281     tape.centered_player_nr_next = game.centered_player_nr_next;
4282     tape.set_centered_player = TRUE;
4283   }
4284
4285   if (tape.playing)
4286   {
4287     // when playing a tape, eliminate all players who do not participate
4288
4289 #if USE_NEW_PLAYER_ASSIGNMENTS
4290
4291     if (!game.team_mode)
4292     {
4293       for (i = 0; i < MAX_PLAYERS; i++)
4294       {
4295         if (stored_player[i].active &&
4296             !tape.player_participates[map_player_action[i]])
4297         {
4298           struct PlayerInfo *player = &stored_player[i];
4299           int jx = player->jx, jy = player->jy;
4300
4301 #if DEBUG_INIT_PLAYER
4302           Debug("game:init:player", "Removing player %d at (%d, %d)",
4303                 i + 1, jx, jy);
4304 #endif
4305
4306           player->active = FALSE;
4307           StorePlayer[jx][jy] = 0;
4308           Tile[jx][jy] = EL_EMPTY;
4309         }
4310       }
4311     }
4312
4313 #else
4314
4315     for (i = 0; i < MAX_PLAYERS; i++)
4316     {
4317       if (stored_player[i].active &&
4318           !tape.player_participates[i])
4319       {
4320         struct PlayerInfo *player = &stored_player[i];
4321         int jx = player->jx, jy = player->jy;
4322
4323         player->active = FALSE;
4324         StorePlayer[jx][jy] = 0;
4325         Tile[jx][jy] = EL_EMPTY;
4326       }
4327     }
4328 #endif
4329   }
4330   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4331   {
4332     // when in single player mode, eliminate all but the local player
4333
4334     for (i = 0; i < MAX_PLAYERS; i++)
4335     {
4336       struct PlayerInfo *player = &stored_player[i];
4337
4338       if (player->active && player != local_player)
4339       {
4340         int jx = player->jx, jy = player->jy;
4341
4342         player->active = FALSE;
4343         player->present = FALSE;
4344
4345         StorePlayer[jx][jy] = 0;
4346         Tile[jx][jy] = EL_EMPTY;
4347       }
4348     }
4349   }
4350
4351   for (i = 0; i < MAX_PLAYERS; i++)
4352     if (stored_player[i].active)
4353       game.players_still_needed++;
4354
4355   if (level.solved_by_one_player)
4356     game.players_still_needed = 1;
4357
4358   // when recording the game, store which players take part in the game
4359   if (tape.recording)
4360   {
4361 #if USE_NEW_PLAYER_ASSIGNMENTS
4362     for (i = 0; i < MAX_PLAYERS; i++)
4363       if (stored_player[i].connected)
4364         tape.player_participates[i] = TRUE;
4365 #else
4366     for (i = 0; i < MAX_PLAYERS; i++)
4367       if (stored_player[i].active)
4368         tape.player_participates[i] = TRUE;
4369 #endif
4370   }
4371
4372 #if DEBUG_INIT_PLAYER
4373   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4374 #endif
4375
4376   if (BorderElement == EL_EMPTY)
4377   {
4378     SBX_Left = 0;
4379     SBX_Right = lev_fieldx - SCR_FIELDX;
4380     SBY_Upper = 0;
4381     SBY_Lower = lev_fieldy - SCR_FIELDY;
4382   }
4383   else
4384   {
4385     SBX_Left = -1;
4386     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4387     SBY_Upper = -1;
4388     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4389   }
4390
4391   if (full_lev_fieldx <= SCR_FIELDX)
4392     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4393   if (full_lev_fieldy <= SCR_FIELDY)
4394     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4395
4396   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4397     SBX_Left--;
4398   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4399     SBY_Upper--;
4400
4401   // if local player not found, look for custom element that might create
4402   // the player (make some assumptions about the right custom element)
4403   if (!local_player->present)
4404   {
4405     int start_x = 0, start_y = 0;
4406     int found_rating = 0;
4407     int found_element = EL_UNDEFINED;
4408     int player_nr = local_player->index_nr;
4409
4410     SCAN_PLAYFIELD(x, y)
4411     {
4412       int element = Tile[x][y];
4413       int content;
4414       int xx, yy;
4415       boolean is_player;
4416
4417       if (level.use_start_element[player_nr] &&
4418           level.start_element[player_nr] == element &&
4419           found_rating < 4)
4420       {
4421         start_x = x;
4422         start_y = y;
4423
4424         found_rating = 4;
4425         found_element = element;
4426       }
4427
4428       if (!IS_CUSTOM_ELEMENT(element))
4429         continue;
4430
4431       if (CAN_CHANGE(element))
4432       {
4433         for (i = 0; i < element_info[element].num_change_pages; i++)
4434         {
4435           // check for player created from custom element as single target
4436           content = element_info[element].change_page[i].target_element;
4437           is_player = IS_PLAYER_ELEMENT(content);
4438
4439           if (is_player && (found_rating < 3 ||
4440                             (found_rating == 3 && element < found_element)))
4441           {
4442             start_x = x;
4443             start_y = y;
4444
4445             found_rating = 3;
4446             found_element = element;
4447           }
4448         }
4449       }
4450
4451       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4452       {
4453         // check for player created from custom element as explosion content
4454         content = element_info[element].content.e[xx][yy];
4455         is_player = IS_PLAYER_ELEMENT(content);
4456
4457         if (is_player && (found_rating < 2 ||
4458                           (found_rating == 2 && element < found_element)))
4459         {
4460           start_x = x + xx - 1;
4461           start_y = y + yy - 1;
4462
4463           found_rating = 2;
4464           found_element = element;
4465         }
4466
4467         if (!CAN_CHANGE(element))
4468           continue;
4469
4470         for (i = 0; i < element_info[element].num_change_pages; i++)
4471         {
4472           // check for player created from custom element as extended target
4473           content =
4474             element_info[element].change_page[i].target_content.e[xx][yy];
4475
4476           is_player = IS_PLAYER_ELEMENT(content);
4477
4478           if (is_player && (found_rating < 1 ||
4479                             (found_rating == 1 && element < found_element)))
4480           {
4481             start_x = x + xx - 1;
4482             start_y = y + yy - 1;
4483
4484             found_rating = 1;
4485             found_element = element;
4486           }
4487         }
4488       }
4489     }
4490
4491     scroll_x = SCROLL_POSITION_X(start_x);
4492     scroll_y = SCROLL_POSITION_Y(start_y);
4493   }
4494   else
4495   {
4496     scroll_x = SCROLL_POSITION_X(local_player->jx);
4497     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4498   }
4499
4500   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4501     scroll_x = game.forced_scroll_x;
4502   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4503     scroll_y = game.forced_scroll_y;
4504
4505   // !!! FIX THIS (START) !!!
4506   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4507   {
4508     InitGameEngine_EM();
4509   }
4510   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4511   {
4512     InitGameEngine_SP();
4513   }
4514   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4515   {
4516     InitGameEngine_MM();
4517   }
4518   else
4519   {
4520     DrawLevel(REDRAW_FIELD);
4521     DrawAllPlayers();
4522
4523     // after drawing the level, correct some elements
4524     if (game.timegate_time_left == 0)
4525       CloseAllOpenTimegates();
4526   }
4527
4528   // blit playfield from scroll buffer to normal back buffer for fading in
4529   BlitScreenToBitmap(backbuffer);
4530   // !!! FIX THIS (END) !!!
4531
4532   DrawMaskedBorder(fade_mask);
4533
4534   FadeIn(fade_mask);
4535
4536 #if 1
4537   // full screen redraw is required at this point in the following cases:
4538   // - special editor door undrawn when game was started from level editor
4539   // - drawing area (playfield) was changed and has to be removed completely
4540   redraw_mask = REDRAW_ALL;
4541   BackToFront();
4542 #endif
4543
4544   if (!game.restart_level)
4545   {
4546     // copy default game door content to main double buffer
4547
4548     // !!! CHECK AGAIN !!!
4549     SetPanelBackground();
4550     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4551     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4552   }
4553
4554   SetPanelBackground();
4555   SetDrawBackgroundMask(REDRAW_DOOR_1);
4556
4557   UpdateAndDisplayGameControlValues();
4558
4559   if (!game.restart_level)
4560   {
4561     UnmapGameButtons();
4562     UnmapTapeButtons();
4563
4564     FreeGameButtons();
4565     CreateGameButtons();
4566
4567     MapGameButtons();
4568     MapTapeButtons();
4569
4570     // copy actual game door content to door double buffer for OpenDoor()
4571     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4572
4573     OpenDoor(DOOR_OPEN_ALL);
4574
4575     KeyboardAutoRepeatOffUnlessAutoplay();
4576
4577 #if DEBUG_INIT_PLAYER
4578     DebugPrintPlayerStatus("Player status (final)");
4579 #endif
4580   }
4581
4582   UnmapAllGadgets();
4583
4584   MapGameButtons();
4585   MapTapeButtons();
4586
4587   if (!game.restart_level && !tape.playing)
4588   {
4589     LevelStats_incPlayed(level_nr);
4590
4591     SaveLevelSetup_SeriesInfo();
4592   }
4593
4594   game.restart_level = FALSE;
4595   game.request_active = FALSE;
4596
4597   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4598     InitGameActions_MM();
4599
4600   SaveEngineSnapshotToListInitial();
4601
4602   if (!game.restart_level)
4603   {
4604     PlaySound(SND_GAME_STARTING);
4605
4606     if (setup.sound_music)
4607       PlayLevelMusic();
4608   }
4609
4610   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4611 }
4612
4613 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4614                         int actual_player_x, int actual_player_y)
4615 {
4616   // this is used for non-R'n'D game engines to update certain engine values
4617
4618   // needed to determine if sounds are played within the visible screen area
4619   scroll_x = actual_scroll_x;
4620   scroll_y = actual_scroll_y;
4621
4622   // needed to get player position for "follow finger" playing input method
4623   local_player->jx = actual_player_x;
4624   local_player->jy = actual_player_y;
4625 }
4626
4627 void InitMovDir(int x, int y)
4628 {
4629   int i, element = Tile[x][y];
4630   static int xy[4][2] =
4631   {
4632     {  0, +1 },
4633     { +1,  0 },
4634     {  0, -1 },
4635     { -1,  0 }
4636   };
4637   static int direction[3][4] =
4638   {
4639     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4640     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4641     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4642   };
4643
4644   switch (element)
4645   {
4646     case EL_BUG_RIGHT:
4647     case EL_BUG_UP:
4648     case EL_BUG_LEFT:
4649     case EL_BUG_DOWN:
4650       Tile[x][y] = EL_BUG;
4651       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4652       break;
4653
4654     case EL_SPACESHIP_RIGHT:
4655     case EL_SPACESHIP_UP:
4656     case EL_SPACESHIP_LEFT:
4657     case EL_SPACESHIP_DOWN:
4658       Tile[x][y] = EL_SPACESHIP;
4659       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4660       break;
4661
4662     case EL_BD_BUTTERFLY_RIGHT:
4663     case EL_BD_BUTTERFLY_UP:
4664     case EL_BD_BUTTERFLY_LEFT:
4665     case EL_BD_BUTTERFLY_DOWN:
4666       Tile[x][y] = EL_BD_BUTTERFLY;
4667       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4668       break;
4669
4670     case EL_BD_FIREFLY_RIGHT:
4671     case EL_BD_FIREFLY_UP:
4672     case EL_BD_FIREFLY_LEFT:
4673     case EL_BD_FIREFLY_DOWN:
4674       Tile[x][y] = EL_BD_FIREFLY;
4675       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4676       break;
4677
4678     case EL_PACMAN_RIGHT:
4679     case EL_PACMAN_UP:
4680     case EL_PACMAN_LEFT:
4681     case EL_PACMAN_DOWN:
4682       Tile[x][y] = EL_PACMAN;
4683       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4684       break;
4685
4686     case EL_YAMYAM_LEFT:
4687     case EL_YAMYAM_RIGHT:
4688     case EL_YAMYAM_UP:
4689     case EL_YAMYAM_DOWN:
4690       Tile[x][y] = EL_YAMYAM;
4691       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4692       break;
4693
4694     case EL_SP_SNIKSNAK:
4695       MovDir[x][y] = MV_UP;
4696       break;
4697
4698     case EL_SP_ELECTRON:
4699       MovDir[x][y] = MV_LEFT;
4700       break;
4701
4702     case EL_MOLE_LEFT:
4703     case EL_MOLE_RIGHT:
4704     case EL_MOLE_UP:
4705     case EL_MOLE_DOWN:
4706       Tile[x][y] = EL_MOLE;
4707       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4708       break;
4709
4710     case EL_SPRING_LEFT:
4711     case EL_SPRING_RIGHT:
4712       Tile[x][y] = EL_SPRING;
4713       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4714       break;
4715
4716     default:
4717       if (IS_CUSTOM_ELEMENT(element))
4718       {
4719         struct ElementInfo *ei = &element_info[element];
4720         int move_direction_initial = ei->move_direction_initial;
4721         int move_pattern = ei->move_pattern;
4722
4723         if (move_direction_initial == MV_START_PREVIOUS)
4724         {
4725           if (MovDir[x][y] != MV_NONE)
4726             return;
4727
4728           move_direction_initial = MV_START_AUTOMATIC;
4729         }
4730
4731         if (move_direction_initial == MV_START_RANDOM)
4732           MovDir[x][y] = 1 << RND(4);
4733         else if (move_direction_initial & MV_ANY_DIRECTION)
4734           MovDir[x][y] = move_direction_initial;
4735         else if (move_pattern == MV_ALL_DIRECTIONS ||
4736                  move_pattern == MV_TURNING_LEFT ||
4737                  move_pattern == MV_TURNING_RIGHT ||
4738                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4739                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4740                  move_pattern == MV_TURNING_RANDOM)
4741           MovDir[x][y] = 1 << RND(4);
4742         else if (move_pattern == MV_HORIZONTAL)
4743           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4744         else if (move_pattern == MV_VERTICAL)
4745           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4746         else if (move_pattern & MV_ANY_DIRECTION)
4747           MovDir[x][y] = element_info[element].move_pattern;
4748         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4749                  move_pattern == MV_ALONG_RIGHT_SIDE)
4750         {
4751           // use random direction as default start direction
4752           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4753             MovDir[x][y] = 1 << RND(4);
4754
4755           for (i = 0; i < NUM_DIRECTIONS; i++)
4756           {
4757             int x1 = x + xy[i][0];
4758             int y1 = y + xy[i][1];
4759
4760             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4761             {
4762               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4763                 MovDir[x][y] = direction[0][i];
4764               else
4765                 MovDir[x][y] = direction[1][i];
4766
4767               break;
4768             }
4769           }
4770         }                
4771       }
4772       else
4773       {
4774         MovDir[x][y] = 1 << RND(4);
4775
4776         if (element != EL_BUG &&
4777             element != EL_SPACESHIP &&
4778             element != EL_BD_BUTTERFLY &&
4779             element != EL_BD_FIREFLY)
4780           break;
4781
4782         for (i = 0; i < NUM_DIRECTIONS; i++)
4783         {
4784           int x1 = x + xy[i][0];
4785           int y1 = y + xy[i][1];
4786
4787           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4788           {
4789             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4790             {
4791               MovDir[x][y] = direction[0][i];
4792               break;
4793             }
4794             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4795                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4796             {
4797               MovDir[x][y] = direction[1][i];
4798               break;
4799             }
4800           }
4801         }
4802       }
4803       break;
4804   }
4805
4806   GfxDir[x][y] = MovDir[x][y];
4807 }
4808
4809 void InitAmoebaNr(int x, int y)
4810 {
4811   int i;
4812   int group_nr = AmoebaNeighbourNr(x, y);
4813
4814   if (group_nr == 0)
4815   {
4816     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4817     {
4818       if (AmoebaCnt[i] == 0)
4819       {
4820         group_nr = i;
4821         break;
4822       }
4823     }
4824   }
4825
4826   AmoebaNr[x][y] = group_nr;
4827   AmoebaCnt[group_nr]++;
4828   AmoebaCnt2[group_nr]++;
4829 }
4830
4831 static void LevelSolved_SetFinalGameValues(void)
4832 {
4833   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4834                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4835   game.score_time_final = (level.use_step_counter ? TimePlayed :
4836                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4837
4838   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4839                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4840                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4841                       game.score);
4842
4843   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4844                        MM_HEALTH(game_mm.laser_overload_value) :
4845                        game.health);
4846
4847   game.LevelSolved_CountingTime = game.time_final;
4848   game.LevelSolved_CountingScore = game.score_final;
4849   game.LevelSolved_CountingHealth = game.health_final;
4850 }
4851
4852 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4853 {
4854   game.LevelSolved_CountingTime = time;
4855   game.LevelSolved_CountingScore = score;
4856   game.LevelSolved_CountingHealth = health;
4857
4858   game_panel_controls[GAME_PANEL_TIME].value = time;
4859   game_panel_controls[GAME_PANEL_SCORE].value = score;
4860   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4861
4862   DisplayGameControlValues();
4863 }
4864
4865 static void LevelSolved(void)
4866 {
4867   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4868       game.players_still_needed > 0)
4869     return;
4870
4871   game.LevelSolved = TRUE;
4872   game.GameOver = TRUE;
4873
4874   tape.solved = TRUE;
4875
4876   // needed here to display correct panel values while player walks into exit
4877   LevelSolved_SetFinalGameValues();
4878 }
4879
4880 void GameWon(void)
4881 {
4882   static int time_count_steps;
4883   static int time, time_final;
4884   static float score, score_final; // needed for time score < 10 for 10 seconds
4885   static int health, health_final;
4886   static int game_over_delay_1 = 0;
4887   static int game_over_delay_2 = 0;
4888   static int game_over_delay_3 = 0;
4889   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4890   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4891
4892   if (!game.LevelSolved_GameWon)
4893   {
4894     int i;
4895
4896     // do not start end game actions before the player stops moving (to exit)
4897     if (local_player->active && local_player->MovPos)
4898       return;
4899
4900     // calculate final game values after player finished walking into exit
4901     LevelSolved_SetFinalGameValues();
4902
4903     game.LevelSolved_GameWon = TRUE;
4904     game.LevelSolved_SaveTape = tape.recording;
4905     game.LevelSolved_SaveScore = !tape.playing;
4906
4907     if (!tape.playing)
4908     {
4909       LevelStats_incSolved(level_nr);
4910
4911       SaveLevelSetup_SeriesInfo();
4912     }
4913
4914     if (tape.auto_play)         // tape might already be stopped here
4915       tape.auto_play_level_solved = TRUE;
4916
4917     TapeStop();
4918
4919     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4920     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4921     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4922
4923     time = time_final = game.time_final;
4924     score = score_final = game.score_final;
4925     health = health_final = game.health_final;
4926
4927     // update game panel values before (delayed) counting of score (if any)
4928     LevelSolved_DisplayFinalGameValues(time, score, health);
4929
4930     // if level has time score defined, calculate new final game values
4931     if (time_score > 0)
4932     {
4933       int time_final_max = 999;
4934       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4935       int time_frames = 0;
4936       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4937       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4938
4939       if (TimeLeft > 0)
4940       {
4941         time_final = 0;
4942         time_frames = time_frames_left;
4943       }
4944       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4945       {
4946         time_final = time_final_max;
4947         time_frames = time_frames_final_max - time_frames_played;
4948       }
4949
4950       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4951
4952       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4953
4954       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4955       {
4956         // keep previous values (final values already processed here)
4957         time_final = time;
4958         score_final = score;
4959       }
4960       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4961       {
4962         health_final = 0;
4963         score_final += health * time_score;
4964       }
4965
4966       game.score_final = score_final;
4967       game.health_final = health_final;
4968     }
4969
4970     // if not counting score after game, immediately update game panel values
4971     if (level_editor_test_game || !setup.count_score_after_game)
4972     {
4973       time = time_final;
4974       score = score_final;
4975
4976       LevelSolved_DisplayFinalGameValues(time, score, health);
4977     }
4978
4979     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4980     {
4981       // check if last player has left the level
4982       if (game.exit_x >= 0 &&
4983           game.exit_y >= 0)
4984       {
4985         int x = game.exit_x;
4986         int y = game.exit_y;
4987         int element = Tile[x][y];
4988
4989         // close exit door after last player
4990         if ((game.all_players_gone &&
4991              (element == EL_EXIT_OPEN ||
4992               element == EL_SP_EXIT_OPEN ||
4993               element == EL_STEEL_EXIT_OPEN)) ||
4994             element == EL_EM_EXIT_OPEN ||
4995             element == EL_EM_STEEL_EXIT_OPEN)
4996         {
4997
4998           Tile[x][y] =
4999             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5000              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5001              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5002              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5003              EL_EM_STEEL_EXIT_CLOSING);
5004
5005           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5006         }
5007
5008         // player disappears
5009         DrawLevelField(x, y);
5010       }
5011
5012       for (i = 0; i < MAX_PLAYERS; i++)
5013       {
5014         struct PlayerInfo *player = &stored_player[i];
5015
5016         if (player->present)
5017         {
5018           RemovePlayer(player);
5019
5020           // player disappears
5021           DrawLevelField(player->jx, player->jy);
5022         }
5023       }
5024     }
5025
5026     PlaySound(SND_GAME_WINNING);
5027   }
5028
5029   if (setup.count_score_after_game)
5030   {
5031     if (time != time_final)
5032     {
5033       if (game_over_delay_1 > 0)
5034       {
5035         game_over_delay_1--;
5036
5037         return;
5038       }
5039
5040       int time_to_go = ABS(time_final - time);
5041       int time_count_dir = (time < time_final ? +1 : -1);
5042
5043       if (time_to_go < time_count_steps)
5044         time_count_steps = 1;
5045
5046       time  += time_count_steps * time_count_dir;
5047       score += time_count_steps * time_score;
5048
5049       // set final score to correct rounding differences after counting score
5050       if (time == time_final)
5051         score = score_final;
5052
5053       LevelSolved_DisplayFinalGameValues(time, score, health);
5054
5055       if (time == time_final)
5056         StopSound(SND_GAME_LEVELTIME_BONUS);
5057       else if (setup.sound_loops)
5058         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5059       else
5060         PlaySound(SND_GAME_LEVELTIME_BONUS);
5061
5062       return;
5063     }
5064
5065     if (health != health_final)
5066     {
5067       if (game_over_delay_2 > 0)
5068       {
5069         game_over_delay_2--;
5070
5071         return;
5072       }
5073
5074       int health_count_dir = (health < health_final ? +1 : -1);
5075
5076       health += health_count_dir;
5077       score  += time_score;
5078
5079       LevelSolved_DisplayFinalGameValues(time, score, health);
5080
5081       if (health == health_final)
5082         StopSound(SND_GAME_LEVELTIME_BONUS);
5083       else if (setup.sound_loops)
5084         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5085       else
5086         PlaySound(SND_GAME_LEVELTIME_BONUS);
5087
5088       return;
5089     }
5090   }
5091
5092   game.panel.active = FALSE;
5093
5094   if (game_over_delay_3 > 0)
5095   {
5096     game_over_delay_3--;
5097
5098     return;
5099   }
5100
5101   GameEnd();
5102 }
5103
5104 void GameEnd(void)
5105 {
5106   // used instead of "level_nr" (needed for network games)
5107   int last_level_nr = levelset.level_nr;
5108   boolean tape_saved = FALSE;
5109
5110   game.LevelSolved_GameEnd = TRUE;
5111
5112   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5113   {
5114     // make sure that request dialog to save tape does not open door again
5115     if (!global.use_envelope_request)
5116       CloseDoor(DOOR_CLOSE_1);
5117
5118     // ask to save tape
5119     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5120
5121     // set unique basename for score tape (also saved in high score table)
5122     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5123   }
5124
5125   // if no tape is to be saved, close both doors simultaneously
5126   CloseDoor(DOOR_CLOSE_ALL);
5127
5128   if (level_editor_test_game || score_info_tape_play)
5129   {
5130     SetGameStatus(GAME_MODE_MAIN);
5131
5132     DrawMainMenu();
5133
5134     return;
5135   }
5136
5137   if (!game.LevelSolved_SaveScore)
5138   {
5139     SetGameStatus(GAME_MODE_MAIN);
5140
5141     DrawMainMenu();
5142
5143     return;
5144   }
5145
5146   if (level_nr == leveldir_current->handicap_level)
5147   {
5148     leveldir_current->handicap_level++;
5149
5150     SaveLevelSetup_SeriesInfo();
5151   }
5152
5153   // save score and score tape before potentially erasing tape below
5154   NewHighScore(last_level_nr, tape_saved);
5155
5156   if (setup.increment_levels &&
5157       level_nr < leveldir_current->last_level &&
5158       !network_playing)
5159   {
5160     level_nr++;         // advance to next level
5161     TapeErase();        // start with empty tape
5162
5163     if (setup.auto_play_next_level)
5164     {
5165       scores.continue_playing = TRUE;
5166       scores.next_level_nr = level_nr;
5167
5168       LoadLevel(level_nr);
5169
5170       SaveLevelSetup_SeriesInfo();
5171     }
5172   }
5173
5174   if (scores.last_added >= 0 && setup.show_scores_after_game)
5175   {
5176     SetGameStatus(GAME_MODE_SCORES);
5177
5178     DrawHallOfFame(last_level_nr);
5179   }
5180   else if (scores.continue_playing)
5181   {
5182     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5183   }
5184   else
5185   {
5186     SetGameStatus(GAME_MODE_MAIN);
5187
5188     DrawMainMenu();
5189   }
5190 }
5191
5192 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5193                          boolean one_score_entry_per_name)
5194 {
5195   int i;
5196
5197   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5198     return -1;
5199
5200   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5201   {
5202     struct ScoreEntry *entry = &list->entry[i];
5203     boolean score_is_better = (new_entry->score >  entry->score);
5204     boolean score_is_equal  = (new_entry->score == entry->score);
5205     boolean time_is_better  = (new_entry->time  <  entry->time);
5206     boolean time_is_equal   = (new_entry->time  == entry->time);
5207     boolean better_by_score = (score_is_better ||
5208                                (score_is_equal && time_is_better));
5209     boolean better_by_time  = (time_is_better ||
5210                                (time_is_equal && score_is_better));
5211     boolean is_better = (level.rate_time_over_score ? better_by_time :
5212                          better_by_score);
5213     boolean entry_is_empty = (entry->score == 0 &&
5214                               entry->time == 0);
5215
5216     // prevent adding server score entries if also existing in local score file
5217     // (special case: historic score entries have an empty tape basename entry)
5218     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5219         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5220     {
5221       // add fields from server score entry not stored in local score entry
5222       // (currently, this means setting platform, version and country fields;
5223       // in rare cases, this may also correct an invalid score value, as
5224       // historic scores might have been truncated to 16-bit values locally)
5225       *entry = *new_entry;
5226
5227       return -1;
5228     }
5229
5230     if (is_better || entry_is_empty)
5231     {
5232       // player has made it to the hall of fame
5233
5234       if (i < MAX_SCORE_ENTRIES - 1)
5235       {
5236         int m = MAX_SCORE_ENTRIES - 1;
5237         int l;
5238
5239         if (one_score_entry_per_name)
5240         {
5241           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5242             if (strEqual(list->entry[l].name, new_entry->name))
5243               m = l;
5244
5245           if (m == i)   // player's new highscore overwrites his old one
5246             goto put_into_list;
5247         }
5248
5249         for (l = m; l > i; l--)
5250           list->entry[l] = list->entry[l - 1];
5251       }
5252
5253       put_into_list:
5254
5255       *entry = *new_entry;
5256
5257       return i;
5258     }
5259     else if (one_score_entry_per_name &&
5260              strEqual(entry->name, new_entry->name))
5261     {
5262       // player already in high score list with better score or time
5263
5264       return -1;
5265     }
5266   }
5267
5268   // special case: new score is beyond the last high score list position
5269   return MAX_SCORE_ENTRIES;
5270 }
5271
5272 void NewHighScore(int level_nr, boolean tape_saved)
5273 {
5274   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5275   boolean one_per_name = FALSE;
5276
5277   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5278   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5279
5280   new_entry.score = game.score_final;
5281   new_entry.time = game.score_time_final;
5282
5283   LoadScore(level_nr);
5284
5285   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5286
5287   if (scores.last_added >= MAX_SCORE_ENTRIES)
5288   {
5289     scores.last_added = MAX_SCORE_ENTRIES - 1;
5290     scores.force_last_added = TRUE;
5291
5292     scores.entry[scores.last_added] = new_entry;
5293
5294     // store last added local score entry (before merging server scores)
5295     scores.last_added_local = scores.last_added;
5296
5297     return;
5298   }
5299
5300   if (scores.last_added < 0)
5301     return;
5302
5303   SaveScore(level_nr);
5304
5305   // store last added local score entry (before merging server scores)
5306   scores.last_added_local = scores.last_added;
5307
5308   if (!game.LevelSolved_SaveTape)
5309     return;
5310
5311   SaveScoreTape(level_nr);
5312
5313   if (setup.ask_for_using_api_server)
5314   {
5315     setup.use_api_server =
5316       Request("Upload your score and tape to the high score server?", REQ_ASK);
5317
5318     if (!setup.use_api_server)
5319       Request("Not using high score server! Use setup menu to enable again!",
5320               REQ_CONFIRM);
5321
5322     runtime.use_api_server = setup.use_api_server;
5323
5324     // after asking for using API server once, do not ask again
5325     setup.ask_for_using_api_server = FALSE;
5326
5327     SaveSetup_ServerSetup();
5328   }
5329
5330   SaveServerScore(level_nr, tape_saved);
5331 }
5332
5333 void MergeServerScore(void)
5334 {
5335   struct ScoreEntry last_added_entry;
5336   boolean one_per_name = FALSE;
5337   int i;
5338
5339   if (scores.last_added >= 0)
5340     last_added_entry = scores.entry[scores.last_added];
5341
5342   for (i = 0; i < server_scores.num_entries; i++)
5343   {
5344     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5345
5346     if (pos >= 0 && pos <= scores.last_added)
5347       scores.last_added++;
5348   }
5349
5350   if (scores.last_added >= MAX_SCORE_ENTRIES)
5351   {
5352     scores.last_added = MAX_SCORE_ENTRIES - 1;
5353     scores.force_last_added = TRUE;
5354
5355     scores.entry[scores.last_added] = last_added_entry;
5356   }
5357 }
5358
5359 static int getElementMoveStepsizeExt(int x, int y, int direction)
5360 {
5361   int element = Tile[x][y];
5362   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5363   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5364   int horiz_move = (dx != 0);
5365   int sign = (horiz_move ? dx : dy);
5366   int step = sign * element_info[element].move_stepsize;
5367
5368   // special values for move stepsize for spring and things on conveyor belt
5369   if (horiz_move)
5370   {
5371     if (CAN_FALL(element) &&
5372         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5373       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5374     else if (element == EL_SPRING)
5375       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5376   }
5377
5378   return step;
5379 }
5380
5381 static int getElementMoveStepsize(int x, int y)
5382 {
5383   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5384 }
5385
5386 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5387 {
5388   if (player->GfxAction != action || player->GfxDir != dir)
5389   {
5390     player->GfxAction = action;
5391     player->GfxDir = dir;
5392     player->Frame = 0;
5393     player->StepFrame = 0;
5394   }
5395 }
5396
5397 static void ResetGfxFrame(int x, int y)
5398 {
5399   // profiling showed that "autotest" spends 10~20% of its time in this function
5400   if (DrawingDeactivatedField())
5401     return;
5402
5403   int element = Tile[x][y];
5404   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5405
5406   if (graphic_info[graphic].anim_global_sync)
5407     GfxFrame[x][y] = FrameCounter;
5408   else if (graphic_info[graphic].anim_global_anim_sync)
5409     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5410   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5411     GfxFrame[x][y] = CustomValue[x][y];
5412   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5413     GfxFrame[x][y] = element_info[element].collect_score;
5414   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5415     GfxFrame[x][y] = ChangeDelay[x][y];
5416 }
5417
5418 static void ResetGfxAnimation(int x, int y)
5419 {
5420   GfxAction[x][y] = ACTION_DEFAULT;
5421   GfxDir[x][y] = MovDir[x][y];
5422   GfxFrame[x][y] = 0;
5423
5424   ResetGfxFrame(x, y);
5425 }
5426
5427 static void ResetRandomAnimationValue(int x, int y)
5428 {
5429   GfxRandom[x][y] = INIT_GFX_RANDOM();
5430 }
5431
5432 static void InitMovingField(int x, int y, int direction)
5433 {
5434   int element = Tile[x][y];
5435   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5436   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5437   int newx = x + dx;
5438   int newy = y + dy;
5439   boolean is_moving_before, is_moving_after;
5440
5441   // check if element was/is moving or being moved before/after mode change
5442   is_moving_before = (WasJustMoving[x][y] != 0);
5443   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5444
5445   // reset animation only for moving elements which change direction of moving
5446   // or which just started or stopped moving
5447   // (else CEs with property "can move" / "not moving" are reset each frame)
5448   if (is_moving_before != is_moving_after ||
5449       direction != MovDir[x][y])
5450     ResetGfxAnimation(x, y);
5451
5452   MovDir[x][y] = direction;
5453   GfxDir[x][y] = direction;
5454
5455   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5456                      direction == MV_DOWN && CAN_FALL(element) ?
5457                      ACTION_FALLING : ACTION_MOVING);
5458
5459   // this is needed for CEs with property "can move" / "not moving"
5460
5461   if (is_moving_after)
5462   {
5463     if (Tile[newx][newy] == EL_EMPTY)
5464       Tile[newx][newy] = EL_BLOCKED;
5465
5466     MovDir[newx][newy] = MovDir[x][y];
5467
5468     CustomValue[newx][newy] = CustomValue[x][y];
5469
5470     GfxFrame[newx][newy] = GfxFrame[x][y];
5471     GfxRandom[newx][newy] = GfxRandom[x][y];
5472     GfxAction[newx][newy] = GfxAction[x][y];
5473     GfxDir[newx][newy] = GfxDir[x][y];
5474   }
5475 }
5476
5477 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5478 {
5479   int direction = MovDir[x][y];
5480   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5481   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5482
5483   *goes_to_x = newx;
5484   *goes_to_y = newy;
5485 }
5486
5487 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5488 {
5489   int direction = MovDir[x][y];
5490   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5491   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5492
5493   *comes_from_x = oldx;
5494   *comes_from_y = oldy;
5495 }
5496
5497 static int MovingOrBlocked2Element(int x, int y)
5498 {
5499   int element = Tile[x][y];
5500
5501   if (element == EL_BLOCKED)
5502   {
5503     int oldx, oldy;
5504
5505     Blocked2Moving(x, y, &oldx, &oldy);
5506
5507     return Tile[oldx][oldy];
5508   }
5509
5510   return element;
5511 }
5512
5513 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5514 {
5515   // like MovingOrBlocked2Element(), but if element is moving
5516   // and (x, y) is the field the moving element is just leaving,
5517   // return EL_BLOCKED instead of the element value
5518   int element = Tile[x][y];
5519
5520   if (IS_MOVING(x, y))
5521   {
5522     if (element == EL_BLOCKED)
5523     {
5524       int oldx, oldy;
5525
5526       Blocked2Moving(x, y, &oldx, &oldy);
5527       return Tile[oldx][oldy];
5528     }
5529     else
5530       return EL_BLOCKED;
5531   }
5532   else
5533     return element;
5534 }
5535
5536 static void RemoveField(int x, int y)
5537 {
5538   Tile[x][y] = EL_EMPTY;
5539
5540   MovPos[x][y] = 0;
5541   MovDir[x][y] = 0;
5542   MovDelay[x][y] = 0;
5543
5544   CustomValue[x][y] = 0;
5545
5546   AmoebaNr[x][y] = 0;
5547   ChangeDelay[x][y] = 0;
5548   ChangePage[x][y] = -1;
5549   Pushed[x][y] = FALSE;
5550
5551   GfxElement[x][y] = EL_UNDEFINED;
5552   GfxAction[x][y] = ACTION_DEFAULT;
5553   GfxDir[x][y] = MV_NONE;
5554 }
5555
5556 static void RemoveMovingField(int x, int y)
5557 {
5558   int oldx = x, oldy = y, newx = x, newy = y;
5559   int element = Tile[x][y];
5560   int next_element = EL_UNDEFINED;
5561
5562   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5563     return;
5564
5565   if (IS_MOVING(x, y))
5566   {
5567     Moving2Blocked(x, y, &newx, &newy);
5568
5569     if (Tile[newx][newy] != EL_BLOCKED)
5570     {
5571       // element is moving, but target field is not free (blocked), but
5572       // already occupied by something different (example: acid pool);
5573       // in this case, only remove the moving field, but not the target
5574
5575       RemoveField(oldx, oldy);
5576
5577       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5578
5579       TEST_DrawLevelField(oldx, oldy);
5580
5581       return;
5582     }
5583   }
5584   else if (element == EL_BLOCKED)
5585   {
5586     Blocked2Moving(x, y, &oldx, &oldy);
5587     if (!IS_MOVING(oldx, oldy))
5588       return;
5589   }
5590
5591   if (element == EL_BLOCKED &&
5592       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5593        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5594        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5595        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5596        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5597        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5598     next_element = get_next_element(Tile[oldx][oldy]);
5599
5600   RemoveField(oldx, oldy);
5601   RemoveField(newx, newy);
5602
5603   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5604
5605   if (next_element != EL_UNDEFINED)
5606     Tile[oldx][oldy] = next_element;
5607
5608   TEST_DrawLevelField(oldx, oldy);
5609   TEST_DrawLevelField(newx, newy);
5610 }
5611
5612 void DrawDynamite(int x, int y)
5613 {
5614   int sx = SCREENX(x), sy = SCREENY(y);
5615   int graphic = el2img(Tile[x][y]);
5616   int frame;
5617
5618   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5619     return;
5620
5621   if (IS_WALKABLE_INSIDE(Back[x][y]))
5622     return;
5623
5624   if (Back[x][y])
5625     DrawLevelElement(x, y, Back[x][y]);
5626   else if (Store[x][y])
5627     DrawLevelElement(x, y, Store[x][y]);
5628   else if (game.use_masked_elements)
5629     DrawLevelElement(x, y, EL_EMPTY);
5630
5631   frame = getGraphicAnimationFrameXY(graphic, x, y);
5632
5633   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5634     DrawGraphicThruMask(sx, sy, graphic, frame);
5635   else
5636     DrawGraphic(sx, sy, graphic, frame);
5637 }
5638
5639 static void CheckDynamite(int x, int y)
5640 {
5641   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5642   {
5643     MovDelay[x][y]--;
5644
5645     if (MovDelay[x][y] != 0)
5646     {
5647       DrawDynamite(x, y);
5648       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5649
5650       return;
5651     }
5652   }
5653
5654   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5655
5656   Bang(x, y);
5657 }
5658
5659 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5660 {
5661   boolean num_checked_players = 0;
5662   int i;
5663
5664   for (i = 0; i < MAX_PLAYERS; i++)
5665   {
5666     if (stored_player[i].active)
5667     {
5668       int sx = stored_player[i].jx;
5669       int sy = stored_player[i].jy;
5670
5671       if (num_checked_players == 0)
5672       {
5673         *sx1 = *sx2 = sx;
5674         *sy1 = *sy2 = sy;
5675       }
5676       else
5677       {
5678         *sx1 = MIN(*sx1, sx);
5679         *sy1 = MIN(*sy1, sy);
5680         *sx2 = MAX(*sx2, sx);
5681         *sy2 = MAX(*sy2, sy);
5682       }
5683
5684       num_checked_players++;
5685     }
5686   }
5687 }
5688
5689 static boolean checkIfAllPlayersFitToScreen_RND(void)
5690 {
5691   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5692
5693   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5694
5695   return (sx2 - sx1 < SCR_FIELDX &&
5696           sy2 - sy1 < SCR_FIELDY);
5697 }
5698
5699 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5700 {
5701   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5702
5703   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5704
5705   *sx = (sx1 + sx2) / 2;
5706   *sy = (sy1 + sy2) / 2;
5707 }
5708
5709 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5710                                boolean center_screen, boolean quick_relocation)
5711 {
5712   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5713   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5714   boolean no_delay = (tape.warp_forward);
5715   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5716   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5717   int new_scroll_x, new_scroll_y;
5718
5719   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5720   {
5721     // case 1: quick relocation inside visible screen (without scrolling)
5722
5723     RedrawPlayfield();
5724
5725     return;
5726   }
5727
5728   if (!level.shifted_relocation || center_screen)
5729   {
5730     // relocation _with_ centering of screen
5731
5732     new_scroll_x = SCROLL_POSITION_X(x);
5733     new_scroll_y = SCROLL_POSITION_Y(y);
5734   }
5735   else
5736   {
5737     // relocation _without_ centering of screen
5738
5739     // apply distance between old and new player position to scroll position
5740     int shifted_scroll_x = scroll_x + (x - old_x);
5741     int shifted_scroll_y = scroll_y + (y - old_y);
5742
5743     // make sure that shifted scroll position does not scroll beyond screen
5744     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5745     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5746
5747     // special case for teleporting from one end of the playfield to the other
5748     // (this kludge prevents the destination area to be shifted by half a tile
5749     // against the source destination for even screen width or screen height;
5750     // probably most useful when used with high "game.forced_scroll_delay_value"
5751     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5752     if (quick_relocation)
5753     {
5754       if (EVEN(SCR_FIELDX))
5755       {
5756         // relocate (teleport) between left and right border (half or full)
5757         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5758           new_scroll_x = SBX_Right;
5759         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5760           new_scroll_x = SBX_Right - 1;
5761         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5762           new_scroll_x = SBX_Left;
5763         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5764           new_scroll_x = SBX_Left + 1;
5765       }
5766
5767       if (EVEN(SCR_FIELDY))
5768       {
5769         // relocate (teleport) between top and bottom border (half or full)
5770         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5771           new_scroll_y = SBY_Lower;
5772         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5773           new_scroll_y = SBY_Lower - 1;
5774         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5775           new_scroll_y = SBY_Upper;
5776         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5777           new_scroll_y = SBY_Upper + 1;
5778       }
5779     }
5780   }
5781
5782   if (quick_relocation)
5783   {
5784     // case 2: quick relocation (redraw without visible scrolling)
5785
5786     scroll_x = new_scroll_x;
5787     scroll_y = new_scroll_y;
5788
5789     RedrawPlayfield();
5790
5791     return;
5792   }
5793
5794   // case 3: visible relocation (with scrolling to new position)
5795
5796   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5797
5798   SetVideoFrameDelay(wait_delay_value);
5799
5800   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5801   {
5802     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5803     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5804
5805     if (dx == 0 && dy == 0)             // no scrolling needed at all
5806       break;
5807
5808     scroll_x -= dx;
5809     scroll_y -= dy;
5810
5811     // set values for horizontal/vertical screen scrolling (half tile size)
5812     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5813     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5814     int pos_x = dx * TILEX / 2;
5815     int pos_y = dy * TILEY / 2;
5816     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5817     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5818
5819     ScrollLevel(dx, dy);
5820     DrawAllPlayers();
5821
5822     // scroll in two steps of half tile size to make things smoother
5823     BlitScreenToBitmapExt_RND(window, fx, fy);
5824
5825     // scroll second step to align at full tile size
5826     BlitScreenToBitmap(window);
5827   }
5828
5829   DrawAllPlayers();
5830   BackToFront();
5831
5832   SetVideoFrameDelay(frame_delay_value_old);
5833 }
5834
5835 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5836 {
5837   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5838   int player_nr = GET_PLAYER_NR(el_player);
5839   struct PlayerInfo *player = &stored_player[player_nr];
5840   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5841   boolean no_delay = (tape.warp_forward);
5842   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5843   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5844   int old_jx = player->jx;
5845   int old_jy = player->jy;
5846   int old_element = Tile[old_jx][old_jy];
5847   int element = Tile[jx][jy];
5848   boolean player_relocated = (old_jx != jx || old_jy != jy);
5849
5850   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5851   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5852   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5853   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5854   int leave_side_horiz = move_dir_horiz;
5855   int leave_side_vert  = move_dir_vert;
5856   int enter_side = enter_side_horiz | enter_side_vert;
5857   int leave_side = leave_side_horiz | leave_side_vert;
5858
5859   if (player->buried)           // do not reanimate dead player
5860     return;
5861
5862   if (!player_relocated)        // no need to relocate the player
5863     return;
5864
5865   if (IS_PLAYER(jx, jy))        // player already placed at new position
5866   {
5867     RemoveField(jx, jy);        // temporarily remove newly placed player
5868     DrawLevelField(jx, jy);
5869   }
5870
5871   if (player->present)
5872   {
5873     while (player->MovPos)
5874     {
5875       ScrollPlayer(player, SCROLL_GO_ON);
5876       ScrollScreen(NULL, SCROLL_GO_ON);
5877
5878       AdvanceFrameAndPlayerCounters(player->index_nr);
5879
5880       DrawPlayer(player);
5881
5882       BackToFront_WithFrameDelay(wait_delay_value);
5883     }
5884
5885     DrawPlayer(player);         // needed here only to cleanup last field
5886     DrawLevelField(player->jx, player->jy);     // remove player graphic
5887
5888     player->is_moving = FALSE;
5889   }
5890
5891   if (IS_CUSTOM_ELEMENT(old_element))
5892     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5893                                CE_LEFT_BY_PLAYER,
5894                                player->index_bit, leave_side);
5895
5896   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5897                                       CE_PLAYER_LEAVES_X,
5898                                       player->index_bit, leave_side);
5899
5900   Tile[jx][jy] = el_player;
5901   InitPlayerField(jx, jy, el_player, TRUE);
5902
5903   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5904      possible that the relocation target field did not contain a player element,
5905      but a walkable element, to which the new player was relocated -- in this
5906      case, restore that (already initialized!) element on the player field */
5907   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5908   {
5909     Tile[jx][jy] = element;     // restore previously existing element
5910   }
5911
5912   // only visually relocate centered player
5913   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5914                      FALSE, level.instant_relocation);
5915
5916   TestIfPlayerTouchesBadThing(jx, jy);
5917   TestIfPlayerTouchesCustomElement(jx, jy);
5918
5919   if (IS_CUSTOM_ELEMENT(element))
5920     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5921                                player->index_bit, enter_side);
5922
5923   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5924                                       player->index_bit, enter_side);
5925
5926   if (player->is_switching)
5927   {
5928     /* ensure that relocation while still switching an element does not cause
5929        a new element to be treated as also switched directly after relocation
5930        (this is important for teleporter switches that teleport the player to
5931        a place where another teleporter switch is in the same direction, which
5932        would then incorrectly be treated as immediately switched before the
5933        direction key that caused the switch was released) */
5934
5935     player->switch_x += jx - old_jx;
5936     player->switch_y += jy - old_jy;
5937   }
5938 }
5939
5940 static void Explode(int ex, int ey, int phase, int mode)
5941 {
5942   int x, y;
5943   int last_phase;
5944   int border_element;
5945
5946   if (game.explosions_delayed)
5947   {
5948     ExplodeField[ex][ey] = mode;
5949     return;
5950   }
5951
5952   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5953   {
5954     int center_element = Tile[ex][ey];
5955     int ce_value = CustomValue[ex][ey];
5956     int ce_score = element_info[center_element].collect_score;
5957     int artwork_element, explosion_element;     // set these values later
5958
5959     // remove things displayed in background while burning dynamite
5960     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5961       Back[ex][ey] = 0;
5962
5963     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5964     {
5965       // put moving element to center field (and let it explode there)
5966       center_element = MovingOrBlocked2Element(ex, ey);
5967       RemoveMovingField(ex, ey);
5968       Tile[ex][ey] = center_element;
5969     }
5970
5971     // now "center_element" is finally determined -- set related values now
5972     artwork_element = center_element;           // for custom player artwork
5973     explosion_element = center_element;         // for custom player artwork
5974
5975     if (IS_PLAYER(ex, ey))
5976     {
5977       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5978
5979       artwork_element = stored_player[player_nr].artwork_element;
5980
5981       if (level.use_explosion_element[player_nr])
5982       {
5983         explosion_element = level.explosion_element[player_nr];
5984         artwork_element = explosion_element;
5985       }
5986     }
5987
5988     if (mode == EX_TYPE_NORMAL ||
5989         mode == EX_TYPE_CENTER ||
5990         mode == EX_TYPE_CROSS)
5991       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5992
5993     last_phase = element_info[explosion_element].explosion_delay + 1;
5994
5995     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5996     {
5997       int xx = x - ex + 1;
5998       int yy = y - ey + 1;
5999       int element;
6000
6001       if (!IN_LEV_FIELD(x, y) ||
6002           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6003           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6004         continue;
6005
6006       element = Tile[x][y];
6007
6008       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6009       {
6010         element = MovingOrBlocked2Element(x, y);
6011
6012         if (!IS_EXPLOSION_PROOF(element))
6013           RemoveMovingField(x, y);
6014       }
6015
6016       // indestructible elements can only explode in center (but not flames)
6017       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6018                                            mode == EX_TYPE_BORDER)) ||
6019           element == EL_FLAMES)
6020         continue;
6021
6022       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6023          behaviour, for example when touching a yamyam that explodes to rocks
6024          with active deadly shield, a rock is created under the player !!! */
6025       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6026 #if 0
6027       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6028           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6029            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6030 #else
6031       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6032 #endif
6033       {
6034         if (IS_ACTIVE_BOMB(element))
6035         {
6036           // re-activate things under the bomb like gate or penguin
6037           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6038           Back[x][y] = 0;
6039         }
6040
6041         continue;
6042       }
6043
6044       // save walkable background elements while explosion on same tile
6045       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6046           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6047         Back[x][y] = element;
6048
6049       // ignite explodable elements reached by other explosion
6050       if (element == EL_EXPLOSION)
6051         element = Store2[x][y];
6052
6053       if (AmoebaNr[x][y] &&
6054           (element == EL_AMOEBA_FULL ||
6055            element == EL_BD_AMOEBA ||
6056            element == EL_AMOEBA_GROWING))
6057       {
6058         AmoebaCnt[AmoebaNr[x][y]]--;
6059         AmoebaCnt2[AmoebaNr[x][y]]--;
6060       }
6061
6062       RemoveField(x, y);
6063
6064       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6065       {
6066         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6067
6068         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6069
6070         if (PLAYERINFO(ex, ey)->use_murphy)
6071           Store[x][y] = EL_EMPTY;
6072       }
6073
6074       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6075       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6076       else if (IS_PLAYER_ELEMENT(center_element))
6077         Store[x][y] = EL_EMPTY;
6078       else if (center_element == EL_YAMYAM)
6079         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6080       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6081         Store[x][y] = element_info[center_element].content.e[xx][yy];
6082 #if 1
6083       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6084       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6085       // otherwise) -- FIX THIS !!!
6086       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6087         Store[x][y] = element_info[element].content.e[1][1];
6088 #else
6089       else if (!CAN_EXPLODE(element))
6090         Store[x][y] = element_info[element].content.e[1][1];
6091 #endif
6092       else
6093         Store[x][y] = EL_EMPTY;
6094
6095       if (IS_CUSTOM_ELEMENT(center_element))
6096         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6097                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6098                        Store[x][y] >= EL_PREV_CE_8 &&
6099                        Store[x][y] <= EL_NEXT_CE_8 ?
6100                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6101                        Store[x][y]);
6102
6103       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6104           center_element == EL_AMOEBA_TO_DIAMOND)
6105         Store2[x][y] = element;
6106
6107       Tile[x][y] = EL_EXPLOSION;
6108       GfxElement[x][y] = artwork_element;
6109
6110       ExplodePhase[x][y] = 1;
6111       ExplodeDelay[x][y] = last_phase;
6112
6113       Stop[x][y] = TRUE;
6114     }
6115
6116     if (center_element == EL_YAMYAM)
6117       game.yamyam_content_nr =
6118         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6119
6120     return;
6121   }
6122
6123   if (Stop[ex][ey])
6124     return;
6125
6126   x = ex;
6127   y = ey;
6128
6129   if (phase == 1)
6130     GfxFrame[x][y] = 0;         // restart explosion animation
6131
6132   last_phase = ExplodeDelay[x][y];
6133
6134   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6135
6136   // this can happen if the player leaves an explosion just in time
6137   if (GfxElement[x][y] == EL_UNDEFINED)
6138     GfxElement[x][y] = EL_EMPTY;
6139
6140   border_element = Store2[x][y];
6141   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6142     border_element = StorePlayer[x][y];
6143
6144   if (phase == element_info[border_element].ignition_delay ||
6145       phase == last_phase)
6146   {
6147     boolean border_explosion = FALSE;
6148
6149     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6150         !PLAYER_EXPLOSION_PROTECTED(x, y))
6151     {
6152       KillPlayerUnlessExplosionProtected(x, y);
6153       border_explosion = TRUE;
6154     }
6155     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6156     {
6157       Tile[x][y] = Store2[x][y];
6158       Store2[x][y] = 0;
6159       Bang(x, y);
6160       border_explosion = TRUE;
6161     }
6162     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6163     {
6164       AmoebaToDiamond(x, y);
6165       Store2[x][y] = 0;
6166       border_explosion = TRUE;
6167     }
6168
6169     // if an element just explodes due to another explosion (chain-reaction),
6170     // do not immediately end the new explosion when it was the last frame of
6171     // the explosion (as it would be done in the following "if"-statement!)
6172     if (border_explosion && phase == last_phase)
6173       return;
6174   }
6175
6176   // this can happen if the player was just killed by an explosion
6177   if (GfxElement[x][y] == EL_UNDEFINED)
6178     GfxElement[x][y] = EL_EMPTY;
6179
6180   if (phase == last_phase)
6181   {
6182     int element;
6183
6184     element = Tile[x][y] = Store[x][y];
6185     Store[x][y] = Store2[x][y] = 0;
6186     GfxElement[x][y] = EL_UNDEFINED;
6187
6188     // player can escape from explosions and might therefore be still alive
6189     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6190         element <= EL_PLAYER_IS_EXPLODING_4)
6191     {
6192       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6193       int explosion_element = EL_PLAYER_1 + player_nr;
6194       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6195       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6196
6197       if (level.use_explosion_element[player_nr])
6198         explosion_element = level.explosion_element[player_nr];
6199
6200       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6201                     element_info[explosion_element].content.e[xx][yy]);
6202     }
6203
6204     // restore probably existing indestructible background element
6205     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6206       element = Tile[x][y] = Back[x][y];
6207     Back[x][y] = 0;
6208
6209     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6210     GfxDir[x][y] = MV_NONE;
6211     ChangeDelay[x][y] = 0;
6212     ChangePage[x][y] = -1;
6213
6214     CustomValue[x][y] = 0;
6215
6216     InitField_WithBug2(x, y, FALSE);
6217
6218     TEST_DrawLevelField(x, y);
6219
6220     TestIfElementTouchesCustomElement(x, y);
6221
6222     if (GFX_CRUMBLED(element))
6223       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6224
6225     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6226       StorePlayer[x][y] = 0;
6227
6228     if (IS_PLAYER_ELEMENT(element))
6229       RelocatePlayer(x, y, element);
6230   }
6231   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6232   {
6233     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6234     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6235
6236     if (phase == 1)
6237       TEST_DrawLevelFieldCrumbled(x, y);
6238
6239     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6240     {
6241       DrawLevelElement(x, y, Back[x][y]);
6242       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6243     }
6244     else if (IS_WALKABLE_UNDER(Back[x][y]))
6245     {
6246       DrawLevelGraphic(x, y, graphic, frame);
6247       DrawLevelElementThruMask(x, y, Back[x][y]);
6248     }
6249     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6250       DrawLevelGraphic(x, y, graphic, frame);
6251   }
6252 }
6253
6254 static void DynaExplode(int ex, int ey)
6255 {
6256   int i, j;
6257   int dynabomb_element = Tile[ex][ey];
6258   int dynabomb_size = 1;
6259   boolean dynabomb_xl = FALSE;
6260   struct PlayerInfo *player;
6261   struct XY *xy = xy_topdown;
6262
6263   if (IS_ACTIVE_BOMB(dynabomb_element))
6264   {
6265     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6266     dynabomb_size = player->dynabomb_size;
6267     dynabomb_xl = player->dynabomb_xl;
6268     player->dynabombs_left++;
6269   }
6270
6271   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6272
6273   for (i = 0; i < NUM_DIRECTIONS; i++)
6274   {
6275     for (j = 1; j <= dynabomb_size; j++)
6276     {
6277       int x = ex + j * xy[i].x;
6278       int y = ey + j * xy[i].y;
6279       int element;
6280
6281       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6282         break;
6283
6284       element = Tile[x][y];
6285
6286       // do not restart explosions of fields with active bombs
6287       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6288         continue;
6289
6290       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6291
6292       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6293           !IS_DIGGABLE(element) && !dynabomb_xl)
6294         break;
6295     }
6296   }
6297 }
6298
6299 void Bang(int x, int y)
6300 {
6301   int element = MovingOrBlocked2Element(x, y);
6302   int explosion_type = EX_TYPE_NORMAL;
6303
6304   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6305   {
6306     struct PlayerInfo *player = PLAYERINFO(x, y);
6307
6308     element = Tile[x][y] = player->initial_element;
6309
6310     if (level.use_explosion_element[player->index_nr])
6311     {
6312       int explosion_element = level.explosion_element[player->index_nr];
6313
6314       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6315         explosion_type = EX_TYPE_CROSS;
6316       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6317         explosion_type = EX_TYPE_CENTER;
6318     }
6319   }
6320
6321   switch (element)
6322   {
6323     case EL_BUG:
6324     case EL_SPACESHIP:
6325     case EL_BD_BUTTERFLY:
6326     case EL_BD_FIREFLY:
6327     case EL_YAMYAM:
6328     case EL_DARK_YAMYAM:
6329     case EL_ROBOT:
6330     case EL_PACMAN:
6331     case EL_MOLE:
6332       RaiseScoreElement(element);
6333       break;
6334
6335     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6336     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6337     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6338     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6339     case EL_DYNABOMB_INCREASE_NUMBER:
6340     case EL_DYNABOMB_INCREASE_SIZE:
6341     case EL_DYNABOMB_INCREASE_POWER:
6342       explosion_type = EX_TYPE_DYNA;
6343       break;
6344
6345     case EL_DC_LANDMINE:
6346       explosion_type = EX_TYPE_CENTER;
6347       break;
6348
6349     case EL_PENGUIN:
6350     case EL_LAMP:
6351     case EL_LAMP_ACTIVE:
6352     case EL_AMOEBA_TO_DIAMOND:
6353       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6354         explosion_type = EX_TYPE_CENTER;
6355       break;
6356
6357     default:
6358       if (element_info[element].explosion_type == EXPLODES_CROSS)
6359         explosion_type = EX_TYPE_CROSS;
6360       else if (element_info[element].explosion_type == EXPLODES_1X1)
6361         explosion_type = EX_TYPE_CENTER;
6362       break;
6363   }
6364
6365   if (explosion_type == EX_TYPE_DYNA)
6366     DynaExplode(x, y);
6367   else
6368     Explode(x, y, EX_PHASE_START, explosion_type);
6369
6370   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6371 }
6372
6373 static void SplashAcid(int x, int y)
6374 {
6375   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6376       (!IN_LEV_FIELD(x - 1, y - 2) ||
6377        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6378     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6379
6380   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6381       (!IN_LEV_FIELD(x + 1, y - 2) ||
6382        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6383     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6384
6385   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6386 }
6387
6388 static void InitBeltMovement(void)
6389 {
6390   static int belt_base_element[4] =
6391   {
6392     EL_CONVEYOR_BELT_1_LEFT,
6393     EL_CONVEYOR_BELT_2_LEFT,
6394     EL_CONVEYOR_BELT_3_LEFT,
6395     EL_CONVEYOR_BELT_4_LEFT
6396   };
6397   static int belt_base_active_element[4] =
6398   {
6399     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6400     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6401     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6402     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6403   };
6404
6405   int x, y, i, j;
6406
6407   // set frame order for belt animation graphic according to belt direction
6408   for (i = 0; i < NUM_BELTS; i++)
6409   {
6410     int belt_nr = i;
6411
6412     for (j = 0; j < NUM_BELT_PARTS; j++)
6413     {
6414       int element = belt_base_active_element[belt_nr] + j;
6415       int graphic_1 = el2img(element);
6416       int graphic_2 = el2panelimg(element);
6417
6418       if (game.belt_dir[i] == MV_LEFT)
6419       {
6420         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6421         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6422       }
6423       else
6424       {
6425         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6426         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6427       }
6428     }
6429   }
6430
6431   SCAN_PLAYFIELD(x, y)
6432   {
6433     int element = Tile[x][y];
6434
6435     for (i = 0; i < NUM_BELTS; i++)
6436     {
6437       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6438       {
6439         int e_belt_nr = getBeltNrFromBeltElement(element);
6440         int belt_nr = i;
6441
6442         if (e_belt_nr == belt_nr)
6443         {
6444           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6445
6446           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6447         }
6448       }
6449     }
6450   }
6451 }
6452
6453 static void ToggleBeltSwitch(int x, int y)
6454 {
6455   static int belt_base_element[4] =
6456   {
6457     EL_CONVEYOR_BELT_1_LEFT,
6458     EL_CONVEYOR_BELT_2_LEFT,
6459     EL_CONVEYOR_BELT_3_LEFT,
6460     EL_CONVEYOR_BELT_4_LEFT
6461   };
6462   static int belt_base_active_element[4] =
6463   {
6464     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6465     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6466     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6467     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6468   };
6469   static int belt_base_switch_element[4] =
6470   {
6471     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6472     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6473     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6474     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6475   };
6476   static int belt_move_dir[4] =
6477   {
6478     MV_LEFT,
6479     MV_NONE,
6480     MV_RIGHT,
6481     MV_NONE,
6482   };
6483
6484   int element = Tile[x][y];
6485   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6486   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6487   int belt_dir = belt_move_dir[belt_dir_nr];
6488   int xx, yy, i;
6489
6490   if (!IS_BELT_SWITCH(element))
6491     return;
6492
6493   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6494   game.belt_dir[belt_nr] = belt_dir;
6495
6496   if (belt_dir_nr == 3)
6497     belt_dir_nr = 1;
6498
6499   // set frame order for belt animation graphic according to belt direction
6500   for (i = 0; i < NUM_BELT_PARTS; i++)
6501   {
6502     int element = belt_base_active_element[belt_nr] + i;
6503     int graphic_1 = el2img(element);
6504     int graphic_2 = el2panelimg(element);
6505
6506     if (belt_dir == MV_LEFT)
6507     {
6508       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6509       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6510     }
6511     else
6512     {
6513       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6514       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6515     }
6516   }
6517
6518   SCAN_PLAYFIELD(xx, yy)
6519   {
6520     int element = Tile[xx][yy];
6521
6522     if (IS_BELT_SWITCH(element))
6523     {
6524       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6525
6526       if (e_belt_nr == belt_nr)
6527       {
6528         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6529         TEST_DrawLevelField(xx, yy);
6530       }
6531     }
6532     else if (IS_BELT(element) && belt_dir != MV_NONE)
6533     {
6534       int e_belt_nr = getBeltNrFromBeltElement(element);
6535
6536       if (e_belt_nr == belt_nr)
6537       {
6538         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6539
6540         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6541         TEST_DrawLevelField(xx, yy);
6542       }
6543     }
6544     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6545     {
6546       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6547
6548       if (e_belt_nr == belt_nr)
6549       {
6550         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6551
6552         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6553         TEST_DrawLevelField(xx, yy);
6554       }
6555     }
6556   }
6557 }
6558
6559 static void ToggleSwitchgateSwitch(void)
6560 {
6561   int xx, yy;
6562
6563   game.switchgate_pos = !game.switchgate_pos;
6564
6565   SCAN_PLAYFIELD(xx, yy)
6566   {
6567     int element = Tile[xx][yy];
6568
6569     if (element == EL_SWITCHGATE_SWITCH_UP)
6570     {
6571       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6572       TEST_DrawLevelField(xx, yy);
6573     }
6574     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6575     {
6576       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6577       TEST_DrawLevelField(xx, yy);
6578     }
6579     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6580     {
6581       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6582       TEST_DrawLevelField(xx, yy);
6583     }
6584     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6585     {
6586       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6587       TEST_DrawLevelField(xx, yy);
6588     }
6589     else if (element == EL_SWITCHGATE_OPEN ||
6590              element == EL_SWITCHGATE_OPENING)
6591     {
6592       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6593
6594       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6595     }
6596     else if (element == EL_SWITCHGATE_CLOSED ||
6597              element == EL_SWITCHGATE_CLOSING)
6598     {
6599       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6600
6601       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6602     }
6603   }
6604 }
6605
6606 static int getInvisibleActiveFromInvisibleElement(int element)
6607 {
6608   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6609           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6610           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6611           element);
6612 }
6613
6614 static int getInvisibleFromInvisibleActiveElement(int element)
6615 {
6616   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6617           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6618           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6619           element);
6620 }
6621
6622 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6623 {
6624   int x, y;
6625
6626   SCAN_PLAYFIELD(x, y)
6627   {
6628     int element = Tile[x][y];
6629
6630     if (element == EL_LIGHT_SWITCH &&
6631         game.light_time_left > 0)
6632     {
6633       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6637              game.light_time_left == 0)
6638     {
6639       Tile[x][y] = EL_LIGHT_SWITCH;
6640       TEST_DrawLevelField(x, y);
6641     }
6642     else if (element == EL_EMC_DRIPPER &&
6643              game.light_time_left > 0)
6644     {
6645       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6646       TEST_DrawLevelField(x, y);
6647     }
6648     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6649              game.light_time_left == 0)
6650     {
6651       Tile[x][y] = EL_EMC_DRIPPER;
6652       TEST_DrawLevelField(x, y);
6653     }
6654     else if (element == EL_INVISIBLE_STEELWALL ||
6655              element == EL_INVISIBLE_WALL ||
6656              element == EL_INVISIBLE_SAND)
6657     {
6658       if (game.light_time_left > 0)
6659         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6660
6661       TEST_DrawLevelField(x, y);
6662
6663       // uncrumble neighbour fields, if needed
6664       if (element == EL_INVISIBLE_SAND)
6665         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6666     }
6667     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6668              element == EL_INVISIBLE_WALL_ACTIVE ||
6669              element == EL_INVISIBLE_SAND_ACTIVE)
6670     {
6671       if (game.light_time_left == 0)
6672         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6673
6674       TEST_DrawLevelField(x, y);
6675
6676       // re-crumble neighbour fields, if needed
6677       if (element == EL_INVISIBLE_SAND)
6678         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6679     }
6680   }
6681 }
6682
6683 static void RedrawAllInvisibleElementsForLenses(void)
6684 {
6685   int x, y;
6686
6687   SCAN_PLAYFIELD(x, y)
6688   {
6689     int element = Tile[x][y];
6690
6691     if (element == EL_EMC_DRIPPER &&
6692         game.lenses_time_left > 0)
6693     {
6694       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6695       TEST_DrawLevelField(x, y);
6696     }
6697     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6698              game.lenses_time_left == 0)
6699     {
6700       Tile[x][y] = EL_EMC_DRIPPER;
6701       TEST_DrawLevelField(x, y);
6702     }
6703     else if (element == EL_INVISIBLE_STEELWALL ||
6704              element == EL_INVISIBLE_WALL ||
6705              element == EL_INVISIBLE_SAND)
6706     {
6707       if (game.lenses_time_left > 0)
6708         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6709
6710       TEST_DrawLevelField(x, y);
6711
6712       // uncrumble neighbour fields, if needed
6713       if (element == EL_INVISIBLE_SAND)
6714         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6715     }
6716     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6717              element == EL_INVISIBLE_WALL_ACTIVE ||
6718              element == EL_INVISIBLE_SAND_ACTIVE)
6719     {
6720       if (game.lenses_time_left == 0)
6721         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6722
6723       TEST_DrawLevelField(x, y);
6724
6725       // re-crumble neighbour fields, if needed
6726       if (element == EL_INVISIBLE_SAND)
6727         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6728     }
6729   }
6730 }
6731
6732 static void RedrawAllInvisibleElementsForMagnifier(void)
6733 {
6734   int x, y;
6735
6736   SCAN_PLAYFIELD(x, y)
6737   {
6738     int element = Tile[x][y];
6739
6740     if (element == EL_EMC_FAKE_GRASS &&
6741         game.magnify_time_left > 0)
6742     {
6743       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6744       TEST_DrawLevelField(x, y);
6745     }
6746     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6747              game.magnify_time_left == 0)
6748     {
6749       Tile[x][y] = EL_EMC_FAKE_GRASS;
6750       TEST_DrawLevelField(x, y);
6751     }
6752     else if (IS_GATE_GRAY(element) &&
6753              game.magnify_time_left > 0)
6754     {
6755       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6756                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6757                     IS_EM_GATE_GRAY(element) ?
6758                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6759                     IS_EMC_GATE_GRAY(element) ?
6760                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6761                     IS_DC_GATE_GRAY(element) ?
6762                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6763                     element);
6764       TEST_DrawLevelField(x, y);
6765     }
6766     else if (IS_GATE_GRAY_ACTIVE(element) &&
6767              game.magnify_time_left == 0)
6768     {
6769       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6770                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6771                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6772                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6773                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6774                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6775                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6776                     EL_DC_GATE_WHITE_GRAY :
6777                     element);
6778       TEST_DrawLevelField(x, y);
6779     }
6780   }
6781 }
6782
6783 static void ToggleLightSwitch(int x, int y)
6784 {
6785   int element = Tile[x][y];
6786
6787   game.light_time_left =
6788     (element == EL_LIGHT_SWITCH ?
6789      level.time_light * FRAMES_PER_SECOND : 0);
6790
6791   RedrawAllLightSwitchesAndInvisibleElements();
6792 }
6793
6794 static void ActivateTimegateSwitch(int x, int y)
6795 {
6796   int xx, yy;
6797
6798   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6799
6800   SCAN_PLAYFIELD(xx, yy)
6801   {
6802     int element = Tile[xx][yy];
6803
6804     if (element == EL_TIMEGATE_CLOSED ||
6805         element == EL_TIMEGATE_CLOSING)
6806     {
6807       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6808       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6809     }
6810
6811     /*
6812     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6813     {
6814       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6815       TEST_DrawLevelField(xx, yy);
6816     }
6817     */
6818
6819   }
6820
6821   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6822                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6823 }
6824
6825 static void Impact(int x, int y)
6826 {
6827   boolean last_line = (y == lev_fieldy - 1);
6828   boolean object_hit = FALSE;
6829   boolean impact = (last_line || object_hit);
6830   int element = Tile[x][y];
6831   int smashed = EL_STEELWALL;
6832
6833   if (!last_line)       // check if element below was hit
6834   {
6835     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6836       return;
6837
6838     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6839                                          MovDir[x][y + 1] != MV_DOWN ||
6840                                          MovPos[x][y + 1] <= TILEY / 2));
6841
6842     // do not smash moving elements that left the smashed field in time
6843     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6844         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6845       object_hit = FALSE;
6846
6847 #if USE_QUICKSAND_IMPACT_BUGFIX
6848     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6849     {
6850       RemoveMovingField(x, y + 1);
6851       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6852       Tile[x][y + 2] = EL_ROCK;
6853       TEST_DrawLevelField(x, y + 2);
6854
6855       object_hit = TRUE;
6856     }
6857
6858     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6859     {
6860       RemoveMovingField(x, y + 1);
6861       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6862       Tile[x][y + 2] = EL_ROCK;
6863       TEST_DrawLevelField(x, y + 2);
6864
6865       object_hit = TRUE;
6866     }
6867 #endif
6868
6869     if (object_hit)
6870       smashed = MovingOrBlocked2Element(x, y + 1);
6871
6872     impact = (last_line || object_hit);
6873   }
6874
6875   if (!last_line && smashed == EL_ACID) // element falls into acid
6876   {
6877     SplashAcid(x, y + 1);
6878     return;
6879   }
6880
6881   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6882   // only reset graphic animation if graphic really changes after impact
6883   if (impact &&
6884       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6885   {
6886     ResetGfxAnimation(x, y);
6887     TEST_DrawLevelField(x, y);
6888   }
6889
6890   if (impact && CAN_EXPLODE_IMPACT(element))
6891   {
6892     Bang(x, y);
6893     return;
6894   }
6895   else if (impact && element == EL_PEARL &&
6896            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6897   {
6898     ResetGfxAnimation(x, y);
6899
6900     Tile[x][y] = EL_PEARL_BREAKING;
6901     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6902     return;
6903   }
6904   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6905   {
6906     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6907
6908     return;
6909   }
6910
6911   if (impact && element == EL_AMOEBA_DROP)
6912   {
6913     if (object_hit && IS_PLAYER(x, y + 1))
6914       KillPlayerUnlessEnemyProtected(x, y + 1);
6915     else if (object_hit && smashed == EL_PENGUIN)
6916       Bang(x, y + 1);
6917     else
6918     {
6919       Tile[x][y] = EL_AMOEBA_GROWING;
6920       Store[x][y] = EL_AMOEBA_WET;
6921
6922       ResetRandomAnimationValue(x, y);
6923     }
6924     return;
6925   }
6926
6927   if (object_hit)               // check which object was hit
6928   {
6929     if ((CAN_PASS_MAGIC_WALL(element) && 
6930          (smashed == EL_MAGIC_WALL ||
6931           smashed == EL_BD_MAGIC_WALL)) ||
6932         (CAN_PASS_DC_MAGIC_WALL(element) &&
6933          smashed == EL_DC_MAGIC_WALL))
6934     {
6935       int xx, yy;
6936       int activated_magic_wall =
6937         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6938          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6939          EL_DC_MAGIC_WALL_ACTIVE);
6940
6941       // activate magic wall / mill
6942       SCAN_PLAYFIELD(xx, yy)
6943       {
6944         if (Tile[xx][yy] == smashed)
6945           Tile[xx][yy] = activated_magic_wall;
6946       }
6947
6948       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6949       game.magic_wall_active = TRUE;
6950
6951       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6952                             SND_MAGIC_WALL_ACTIVATING :
6953                             smashed == EL_BD_MAGIC_WALL ?
6954                             SND_BD_MAGIC_WALL_ACTIVATING :
6955                             SND_DC_MAGIC_WALL_ACTIVATING));
6956     }
6957
6958     if (IS_PLAYER(x, y + 1))
6959     {
6960       if (CAN_SMASH_PLAYER(element))
6961       {
6962         KillPlayerUnlessEnemyProtected(x, y + 1);
6963         return;
6964       }
6965     }
6966     else if (smashed == EL_PENGUIN)
6967     {
6968       if (CAN_SMASH_PLAYER(element))
6969       {
6970         Bang(x, y + 1);
6971         return;
6972       }
6973     }
6974     else if (element == EL_BD_DIAMOND)
6975     {
6976       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6977       {
6978         Bang(x, y + 1);
6979         return;
6980       }
6981     }
6982     else if (((element == EL_SP_INFOTRON ||
6983                element == EL_SP_ZONK) &&
6984               (smashed == EL_SP_SNIKSNAK ||
6985                smashed == EL_SP_ELECTRON ||
6986                smashed == EL_SP_DISK_ORANGE)) ||
6987              (element == EL_SP_INFOTRON &&
6988               smashed == EL_SP_DISK_YELLOW))
6989     {
6990       Bang(x, y + 1);
6991       return;
6992     }
6993     else if (CAN_SMASH_EVERYTHING(element))
6994     {
6995       if (IS_CLASSIC_ENEMY(smashed) ||
6996           CAN_EXPLODE_SMASHED(smashed))
6997       {
6998         Bang(x, y + 1);
6999         return;
7000       }
7001       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7002       {
7003         if (smashed == EL_LAMP ||
7004             smashed == EL_LAMP_ACTIVE)
7005         {
7006           Bang(x, y + 1);
7007           return;
7008         }
7009         else if (smashed == EL_NUT)
7010         {
7011           Tile[x][y + 1] = EL_NUT_BREAKING;
7012           PlayLevelSound(x, y, SND_NUT_BREAKING);
7013           RaiseScoreElement(EL_NUT);
7014           return;
7015         }
7016         else if (smashed == EL_PEARL)
7017         {
7018           ResetGfxAnimation(x, y);
7019
7020           Tile[x][y + 1] = EL_PEARL_BREAKING;
7021           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7022           return;
7023         }
7024         else if (smashed == EL_DIAMOND)
7025         {
7026           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7027           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7028           return;
7029         }
7030         else if (IS_BELT_SWITCH(smashed))
7031         {
7032           ToggleBeltSwitch(x, y + 1);
7033         }
7034         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7035                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7036                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7037                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7038         {
7039           ToggleSwitchgateSwitch();
7040         }
7041         else if (smashed == EL_LIGHT_SWITCH ||
7042                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7043         {
7044           ToggleLightSwitch(x, y + 1);
7045         }
7046         else
7047         {
7048           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7049
7050           CheckElementChangeBySide(x, y + 1, smashed, element,
7051                                    CE_SWITCHED, CH_SIDE_TOP);
7052           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7053                                             CH_SIDE_TOP);
7054         }
7055       }
7056       else
7057       {
7058         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7059       }
7060     }
7061   }
7062
7063   // play sound of magic wall / mill
7064   if (!last_line &&
7065       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7066        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7067        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7068   {
7069     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7070       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7071     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7072       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7073     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7074       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7075
7076     return;
7077   }
7078
7079   // play sound of object that hits the ground
7080   if (last_line || object_hit)
7081     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7082 }
7083
7084 static void TurnRoundExt(int x, int y)
7085 {
7086   static struct
7087   {
7088     int dx, dy;
7089   } move_xy[] =
7090   {
7091     {  0,  0 },
7092     { -1,  0 },
7093     { +1,  0 },
7094     {  0,  0 },
7095     {  0, -1 },
7096     {  0,  0 }, { 0, 0 }, { 0, 0 },
7097     {  0, +1 }
7098   };
7099   static struct
7100   {
7101     int left, right, back;
7102   } turn[] =
7103   {
7104     { 0,        0,              0        },
7105     { MV_DOWN,  MV_UP,          MV_RIGHT },
7106     { MV_UP,    MV_DOWN,        MV_LEFT  },
7107     { 0,        0,              0        },
7108     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7109     { 0,        0,              0        },
7110     { 0,        0,              0        },
7111     { 0,        0,              0        },
7112     { MV_RIGHT, MV_LEFT,        MV_UP    }
7113   };
7114
7115   int element = Tile[x][y];
7116   int move_pattern = element_info[element].move_pattern;
7117
7118   int old_move_dir = MovDir[x][y];
7119   int left_dir  = turn[old_move_dir].left;
7120   int right_dir = turn[old_move_dir].right;
7121   int back_dir  = turn[old_move_dir].back;
7122
7123   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7124   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7125   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7126   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7127
7128   int left_x  = x + left_dx,  left_y  = y + left_dy;
7129   int right_x = x + right_dx, right_y = y + right_dy;
7130   int move_x  = x + move_dx,  move_y  = y + move_dy;
7131
7132   int xx, yy;
7133
7134   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7135   {
7136     TestIfBadThingTouchesOtherBadThing(x, y);
7137
7138     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7139       MovDir[x][y] = right_dir;
7140     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7141       MovDir[x][y] = left_dir;
7142
7143     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7144       MovDelay[x][y] = 9;
7145     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7146       MovDelay[x][y] = 1;
7147   }
7148   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7149   {
7150     TestIfBadThingTouchesOtherBadThing(x, y);
7151
7152     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7153       MovDir[x][y] = left_dir;
7154     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7155       MovDir[x][y] = right_dir;
7156
7157     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7158       MovDelay[x][y] = 9;
7159     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7160       MovDelay[x][y] = 1;
7161   }
7162   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7163   {
7164     TestIfBadThingTouchesOtherBadThing(x, y);
7165
7166     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7167       MovDir[x][y] = left_dir;
7168     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7169       MovDir[x][y] = right_dir;
7170
7171     if (MovDir[x][y] != old_move_dir)
7172       MovDelay[x][y] = 9;
7173   }
7174   else if (element == EL_YAMYAM)
7175   {
7176     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7177     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7178
7179     if (can_turn_left && can_turn_right)
7180       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7181     else if (can_turn_left)
7182       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7183     else if (can_turn_right)
7184       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7185     else
7186       MovDir[x][y] = back_dir;
7187
7188     MovDelay[x][y] = 16 + 16 * RND(3);
7189   }
7190   else if (element == EL_DARK_YAMYAM)
7191   {
7192     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7193                                                          left_x, left_y);
7194     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7195                                                          right_x, right_y);
7196
7197     if (can_turn_left && can_turn_right)
7198       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7199     else if (can_turn_left)
7200       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7201     else if (can_turn_right)
7202       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7203     else
7204       MovDir[x][y] = back_dir;
7205
7206     MovDelay[x][y] = 16 + 16 * RND(3);
7207   }
7208   else if (element == EL_PACMAN)
7209   {
7210     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7211     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7212
7213     if (can_turn_left && can_turn_right)
7214       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7215     else if (can_turn_left)
7216       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7217     else if (can_turn_right)
7218       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7219     else
7220       MovDir[x][y] = back_dir;
7221
7222     MovDelay[x][y] = 6 + RND(40);
7223   }
7224   else if (element == EL_PIG)
7225   {
7226     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7227     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7228     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7229     boolean should_turn_left, should_turn_right, should_move_on;
7230     int rnd_value = 24;
7231     int rnd = RND(rnd_value);
7232
7233     should_turn_left = (can_turn_left &&
7234                         (!can_move_on ||
7235                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7236                                                    y + back_dy + left_dy)));
7237     should_turn_right = (can_turn_right &&
7238                          (!can_move_on ||
7239                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7240                                                     y + back_dy + right_dy)));
7241     should_move_on = (can_move_on &&
7242                       (!can_turn_left ||
7243                        !can_turn_right ||
7244                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7245                                                  y + move_dy + left_dy) ||
7246                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7247                                                  y + move_dy + right_dy)));
7248
7249     if (should_turn_left || should_turn_right || should_move_on)
7250     {
7251       if (should_turn_left && should_turn_right && should_move_on)
7252         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7253                         rnd < 2 * rnd_value / 3 ? right_dir :
7254                         old_move_dir);
7255       else if (should_turn_left && should_turn_right)
7256         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7257       else if (should_turn_left && should_move_on)
7258         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7259       else if (should_turn_right && should_move_on)
7260         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7261       else if (should_turn_left)
7262         MovDir[x][y] = left_dir;
7263       else if (should_turn_right)
7264         MovDir[x][y] = right_dir;
7265       else if (should_move_on)
7266         MovDir[x][y] = old_move_dir;
7267     }
7268     else if (can_move_on && rnd > rnd_value / 8)
7269       MovDir[x][y] = old_move_dir;
7270     else if (can_turn_left && can_turn_right)
7271       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7272     else if (can_turn_left && rnd > rnd_value / 8)
7273       MovDir[x][y] = left_dir;
7274     else if (can_turn_right && rnd > rnd_value/8)
7275       MovDir[x][y] = right_dir;
7276     else
7277       MovDir[x][y] = back_dir;
7278
7279     xx = x + move_xy[MovDir[x][y]].dx;
7280     yy = y + move_xy[MovDir[x][y]].dy;
7281
7282     if (!IN_LEV_FIELD(xx, yy) ||
7283         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7284       MovDir[x][y] = old_move_dir;
7285
7286     MovDelay[x][y] = 0;
7287   }
7288   else if (element == EL_DRAGON)
7289   {
7290     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7291     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7292     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7293     int rnd_value = 24;
7294     int rnd = RND(rnd_value);
7295
7296     if (can_move_on && rnd > rnd_value / 8)
7297       MovDir[x][y] = old_move_dir;
7298     else if (can_turn_left && can_turn_right)
7299       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7300     else if (can_turn_left && rnd > rnd_value / 8)
7301       MovDir[x][y] = left_dir;
7302     else if (can_turn_right && rnd > rnd_value / 8)
7303       MovDir[x][y] = right_dir;
7304     else
7305       MovDir[x][y] = back_dir;
7306
7307     xx = x + move_xy[MovDir[x][y]].dx;
7308     yy = y + move_xy[MovDir[x][y]].dy;
7309
7310     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7311       MovDir[x][y] = old_move_dir;
7312
7313     MovDelay[x][y] = 0;
7314   }
7315   else if (element == EL_MOLE)
7316   {
7317     boolean can_move_on =
7318       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7319                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7320                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7321     if (!can_move_on)
7322     {
7323       boolean can_turn_left =
7324         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7325                               IS_AMOEBOID(Tile[left_x][left_y])));
7326
7327       boolean can_turn_right =
7328         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7329                               IS_AMOEBOID(Tile[right_x][right_y])));
7330
7331       if (can_turn_left && can_turn_right)
7332         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7333       else if (can_turn_left)
7334         MovDir[x][y] = left_dir;
7335       else
7336         MovDir[x][y] = right_dir;
7337     }
7338
7339     if (MovDir[x][y] != old_move_dir)
7340       MovDelay[x][y] = 9;
7341   }
7342   else if (element == EL_BALLOON)
7343   {
7344     MovDir[x][y] = game.wind_direction;
7345     MovDelay[x][y] = 0;
7346   }
7347   else if (element == EL_SPRING)
7348   {
7349     if (MovDir[x][y] & MV_HORIZONTAL)
7350     {
7351       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7352           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7353       {
7354         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7355         ResetGfxAnimation(move_x, move_y);
7356         TEST_DrawLevelField(move_x, move_y);
7357
7358         MovDir[x][y] = back_dir;
7359       }
7360       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7361                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7362         MovDir[x][y] = MV_NONE;
7363     }
7364
7365     MovDelay[x][y] = 0;
7366   }
7367   else if (element == EL_ROBOT ||
7368            element == EL_SATELLITE ||
7369            element == EL_PENGUIN ||
7370            element == EL_EMC_ANDROID)
7371   {
7372     int attr_x = -1, attr_y = -1;
7373
7374     if (game.all_players_gone)
7375     {
7376       attr_x = game.exit_x;
7377       attr_y = game.exit_y;
7378     }
7379     else
7380     {
7381       int i;
7382
7383       for (i = 0; i < MAX_PLAYERS; i++)
7384       {
7385         struct PlayerInfo *player = &stored_player[i];
7386         int jx = player->jx, jy = player->jy;
7387
7388         if (!player->active)
7389           continue;
7390
7391         if (attr_x == -1 ||
7392             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7393         {
7394           attr_x = jx;
7395           attr_y = jy;
7396         }
7397       }
7398     }
7399
7400     if (element == EL_ROBOT &&
7401         game.robot_wheel_x >= 0 &&
7402         game.robot_wheel_y >= 0 &&
7403         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7404          game.engine_version < VERSION_IDENT(3,1,0,0)))
7405     {
7406       attr_x = game.robot_wheel_x;
7407       attr_y = game.robot_wheel_y;
7408     }
7409
7410     if (element == EL_PENGUIN)
7411     {
7412       int i;
7413       struct XY *xy = xy_topdown;
7414
7415       for (i = 0; i < NUM_DIRECTIONS; i++)
7416       {
7417         int ex = x + xy[i].x;
7418         int ey = y + xy[i].y;
7419
7420         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7421                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7422                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7423                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7424         {
7425           attr_x = ex;
7426           attr_y = ey;
7427           break;
7428         }
7429       }
7430     }
7431
7432     MovDir[x][y] = MV_NONE;
7433     if (attr_x < x)
7434       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7435     else if (attr_x > x)
7436       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7437     if (attr_y < y)
7438       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7439     else if (attr_y > y)
7440       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7441
7442     if (element == EL_ROBOT)
7443     {
7444       int newx, newy;
7445
7446       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7447         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7448       Moving2Blocked(x, y, &newx, &newy);
7449
7450       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7451         MovDelay[x][y] = 8 + 8 * !RND(3);
7452       else
7453         MovDelay[x][y] = 16;
7454     }
7455     else if (element == EL_PENGUIN)
7456     {
7457       int newx, newy;
7458
7459       MovDelay[x][y] = 1;
7460
7461       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7462       {
7463         boolean first_horiz = RND(2);
7464         int new_move_dir = MovDir[x][y];
7465
7466         MovDir[x][y] =
7467           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7468         Moving2Blocked(x, y, &newx, &newy);
7469
7470         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7471           return;
7472
7473         MovDir[x][y] =
7474           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7475         Moving2Blocked(x, y, &newx, &newy);
7476
7477         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7478           return;
7479
7480         MovDir[x][y] = old_move_dir;
7481         return;
7482       }
7483     }
7484     else if (element == EL_SATELLITE)
7485     {
7486       int newx, newy;
7487
7488       MovDelay[x][y] = 1;
7489
7490       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7491       {
7492         boolean first_horiz = RND(2);
7493         int new_move_dir = MovDir[x][y];
7494
7495         MovDir[x][y] =
7496           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7497         Moving2Blocked(x, y, &newx, &newy);
7498
7499         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7500           return;
7501
7502         MovDir[x][y] =
7503           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7504         Moving2Blocked(x, y, &newx, &newy);
7505
7506         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7507           return;
7508
7509         MovDir[x][y] = old_move_dir;
7510         return;
7511       }
7512     }
7513     else if (element == EL_EMC_ANDROID)
7514     {
7515       static int check_pos[16] =
7516       {
7517         -1,             //  0 => (invalid)
7518         7,              //  1 => MV_LEFT
7519         3,              //  2 => MV_RIGHT
7520         -1,             //  3 => (invalid)
7521         1,              //  4 =>            MV_UP
7522         0,              //  5 => MV_LEFT  | MV_UP
7523         2,              //  6 => MV_RIGHT | MV_UP
7524         -1,             //  7 => (invalid)
7525         5,              //  8 =>            MV_DOWN
7526         6,              //  9 => MV_LEFT  | MV_DOWN
7527         4,              // 10 => MV_RIGHT | MV_DOWN
7528         -1,             // 11 => (invalid)
7529         -1,             // 12 => (invalid)
7530         -1,             // 13 => (invalid)
7531         -1,             // 14 => (invalid)
7532         -1,             // 15 => (invalid)
7533       };
7534       static struct
7535       {
7536         int dx, dy;
7537         int dir;
7538       } check_xy[8] =
7539       {
7540         { -1, -1,       MV_LEFT  | MV_UP   },
7541         {  0, -1,                  MV_UP   },
7542         { +1, -1,       MV_RIGHT | MV_UP   },
7543         { +1,  0,       MV_RIGHT           },
7544         { +1, +1,       MV_RIGHT | MV_DOWN },
7545         {  0, +1,                  MV_DOWN },
7546         { -1, +1,       MV_LEFT  | MV_DOWN },
7547         { -1,  0,       MV_LEFT            },
7548       };
7549       int start_pos, check_order;
7550       boolean can_clone = FALSE;
7551       int i;
7552
7553       // check if there is any free field around current position
7554       for (i = 0; i < 8; i++)
7555       {
7556         int newx = x + check_xy[i].dx;
7557         int newy = y + check_xy[i].dy;
7558
7559         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7560         {
7561           can_clone = TRUE;
7562
7563           break;
7564         }
7565       }
7566
7567       if (can_clone)            // randomly find an element to clone
7568       {
7569         can_clone = FALSE;
7570
7571         start_pos = check_pos[RND(8)];
7572         check_order = (RND(2) ? -1 : +1);
7573
7574         for (i = 0; i < 8; i++)
7575         {
7576           int pos_raw = start_pos + i * check_order;
7577           int pos = (pos_raw + 8) % 8;
7578           int newx = x + check_xy[pos].dx;
7579           int newy = y + check_xy[pos].dy;
7580
7581           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7582           {
7583             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7584             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7585
7586             Store[x][y] = Tile[newx][newy];
7587
7588             can_clone = TRUE;
7589
7590             break;
7591           }
7592         }
7593       }
7594
7595       if (can_clone)            // randomly find a direction to move
7596       {
7597         can_clone = FALSE;
7598
7599         start_pos = check_pos[RND(8)];
7600         check_order = (RND(2) ? -1 : +1);
7601
7602         for (i = 0; i < 8; i++)
7603         {
7604           int pos_raw = start_pos + i * check_order;
7605           int pos = (pos_raw + 8) % 8;
7606           int newx = x + check_xy[pos].dx;
7607           int newy = y + check_xy[pos].dy;
7608           int new_move_dir = check_xy[pos].dir;
7609
7610           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7611           {
7612             MovDir[x][y] = new_move_dir;
7613             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7614
7615             can_clone = TRUE;
7616
7617             break;
7618           }
7619         }
7620       }
7621
7622       if (can_clone)            // cloning and moving successful
7623         return;
7624
7625       // cannot clone -- try to move towards player
7626
7627       start_pos = check_pos[MovDir[x][y] & 0x0f];
7628       check_order = (RND(2) ? -1 : +1);
7629
7630       for (i = 0; i < 3; i++)
7631       {
7632         // first check start_pos, then previous/next or (next/previous) pos
7633         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7634         int pos = (pos_raw + 8) % 8;
7635         int newx = x + check_xy[pos].dx;
7636         int newy = y + check_xy[pos].dy;
7637         int new_move_dir = check_xy[pos].dir;
7638
7639         if (IS_PLAYER(newx, newy))
7640           break;
7641
7642         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7643         {
7644           MovDir[x][y] = new_move_dir;
7645           MovDelay[x][y] = level.android_move_time * 8 + 1;
7646
7647           break;
7648         }
7649       }
7650     }
7651   }
7652   else if (move_pattern == MV_TURNING_LEFT ||
7653            move_pattern == MV_TURNING_RIGHT ||
7654            move_pattern == MV_TURNING_LEFT_RIGHT ||
7655            move_pattern == MV_TURNING_RIGHT_LEFT ||
7656            move_pattern == MV_TURNING_RANDOM ||
7657            move_pattern == MV_ALL_DIRECTIONS)
7658   {
7659     boolean can_turn_left =
7660       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7661     boolean can_turn_right =
7662       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7663
7664     if (element_info[element].move_stepsize == 0)       // "not moving"
7665       return;
7666
7667     if (move_pattern == MV_TURNING_LEFT)
7668       MovDir[x][y] = left_dir;
7669     else if (move_pattern == MV_TURNING_RIGHT)
7670       MovDir[x][y] = right_dir;
7671     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7672       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7673     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7674       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7675     else if (move_pattern == MV_TURNING_RANDOM)
7676       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7677                       can_turn_right && !can_turn_left ? right_dir :
7678                       RND(2) ? left_dir : right_dir);
7679     else if (can_turn_left && can_turn_right)
7680       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7681     else if (can_turn_left)
7682       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7683     else if (can_turn_right)
7684       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7685     else
7686       MovDir[x][y] = back_dir;
7687
7688     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7689   }
7690   else if (move_pattern == MV_HORIZONTAL ||
7691            move_pattern == MV_VERTICAL)
7692   {
7693     if (move_pattern & old_move_dir)
7694       MovDir[x][y] = back_dir;
7695     else if (move_pattern == MV_HORIZONTAL)
7696       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7697     else if (move_pattern == MV_VERTICAL)
7698       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7699
7700     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7701   }
7702   else if (move_pattern & MV_ANY_DIRECTION)
7703   {
7704     MovDir[x][y] = move_pattern;
7705     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7706   }
7707   else if (move_pattern & MV_WIND_DIRECTION)
7708   {
7709     MovDir[x][y] = game.wind_direction;
7710     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7711   }
7712   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7713   {
7714     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7715       MovDir[x][y] = left_dir;
7716     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7717       MovDir[x][y] = right_dir;
7718
7719     if (MovDir[x][y] != old_move_dir)
7720       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7721   }
7722   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7723   {
7724     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7725       MovDir[x][y] = right_dir;
7726     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7727       MovDir[x][y] = left_dir;
7728
7729     if (MovDir[x][y] != old_move_dir)
7730       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7731   }
7732   else if (move_pattern == MV_TOWARDS_PLAYER ||
7733            move_pattern == MV_AWAY_FROM_PLAYER)
7734   {
7735     int attr_x = -1, attr_y = -1;
7736     int newx, newy;
7737     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7738
7739     if (game.all_players_gone)
7740     {
7741       attr_x = game.exit_x;
7742       attr_y = game.exit_y;
7743     }
7744     else
7745     {
7746       int i;
7747
7748       for (i = 0; i < MAX_PLAYERS; i++)
7749       {
7750         struct PlayerInfo *player = &stored_player[i];
7751         int jx = player->jx, jy = player->jy;
7752
7753         if (!player->active)
7754           continue;
7755
7756         if (attr_x == -1 ||
7757             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7758         {
7759           attr_x = jx;
7760           attr_y = jy;
7761         }
7762       }
7763     }
7764
7765     MovDir[x][y] = MV_NONE;
7766     if (attr_x < x)
7767       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7768     else if (attr_x > x)
7769       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7770     if (attr_y < y)
7771       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7772     else if (attr_y > y)
7773       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7774
7775     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7776
7777     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7778     {
7779       boolean first_horiz = RND(2);
7780       int new_move_dir = MovDir[x][y];
7781
7782       if (element_info[element].move_stepsize == 0)     // "not moving"
7783       {
7784         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7785         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7786
7787         return;
7788       }
7789
7790       MovDir[x][y] =
7791         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7792       Moving2Blocked(x, y, &newx, &newy);
7793
7794       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7795         return;
7796
7797       MovDir[x][y] =
7798         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7799       Moving2Blocked(x, y, &newx, &newy);
7800
7801       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7802         return;
7803
7804       MovDir[x][y] = old_move_dir;
7805     }
7806   }
7807   else if (move_pattern == MV_WHEN_PUSHED ||
7808            move_pattern == MV_WHEN_DROPPED)
7809   {
7810     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7811       MovDir[x][y] = MV_NONE;
7812
7813     MovDelay[x][y] = 0;
7814   }
7815   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7816   {
7817     struct XY *test_xy = xy_topdown;
7818     static int test_dir[4] =
7819     {
7820       MV_UP,
7821       MV_LEFT,
7822       MV_RIGHT,
7823       MV_DOWN
7824     };
7825     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7826     int move_preference = -1000000;     // start with very low preference
7827     int new_move_dir = MV_NONE;
7828     int start_test = RND(4);
7829     int i;
7830
7831     for (i = 0; i < NUM_DIRECTIONS; i++)
7832     {
7833       int j = (start_test + i) % 4;
7834       int move_dir = test_dir[j];
7835       int move_dir_preference;
7836
7837       xx = x + test_xy[j].x;
7838       yy = y + test_xy[j].y;
7839
7840       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7841           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7842       {
7843         new_move_dir = move_dir;
7844
7845         break;
7846       }
7847
7848       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7849         continue;
7850
7851       move_dir_preference = -1 * RunnerVisit[xx][yy];
7852       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7853         move_dir_preference = PlayerVisit[xx][yy];
7854
7855       if (move_dir_preference > move_preference)
7856       {
7857         // prefer field that has not been visited for the longest time
7858         move_preference = move_dir_preference;
7859         new_move_dir = move_dir;
7860       }
7861       else if (move_dir_preference == move_preference &&
7862                move_dir == old_move_dir)
7863       {
7864         // prefer last direction when all directions are preferred equally
7865         move_preference = move_dir_preference;
7866         new_move_dir = move_dir;
7867       }
7868     }
7869
7870     MovDir[x][y] = new_move_dir;
7871     if (old_move_dir != new_move_dir)
7872       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7873   }
7874 }
7875
7876 static void TurnRound(int x, int y)
7877 {
7878   int direction = MovDir[x][y];
7879
7880   TurnRoundExt(x, y);
7881
7882   GfxDir[x][y] = MovDir[x][y];
7883
7884   if (direction != MovDir[x][y])
7885     GfxFrame[x][y] = 0;
7886
7887   if (MovDelay[x][y])
7888     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7889
7890   ResetGfxFrame(x, y);
7891 }
7892
7893 static boolean JustBeingPushed(int x, int y)
7894 {
7895   int i;
7896
7897   for (i = 0; i < MAX_PLAYERS; i++)
7898   {
7899     struct PlayerInfo *player = &stored_player[i];
7900
7901     if (player->active && player->is_pushing && player->MovPos)
7902     {
7903       int next_jx = player->jx + (player->jx - player->last_jx);
7904       int next_jy = player->jy + (player->jy - player->last_jy);
7905
7906       if (x == next_jx && y == next_jy)
7907         return TRUE;
7908     }
7909   }
7910
7911   return FALSE;
7912 }
7913
7914 static void StartMoving(int x, int y)
7915 {
7916   boolean started_moving = FALSE;       // some elements can fall _and_ move
7917   int element = Tile[x][y];
7918
7919   if (Stop[x][y])
7920     return;
7921
7922   if (MovDelay[x][y] == 0)
7923     GfxAction[x][y] = ACTION_DEFAULT;
7924
7925   if (CAN_FALL(element) && y < lev_fieldy - 1)
7926   {
7927     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7928         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7929       if (JustBeingPushed(x, y))
7930         return;
7931
7932     if (element == EL_QUICKSAND_FULL)
7933     {
7934       if (IS_FREE(x, y + 1))
7935       {
7936         InitMovingField(x, y, MV_DOWN);
7937         started_moving = TRUE;
7938
7939         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7940 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7941         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7942           Store[x][y] = EL_ROCK;
7943 #else
7944         Store[x][y] = EL_ROCK;
7945 #endif
7946
7947         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7948       }
7949       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7950       {
7951         if (!MovDelay[x][y])
7952         {
7953           MovDelay[x][y] = TILEY + 1;
7954
7955           ResetGfxAnimation(x, y);
7956           ResetGfxAnimation(x, y + 1);
7957         }
7958
7959         if (MovDelay[x][y])
7960         {
7961           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7962           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7963
7964           MovDelay[x][y]--;
7965           if (MovDelay[x][y])
7966             return;
7967         }
7968
7969         Tile[x][y] = EL_QUICKSAND_EMPTY;
7970         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7971         Store[x][y + 1] = Store[x][y];
7972         Store[x][y] = 0;
7973
7974         PlayLevelSoundAction(x, y, ACTION_FILLING);
7975       }
7976       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7977       {
7978         if (!MovDelay[x][y])
7979         {
7980           MovDelay[x][y] = TILEY + 1;
7981
7982           ResetGfxAnimation(x, y);
7983           ResetGfxAnimation(x, y + 1);
7984         }
7985
7986         if (MovDelay[x][y])
7987         {
7988           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7989           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7990
7991           MovDelay[x][y]--;
7992           if (MovDelay[x][y])
7993             return;
7994         }
7995
7996         Tile[x][y] = EL_QUICKSAND_EMPTY;
7997         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7998         Store[x][y + 1] = Store[x][y];
7999         Store[x][y] = 0;
8000
8001         PlayLevelSoundAction(x, y, ACTION_FILLING);
8002       }
8003     }
8004     else if (element == EL_QUICKSAND_FAST_FULL)
8005     {
8006       if (IS_FREE(x, y + 1))
8007       {
8008         InitMovingField(x, y, MV_DOWN);
8009         started_moving = TRUE;
8010
8011         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8012 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8013         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8014           Store[x][y] = EL_ROCK;
8015 #else
8016         Store[x][y] = EL_ROCK;
8017 #endif
8018
8019         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8020       }
8021       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8022       {
8023         if (!MovDelay[x][y])
8024         {
8025           MovDelay[x][y] = TILEY + 1;
8026
8027           ResetGfxAnimation(x, y);
8028           ResetGfxAnimation(x, y + 1);
8029         }
8030
8031         if (MovDelay[x][y])
8032         {
8033           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8034           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8035
8036           MovDelay[x][y]--;
8037           if (MovDelay[x][y])
8038             return;
8039         }
8040
8041         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8042         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8043         Store[x][y + 1] = Store[x][y];
8044         Store[x][y] = 0;
8045
8046         PlayLevelSoundAction(x, y, ACTION_FILLING);
8047       }
8048       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8049       {
8050         if (!MovDelay[x][y])
8051         {
8052           MovDelay[x][y] = TILEY + 1;
8053
8054           ResetGfxAnimation(x, y);
8055           ResetGfxAnimation(x, y + 1);
8056         }
8057
8058         if (MovDelay[x][y])
8059         {
8060           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8061           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8062
8063           MovDelay[x][y]--;
8064           if (MovDelay[x][y])
8065             return;
8066         }
8067
8068         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8069         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8070         Store[x][y + 1] = Store[x][y];
8071         Store[x][y] = 0;
8072
8073         PlayLevelSoundAction(x, y, ACTION_FILLING);
8074       }
8075     }
8076     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8077              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8078     {
8079       InitMovingField(x, y, MV_DOWN);
8080       started_moving = TRUE;
8081
8082       Tile[x][y] = EL_QUICKSAND_FILLING;
8083       Store[x][y] = element;
8084
8085       PlayLevelSoundAction(x, y, ACTION_FILLING);
8086     }
8087     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8088              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8089     {
8090       InitMovingField(x, y, MV_DOWN);
8091       started_moving = TRUE;
8092
8093       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8094       Store[x][y] = element;
8095
8096       PlayLevelSoundAction(x, y, ACTION_FILLING);
8097     }
8098     else if (element == EL_MAGIC_WALL_FULL)
8099     {
8100       if (IS_FREE(x, y + 1))
8101       {
8102         InitMovingField(x, y, MV_DOWN);
8103         started_moving = TRUE;
8104
8105         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8106         Store[x][y] = EL_CHANGED(Store[x][y]);
8107       }
8108       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8109       {
8110         if (!MovDelay[x][y])
8111           MovDelay[x][y] = TILEY / 4 + 1;
8112
8113         if (MovDelay[x][y])
8114         {
8115           MovDelay[x][y]--;
8116           if (MovDelay[x][y])
8117             return;
8118         }
8119
8120         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8121         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8122         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8123         Store[x][y] = 0;
8124       }
8125     }
8126     else if (element == EL_BD_MAGIC_WALL_FULL)
8127     {
8128       if (IS_FREE(x, y + 1))
8129       {
8130         InitMovingField(x, y, MV_DOWN);
8131         started_moving = TRUE;
8132
8133         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8134         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8135       }
8136       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8137       {
8138         if (!MovDelay[x][y])
8139           MovDelay[x][y] = TILEY / 4 + 1;
8140
8141         if (MovDelay[x][y])
8142         {
8143           MovDelay[x][y]--;
8144           if (MovDelay[x][y])
8145             return;
8146         }
8147
8148         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8149         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8150         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8151         Store[x][y] = 0;
8152       }
8153     }
8154     else if (element == EL_DC_MAGIC_WALL_FULL)
8155     {
8156       if (IS_FREE(x, y + 1))
8157       {
8158         InitMovingField(x, y, MV_DOWN);
8159         started_moving = TRUE;
8160
8161         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8162         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8163       }
8164       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8165       {
8166         if (!MovDelay[x][y])
8167           MovDelay[x][y] = TILEY / 4 + 1;
8168
8169         if (MovDelay[x][y])
8170         {
8171           MovDelay[x][y]--;
8172           if (MovDelay[x][y])
8173             return;
8174         }
8175
8176         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8177         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8178         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8179         Store[x][y] = 0;
8180       }
8181     }
8182     else if ((CAN_PASS_MAGIC_WALL(element) &&
8183               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8184                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8185              (CAN_PASS_DC_MAGIC_WALL(element) &&
8186               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8187
8188     {
8189       InitMovingField(x, y, MV_DOWN);
8190       started_moving = TRUE;
8191
8192       Tile[x][y] =
8193         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8194          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8195          EL_DC_MAGIC_WALL_FILLING);
8196       Store[x][y] = element;
8197     }
8198     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8199     {
8200       SplashAcid(x, y + 1);
8201
8202       InitMovingField(x, y, MV_DOWN);
8203       started_moving = TRUE;
8204
8205       Store[x][y] = EL_ACID;
8206     }
8207     else if (
8208              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8209               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8210              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8211               CAN_FALL(element) && WasJustFalling[x][y] &&
8212               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8213
8214              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8215               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8216               (Tile[x][y + 1] == EL_BLOCKED)))
8217     {
8218       /* this is needed for a special case not covered by calling "Impact()"
8219          from "ContinueMoving()": if an element moves to a tile directly below
8220          another element which was just falling on that tile (which was empty
8221          in the previous frame), the falling element above would just stop
8222          instead of smashing the element below (in previous version, the above
8223          element was just checked for "moving" instead of "falling", resulting
8224          in incorrect smashes caused by horizontal movement of the above
8225          element; also, the case of the player being the element to smash was
8226          simply not covered here... :-/ ) */
8227
8228       CheckCollision[x][y] = 0;
8229       CheckImpact[x][y] = 0;
8230
8231       Impact(x, y);
8232     }
8233     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8234     {
8235       if (MovDir[x][y] == MV_NONE)
8236       {
8237         InitMovingField(x, y, MV_DOWN);
8238         started_moving = TRUE;
8239       }
8240     }
8241     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8242     {
8243       if (WasJustFalling[x][y]) // prevent animation from being restarted
8244         MovDir[x][y] = MV_DOWN;
8245
8246       InitMovingField(x, y, MV_DOWN);
8247       started_moving = TRUE;
8248     }
8249     else if (element == EL_AMOEBA_DROP)
8250     {
8251       Tile[x][y] = EL_AMOEBA_GROWING;
8252       Store[x][y] = EL_AMOEBA_WET;
8253     }
8254     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8255               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8256              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8257              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8258     {
8259       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8260                                 (IS_FREE(x - 1, y + 1) ||
8261                                  Tile[x - 1][y + 1] == EL_ACID));
8262       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8263                                 (IS_FREE(x + 1, y + 1) ||
8264                                  Tile[x + 1][y + 1] == EL_ACID));
8265       boolean can_fall_any  = (can_fall_left || can_fall_right);
8266       boolean can_fall_both = (can_fall_left && can_fall_right);
8267       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8268
8269       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8270       {
8271         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8272           can_fall_right = FALSE;
8273         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8274           can_fall_left = FALSE;
8275         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8276           can_fall_right = FALSE;
8277         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8278           can_fall_left = FALSE;
8279
8280         can_fall_any  = (can_fall_left || can_fall_right);
8281         can_fall_both = FALSE;
8282       }
8283
8284       if (can_fall_both)
8285       {
8286         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8287           can_fall_right = FALSE;       // slip down on left side
8288         else
8289           can_fall_left = !(can_fall_right = RND(2));
8290
8291         can_fall_both = FALSE;
8292       }
8293
8294       if (can_fall_any)
8295       {
8296         // if not determined otherwise, prefer left side for slipping down
8297         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8298         started_moving = TRUE;
8299       }
8300     }
8301     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8302     {
8303       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8304       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8305       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8306       int belt_dir = game.belt_dir[belt_nr];
8307
8308       if ((belt_dir == MV_LEFT  && left_is_free) ||
8309           (belt_dir == MV_RIGHT && right_is_free))
8310       {
8311         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8312
8313         InitMovingField(x, y, belt_dir);
8314         started_moving = TRUE;
8315
8316         Pushed[x][y] = TRUE;
8317         Pushed[nextx][y] = TRUE;
8318
8319         GfxAction[x][y] = ACTION_DEFAULT;
8320       }
8321       else
8322       {
8323         MovDir[x][y] = 0;       // if element was moving, stop it
8324       }
8325     }
8326   }
8327
8328   // not "else if" because of elements that can fall and move (EL_SPRING)
8329   if (CAN_MOVE(element) && !started_moving)
8330   {
8331     int move_pattern = element_info[element].move_pattern;
8332     int newx, newy;
8333
8334     Moving2Blocked(x, y, &newx, &newy);
8335
8336     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8337       return;
8338
8339     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8340         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8341     {
8342       WasJustMoving[x][y] = 0;
8343       CheckCollision[x][y] = 0;
8344
8345       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8346
8347       if (Tile[x][y] != element)        // element has changed
8348         return;
8349     }
8350
8351     if (!MovDelay[x][y])        // start new movement phase
8352     {
8353       // all objects that can change their move direction after each step
8354       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8355
8356       if (element != EL_YAMYAM &&
8357           element != EL_DARK_YAMYAM &&
8358           element != EL_PACMAN &&
8359           !(move_pattern & MV_ANY_DIRECTION) &&
8360           move_pattern != MV_TURNING_LEFT &&
8361           move_pattern != MV_TURNING_RIGHT &&
8362           move_pattern != MV_TURNING_LEFT_RIGHT &&
8363           move_pattern != MV_TURNING_RIGHT_LEFT &&
8364           move_pattern != MV_TURNING_RANDOM)
8365       {
8366         TurnRound(x, y);
8367
8368         if (MovDelay[x][y] && (element == EL_BUG ||
8369                                element == EL_SPACESHIP ||
8370                                element == EL_SP_SNIKSNAK ||
8371                                element == EL_SP_ELECTRON ||
8372                                element == EL_MOLE))
8373           TEST_DrawLevelField(x, y);
8374       }
8375     }
8376
8377     if (MovDelay[x][y])         // wait some time before next movement
8378     {
8379       MovDelay[x][y]--;
8380
8381       if (element == EL_ROBOT ||
8382           element == EL_YAMYAM ||
8383           element == EL_DARK_YAMYAM)
8384       {
8385         DrawLevelElementAnimationIfNeeded(x, y, element);
8386         PlayLevelSoundAction(x, y, ACTION_WAITING);
8387       }
8388       else if (element == EL_SP_ELECTRON)
8389         DrawLevelElementAnimationIfNeeded(x, y, element);
8390       else if (element == EL_DRAGON)
8391       {
8392         int i;
8393         int dir = MovDir[x][y];
8394         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8395         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8396         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8397                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8398                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8399                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8400         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8401
8402         GfxAction[x][y] = ACTION_ATTACKING;
8403
8404         if (IS_PLAYER(x, y))
8405           DrawPlayerField(x, y);
8406         else
8407           TEST_DrawLevelField(x, y);
8408
8409         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8410
8411         for (i = 1; i <= 3; i++)
8412         {
8413           int xx = x + i * dx;
8414           int yy = y + i * dy;
8415           int sx = SCREENX(xx);
8416           int sy = SCREENY(yy);
8417           int flame_graphic = graphic + (i - 1);
8418
8419           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8420             break;
8421
8422           if (MovDelay[x][y])
8423           {
8424             int flamed = MovingOrBlocked2Element(xx, yy);
8425
8426             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8427               Bang(xx, yy);
8428             else
8429               RemoveMovingField(xx, yy);
8430
8431             ChangeDelay[xx][yy] = 0;
8432
8433             Tile[xx][yy] = EL_FLAMES;
8434
8435             if (IN_SCR_FIELD(sx, sy))
8436             {
8437               TEST_DrawLevelFieldCrumbled(xx, yy);
8438               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8439             }
8440           }
8441           else
8442           {
8443             if (Tile[xx][yy] == EL_FLAMES)
8444               Tile[xx][yy] = EL_EMPTY;
8445             TEST_DrawLevelField(xx, yy);
8446           }
8447         }
8448       }
8449
8450       if (MovDelay[x][y])       // element still has to wait some time
8451       {
8452         PlayLevelSoundAction(x, y, ACTION_WAITING);
8453
8454         return;
8455       }
8456     }
8457
8458     // now make next step
8459
8460     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8461
8462     if (DONT_COLLIDE_WITH(element) &&
8463         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8464         !PLAYER_ENEMY_PROTECTED(newx, newy))
8465     {
8466       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8467
8468       return;
8469     }
8470
8471     else if (CAN_MOVE_INTO_ACID(element) &&
8472              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8473              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8474              (MovDir[x][y] == MV_DOWN ||
8475               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8476     {
8477       SplashAcid(newx, newy);
8478       Store[x][y] = EL_ACID;
8479     }
8480     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8481     {
8482       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8483           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8484           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8485           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8486       {
8487         RemoveField(x, y);
8488         TEST_DrawLevelField(x, y);
8489
8490         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8491         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8492           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8493
8494         game.friends_still_needed--;
8495         if (!game.friends_still_needed &&
8496             !game.GameOver &&
8497             game.all_players_gone)
8498           LevelSolved();
8499
8500         return;
8501       }
8502       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8503       {
8504         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8505           TEST_DrawLevelField(newx, newy);
8506         else
8507           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8508       }
8509       else if (!IS_FREE(newx, newy))
8510       {
8511         GfxAction[x][y] = ACTION_WAITING;
8512
8513         if (IS_PLAYER(x, y))
8514           DrawPlayerField(x, y);
8515         else
8516           TEST_DrawLevelField(x, y);
8517
8518         return;
8519       }
8520     }
8521     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8522     {
8523       if (IS_FOOD_PIG(Tile[newx][newy]))
8524       {
8525         if (IS_MOVING(newx, newy))
8526           RemoveMovingField(newx, newy);
8527         else
8528         {
8529           Tile[newx][newy] = EL_EMPTY;
8530           TEST_DrawLevelField(newx, newy);
8531         }
8532
8533         PlayLevelSound(x, y, SND_PIG_DIGGING);
8534       }
8535       else if (!IS_FREE(newx, newy))
8536       {
8537         if (IS_PLAYER(x, y))
8538           DrawPlayerField(x, y);
8539         else
8540           TEST_DrawLevelField(x, y);
8541
8542         return;
8543       }
8544     }
8545     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8546     {
8547       if (Store[x][y] != EL_EMPTY)
8548       {
8549         boolean can_clone = FALSE;
8550         int xx, yy;
8551
8552         // check if element to clone is still there
8553         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8554         {
8555           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8556           {
8557             can_clone = TRUE;
8558
8559             break;
8560           }
8561         }
8562
8563         // cannot clone or target field not free anymore -- do not clone
8564         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8565           Store[x][y] = EL_EMPTY;
8566       }
8567
8568       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8569       {
8570         if (IS_MV_DIAGONAL(MovDir[x][y]))
8571         {
8572           int diagonal_move_dir = MovDir[x][y];
8573           int stored = Store[x][y];
8574           int change_delay = 8;
8575           int graphic;
8576
8577           // android is moving diagonally
8578
8579           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8580
8581           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8582           GfxElement[x][y] = EL_EMC_ANDROID;
8583           GfxAction[x][y] = ACTION_SHRINKING;
8584           GfxDir[x][y] = diagonal_move_dir;
8585           ChangeDelay[x][y] = change_delay;
8586
8587           if (Store[x][y] == EL_EMPTY)
8588             Store[x][y] = GfxElementEmpty[x][y];
8589
8590           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8591                                    GfxDir[x][y]);
8592
8593           DrawLevelGraphicAnimation(x, y, graphic);
8594           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8595
8596           if (Tile[newx][newy] == EL_ACID)
8597           {
8598             SplashAcid(newx, newy);
8599
8600             return;
8601           }
8602
8603           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8604
8605           Store[newx][newy] = EL_EMC_ANDROID;
8606           GfxElement[newx][newy] = EL_EMC_ANDROID;
8607           GfxAction[newx][newy] = ACTION_GROWING;
8608           GfxDir[newx][newy] = diagonal_move_dir;
8609           ChangeDelay[newx][newy] = change_delay;
8610
8611           graphic = el_act_dir2img(GfxElement[newx][newy],
8612                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8613
8614           DrawLevelGraphicAnimation(newx, newy, graphic);
8615           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8616
8617           return;
8618         }
8619         else
8620         {
8621           Tile[newx][newy] = EL_EMPTY;
8622           TEST_DrawLevelField(newx, newy);
8623
8624           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8625         }
8626       }
8627       else if (!IS_FREE(newx, newy))
8628       {
8629         return;
8630       }
8631     }
8632     else if (IS_CUSTOM_ELEMENT(element) &&
8633              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8634     {
8635       if (!DigFieldByCE(newx, newy, element))
8636         return;
8637
8638       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8639       {
8640         RunnerVisit[x][y] = FrameCounter;
8641         PlayerVisit[x][y] /= 8;         // expire player visit path
8642       }
8643     }
8644     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8645     {
8646       if (!IS_FREE(newx, newy))
8647       {
8648         if (IS_PLAYER(x, y))
8649           DrawPlayerField(x, y);
8650         else
8651           TEST_DrawLevelField(x, y);
8652
8653         return;
8654       }
8655       else
8656       {
8657         boolean wanna_flame = !RND(10);
8658         int dx = newx - x, dy = newy - y;
8659         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8660         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8661         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8662                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8663         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8664                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8665
8666         if ((wanna_flame ||
8667              IS_CLASSIC_ENEMY(element1) ||
8668              IS_CLASSIC_ENEMY(element2)) &&
8669             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8670             element1 != EL_FLAMES && element2 != EL_FLAMES)
8671         {
8672           ResetGfxAnimation(x, y);
8673           GfxAction[x][y] = ACTION_ATTACKING;
8674
8675           if (IS_PLAYER(x, y))
8676             DrawPlayerField(x, y);
8677           else
8678             TEST_DrawLevelField(x, y);
8679
8680           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8681
8682           MovDelay[x][y] = 50;
8683
8684           Tile[newx][newy] = EL_FLAMES;
8685           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8686             Tile[newx1][newy1] = EL_FLAMES;
8687           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8688             Tile[newx2][newy2] = EL_FLAMES;
8689
8690           return;
8691         }
8692       }
8693     }
8694     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8695              Tile[newx][newy] == EL_DIAMOND)
8696     {
8697       if (IS_MOVING(newx, newy))
8698         RemoveMovingField(newx, newy);
8699       else
8700       {
8701         Tile[newx][newy] = EL_EMPTY;
8702         TEST_DrawLevelField(newx, newy);
8703       }
8704
8705       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8706     }
8707     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8708              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8709     {
8710       if (AmoebaNr[newx][newy])
8711       {
8712         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8713         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8714             Tile[newx][newy] == EL_BD_AMOEBA)
8715           AmoebaCnt[AmoebaNr[newx][newy]]--;
8716       }
8717
8718       if (IS_MOVING(newx, newy))
8719       {
8720         RemoveMovingField(newx, newy);
8721       }
8722       else
8723       {
8724         Tile[newx][newy] = EL_EMPTY;
8725         TEST_DrawLevelField(newx, newy);
8726       }
8727
8728       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8729     }
8730     else if ((element == EL_PACMAN || element == EL_MOLE)
8731              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8732     {
8733       if (AmoebaNr[newx][newy])
8734       {
8735         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8736         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8737             Tile[newx][newy] == EL_BD_AMOEBA)
8738           AmoebaCnt[AmoebaNr[newx][newy]]--;
8739       }
8740
8741       if (element == EL_MOLE)
8742       {
8743         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8744         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8745
8746         ResetGfxAnimation(x, y);
8747         GfxAction[x][y] = ACTION_DIGGING;
8748         TEST_DrawLevelField(x, y);
8749
8750         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8751
8752         return;                         // wait for shrinking amoeba
8753       }
8754       else      // element == EL_PACMAN
8755       {
8756         Tile[newx][newy] = EL_EMPTY;
8757         TEST_DrawLevelField(newx, newy);
8758         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8759       }
8760     }
8761     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8762              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8763               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8764     {
8765       // wait for shrinking amoeba to completely disappear
8766       return;
8767     }
8768     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8769     {
8770       // object was running against a wall
8771
8772       TurnRound(x, y);
8773
8774       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8775         DrawLevelElementAnimation(x, y, element);
8776
8777       if (DONT_TOUCH(element))
8778         TestIfBadThingTouchesPlayer(x, y);
8779
8780       return;
8781     }
8782
8783     InitMovingField(x, y, MovDir[x][y]);
8784
8785     PlayLevelSoundAction(x, y, ACTION_MOVING);
8786   }
8787
8788   if (MovDir[x][y])
8789     ContinueMoving(x, y);
8790 }
8791
8792 void ContinueMoving(int x, int y)
8793 {
8794   int element = Tile[x][y];
8795   struct ElementInfo *ei = &element_info[element];
8796   int direction = MovDir[x][y];
8797   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8798   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8799   int newx = x + dx, newy = y + dy;
8800   int stored = Store[x][y];
8801   int stored_new = Store[newx][newy];
8802   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8803   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8804   boolean last_line = (newy == lev_fieldy - 1);
8805   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8806
8807   if (pushed_by_player)         // special case: moving object pushed by player
8808   {
8809     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8810   }
8811   else if (use_step_delay)      // special case: moving object has step delay
8812   {
8813     if (!MovDelay[x][y])
8814       MovPos[x][y] += getElementMoveStepsize(x, y);
8815
8816     if (MovDelay[x][y])
8817       MovDelay[x][y]--;
8818     else
8819       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8820
8821     if (MovDelay[x][y])
8822     {
8823       TEST_DrawLevelField(x, y);
8824
8825       return;   // element is still waiting
8826     }
8827   }
8828   else                          // normal case: generically moving object
8829   {
8830     MovPos[x][y] += getElementMoveStepsize(x, y);
8831   }
8832
8833   if (ABS(MovPos[x][y]) < TILEX)
8834   {
8835     TEST_DrawLevelField(x, y);
8836
8837     return;     // element is still moving
8838   }
8839
8840   // element reached destination field
8841
8842   Tile[x][y] = EL_EMPTY;
8843   Tile[newx][newy] = element;
8844   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8845
8846   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8847   {
8848     element = Tile[newx][newy] = EL_ACID;
8849   }
8850   else if (element == EL_MOLE)
8851   {
8852     Tile[x][y] = EL_SAND;
8853
8854     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8855   }
8856   else if (element == EL_QUICKSAND_FILLING)
8857   {
8858     element = Tile[newx][newy] = get_next_element(element);
8859     Store[newx][newy] = Store[x][y];
8860   }
8861   else if (element == EL_QUICKSAND_EMPTYING)
8862   {
8863     Tile[x][y] = get_next_element(element);
8864     element = Tile[newx][newy] = Store[x][y];
8865   }
8866   else if (element == EL_QUICKSAND_FAST_FILLING)
8867   {
8868     element = Tile[newx][newy] = get_next_element(element);
8869     Store[newx][newy] = Store[x][y];
8870   }
8871   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8872   {
8873     Tile[x][y] = get_next_element(element);
8874     element = Tile[newx][newy] = Store[x][y];
8875   }
8876   else if (element == EL_MAGIC_WALL_FILLING)
8877   {
8878     element = Tile[newx][newy] = get_next_element(element);
8879     if (!game.magic_wall_active)
8880       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8881     Store[newx][newy] = Store[x][y];
8882   }
8883   else if (element == EL_MAGIC_WALL_EMPTYING)
8884   {
8885     Tile[x][y] = get_next_element(element);
8886     if (!game.magic_wall_active)
8887       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8888     element = Tile[newx][newy] = Store[x][y];
8889
8890     InitField(newx, newy, FALSE);
8891   }
8892   else if (element == EL_BD_MAGIC_WALL_FILLING)
8893   {
8894     element = Tile[newx][newy] = get_next_element(element);
8895     if (!game.magic_wall_active)
8896       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8897     Store[newx][newy] = Store[x][y];
8898   }
8899   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8900   {
8901     Tile[x][y] = get_next_element(element);
8902     if (!game.magic_wall_active)
8903       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8904     element = Tile[newx][newy] = Store[x][y];
8905
8906     InitField(newx, newy, FALSE);
8907   }
8908   else if (element == EL_DC_MAGIC_WALL_FILLING)
8909   {
8910     element = Tile[newx][newy] = get_next_element(element);
8911     if (!game.magic_wall_active)
8912       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8913     Store[newx][newy] = Store[x][y];
8914   }
8915   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8916   {
8917     Tile[x][y] = get_next_element(element);
8918     if (!game.magic_wall_active)
8919       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8920     element = Tile[newx][newy] = Store[x][y];
8921
8922     InitField(newx, newy, FALSE);
8923   }
8924   else if (element == EL_AMOEBA_DROPPING)
8925   {
8926     Tile[x][y] = get_next_element(element);
8927     element = Tile[newx][newy] = Store[x][y];
8928   }
8929   else if (element == EL_SOKOBAN_OBJECT)
8930   {
8931     if (Back[x][y])
8932       Tile[x][y] = Back[x][y];
8933
8934     if (Back[newx][newy])
8935       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8936
8937     Back[x][y] = Back[newx][newy] = 0;
8938   }
8939
8940   Store[x][y] = EL_EMPTY;
8941   MovPos[x][y] = 0;
8942   MovDir[x][y] = 0;
8943   MovDelay[x][y] = 0;
8944
8945   MovDelay[newx][newy] = 0;
8946
8947   if (CAN_CHANGE_OR_HAS_ACTION(element))
8948   {
8949     // copy element change control values to new field
8950     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8951     ChangePage[newx][newy]  = ChangePage[x][y];
8952     ChangeCount[newx][newy] = ChangeCount[x][y];
8953     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8954   }
8955
8956   CustomValue[newx][newy] = CustomValue[x][y];
8957
8958   ChangeDelay[x][y] = 0;
8959   ChangePage[x][y] = -1;
8960   ChangeCount[x][y] = 0;
8961   ChangeEvent[x][y] = -1;
8962
8963   CustomValue[x][y] = 0;
8964
8965   // copy animation control values to new field
8966   GfxFrame[newx][newy]  = GfxFrame[x][y];
8967   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8968   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8969   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8970
8971   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8972
8973   // some elements can leave other elements behind after moving
8974   if (ei->move_leave_element != EL_EMPTY &&
8975       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8976       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8977   {
8978     int move_leave_element = ei->move_leave_element;
8979
8980     // this makes it possible to leave the removed element again
8981     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8982       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8983
8984     Tile[x][y] = move_leave_element;
8985
8986     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8987       MovDir[x][y] = direction;
8988
8989     InitField(x, y, FALSE);
8990
8991     if (GFX_CRUMBLED(Tile[x][y]))
8992       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8993
8994     if (IS_PLAYER_ELEMENT(move_leave_element))
8995       RelocatePlayer(x, y, move_leave_element);
8996   }
8997
8998   // do this after checking for left-behind element
8999   ResetGfxAnimation(x, y);      // reset animation values for old field
9000
9001   if (!CAN_MOVE(element) ||
9002       (CAN_FALL(element) && direction == MV_DOWN &&
9003        (element == EL_SPRING ||
9004         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9005         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9006     GfxDir[x][y] = MovDir[newx][newy] = 0;
9007
9008   TEST_DrawLevelField(x, y);
9009   TEST_DrawLevelField(newx, newy);
9010
9011   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9012
9013   // prevent pushed element from moving on in pushed direction
9014   if (pushed_by_player && CAN_MOVE(element) &&
9015       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9016       !(element_info[element].move_pattern & direction))
9017     TurnRound(newx, newy);
9018
9019   // prevent elements on conveyor belt from moving on in last direction
9020   if (pushed_by_conveyor && CAN_FALL(element) &&
9021       direction & MV_HORIZONTAL)
9022     MovDir[newx][newy] = 0;
9023
9024   if (!pushed_by_player)
9025   {
9026     int nextx = newx + dx, nexty = newy + dy;
9027     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9028
9029     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9030
9031     if (CAN_FALL(element) && direction == MV_DOWN)
9032       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9033
9034     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9035       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9036
9037     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9038       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9039   }
9040
9041   if (DONT_TOUCH(element))      // object may be nasty to player or others
9042   {
9043     TestIfBadThingTouchesPlayer(newx, newy);
9044     TestIfBadThingTouchesFriend(newx, newy);
9045
9046     if (!IS_CUSTOM_ELEMENT(element))
9047       TestIfBadThingTouchesOtherBadThing(newx, newy);
9048   }
9049   else if (element == EL_PENGUIN)
9050     TestIfFriendTouchesBadThing(newx, newy);
9051
9052   if (DONT_GET_HIT_BY(element))
9053   {
9054     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9055   }
9056
9057   // give the player one last chance (one more frame) to move away
9058   if (CAN_FALL(element) && direction == MV_DOWN &&
9059       (last_line || (!IS_FREE(x, newy + 1) &&
9060                      (!IS_PLAYER(x, newy + 1) ||
9061                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9062     Impact(x, newy);
9063
9064   if (pushed_by_player && !game.use_change_when_pushing_bug)
9065   {
9066     int push_side = MV_DIR_OPPOSITE(direction);
9067     struct PlayerInfo *player = PLAYERINFO(x, y);
9068
9069     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9070                                player->index_bit, push_side);
9071     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9072                                         player->index_bit, push_side);
9073   }
9074
9075   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9076     MovDelay[newx][newy] = 1;
9077
9078   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9079
9080   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9081   TestIfElementHitsCustomElement(newx, newy, direction);
9082   TestIfPlayerTouchesCustomElement(newx, newy);
9083   TestIfElementTouchesCustomElement(newx, newy);
9084
9085   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9086       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9087     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9088                              MV_DIR_OPPOSITE(direction));
9089 }
9090
9091 int AmoebaNeighbourNr(int ax, int ay)
9092 {
9093   int i;
9094   int element = Tile[ax][ay];
9095   int group_nr = 0;
9096   struct XY *xy = xy_topdown;
9097
9098   for (i = 0; i < NUM_DIRECTIONS; i++)
9099   {
9100     int x = ax + xy[i].x;
9101     int y = ay + xy[i].y;
9102
9103     if (!IN_LEV_FIELD(x, y))
9104       continue;
9105
9106     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9107       group_nr = AmoebaNr[x][y];
9108   }
9109
9110   return group_nr;
9111 }
9112
9113 static void AmoebaMerge(int ax, int ay)
9114 {
9115   int i, x, y, xx, yy;
9116   int new_group_nr = AmoebaNr[ax][ay];
9117   struct XY *xy = xy_topdown;
9118
9119   if (new_group_nr == 0)
9120     return;
9121
9122   for (i = 0; i < NUM_DIRECTIONS; i++)
9123   {
9124     x = ax + xy[i].x;
9125     y = ay + xy[i].y;
9126
9127     if (!IN_LEV_FIELD(x, y))
9128       continue;
9129
9130     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9131          Tile[x][y] == EL_BD_AMOEBA ||
9132          Tile[x][y] == EL_AMOEBA_DEAD) &&
9133         AmoebaNr[x][y] != new_group_nr)
9134     {
9135       int old_group_nr = AmoebaNr[x][y];
9136
9137       if (old_group_nr == 0)
9138         return;
9139
9140       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9141       AmoebaCnt[old_group_nr] = 0;
9142       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9143       AmoebaCnt2[old_group_nr] = 0;
9144
9145       SCAN_PLAYFIELD(xx, yy)
9146       {
9147         if (AmoebaNr[xx][yy] == old_group_nr)
9148           AmoebaNr[xx][yy] = new_group_nr;
9149       }
9150     }
9151   }
9152 }
9153
9154 void AmoebaToDiamond(int ax, int ay)
9155 {
9156   int i, x, y;
9157
9158   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9159   {
9160     int group_nr = AmoebaNr[ax][ay];
9161
9162 #ifdef DEBUG
9163     if (group_nr == 0)
9164     {
9165       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9166       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9167
9168       return;
9169     }
9170 #endif
9171
9172     SCAN_PLAYFIELD(x, y)
9173     {
9174       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9175       {
9176         AmoebaNr[x][y] = 0;
9177         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9178       }
9179     }
9180
9181     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9182                             SND_AMOEBA_TURNING_TO_GEM :
9183                             SND_AMOEBA_TURNING_TO_ROCK));
9184     Bang(ax, ay);
9185   }
9186   else
9187   {
9188     struct XY *xy = xy_topdown;
9189
9190     for (i = 0; i < NUM_DIRECTIONS; i++)
9191     {
9192       x = ax + xy[i].x;
9193       y = ay + xy[i].y;
9194
9195       if (!IN_LEV_FIELD(x, y))
9196         continue;
9197
9198       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9199       {
9200         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9201                               SND_AMOEBA_TURNING_TO_GEM :
9202                               SND_AMOEBA_TURNING_TO_ROCK));
9203         Bang(x, y);
9204       }
9205     }
9206   }
9207 }
9208
9209 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9210 {
9211   int x, y;
9212   int group_nr = AmoebaNr[ax][ay];
9213   boolean done = FALSE;
9214
9215 #ifdef DEBUG
9216   if (group_nr == 0)
9217   {
9218     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9219     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9220
9221     return;
9222   }
9223 #endif
9224
9225   SCAN_PLAYFIELD(x, y)
9226   {
9227     if (AmoebaNr[x][y] == group_nr &&
9228         (Tile[x][y] == EL_AMOEBA_DEAD ||
9229          Tile[x][y] == EL_BD_AMOEBA ||
9230          Tile[x][y] == EL_AMOEBA_GROWING))
9231     {
9232       AmoebaNr[x][y] = 0;
9233       Tile[x][y] = new_element;
9234       InitField(x, y, FALSE);
9235       TEST_DrawLevelField(x, y);
9236       done = TRUE;
9237     }
9238   }
9239
9240   if (done)
9241     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9242                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9243                             SND_BD_AMOEBA_TURNING_TO_GEM));
9244 }
9245
9246 static void AmoebaGrowing(int x, int y)
9247 {
9248   static DelayCounter sound_delay = { 0 };
9249
9250   if (!MovDelay[x][y])          // start new growing cycle
9251   {
9252     MovDelay[x][y] = 7;
9253
9254     if (DelayReached(&sound_delay))
9255     {
9256       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9257       sound_delay.value = 30;
9258     }
9259   }
9260
9261   if (MovDelay[x][y])           // wait some time before growing bigger
9262   {
9263     MovDelay[x][y]--;
9264     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9265     {
9266       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9267                                            6 - MovDelay[x][y]);
9268
9269       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9270     }
9271
9272     if (!MovDelay[x][y])
9273     {
9274       Tile[x][y] = Store[x][y];
9275       Store[x][y] = 0;
9276       TEST_DrawLevelField(x, y);
9277     }
9278   }
9279 }
9280
9281 static void AmoebaShrinking(int x, int y)
9282 {
9283   static DelayCounter sound_delay = { 0 };
9284
9285   if (!MovDelay[x][y])          // start new shrinking cycle
9286   {
9287     MovDelay[x][y] = 7;
9288
9289     if (DelayReached(&sound_delay))
9290       sound_delay.value = 30;
9291   }
9292
9293   if (MovDelay[x][y])           // wait some time before shrinking
9294   {
9295     MovDelay[x][y]--;
9296     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9297     {
9298       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9299                                            6 - MovDelay[x][y]);
9300
9301       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9302     }
9303
9304     if (!MovDelay[x][y])
9305     {
9306       Tile[x][y] = EL_EMPTY;
9307       TEST_DrawLevelField(x, y);
9308
9309       // don't let mole enter this field in this cycle;
9310       // (give priority to objects falling to this field from above)
9311       Stop[x][y] = TRUE;
9312     }
9313   }
9314 }
9315
9316 static void AmoebaReproduce(int ax, int ay)
9317 {
9318   int i;
9319   int element = Tile[ax][ay];
9320   int graphic = el2img(element);
9321   int newax = ax, neway = ay;
9322   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9323   struct XY *xy = xy_topdown;
9324
9325   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9326   {
9327     Tile[ax][ay] = EL_AMOEBA_DEAD;
9328     TEST_DrawLevelField(ax, ay);
9329     return;
9330   }
9331
9332   if (IS_ANIMATED(graphic))
9333     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9334
9335   if (!MovDelay[ax][ay])        // start making new amoeba field
9336     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9337
9338   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9339   {
9340     MovDelay[ax][ay]--;
9341     if (MovDelay[ax][ay])
9342       return;
9343   }
9344
9345   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9346   {
9347     int start = RND(4);
9348     int x = ax + xy[start].x;
9349     int y = ay + xy[start].y;
9350
9351     if (!IN_LEV_FIELD(x, y))
9352       return;
9353
9354     if (IS_FREE(x, y) ||
9355         CAN_GROW_INTO(Tile[x][y]) ||
9356         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9357         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9358     {
9359       newax = x;
9360       neway = y;
9361     }
9362
9363     if (newax == ax && neway == ay)
9364       return;
9365   }
9366   else                          // normal or "filled" (BD style) amoeba
9367   {
9368     int start = RND(4);
9369     boolean waiting_for_player = FALSE;
9370
9371     for (i = 0; i < NUM_DIRECTIONS; i++)
9372     {
9373       int j = (start + i) % 4;
9374       int x = ax + xy[j].x;
9375       int y = ay + xy[j].y;
9376
9377       if (!IN_LEV_FIELD(x, y))
9378         continue;
9379
9380       if (IS_FREE(x, y) ||
9381           CAN_GROW_INTO(Tile[x][y]) ||
9382           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9383           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9384       {
9385         newax = x;
9386         neway = y;
9387         break;
9388       }
9389       else if (IS_PLAYER(x, y))
9390         waiting_for_player = TRUE;
9391     }
9392
9393     if (newax == ax && neway == ay)             // amoeba cannot grow
9394     {
9395       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9396       {
9397         Tile[ax][ay] = EL_AMOEBA_DEAD;
9398         TEST_DrawLevelField(ax, ay);
9399         AmoebaCnt[AmoebaNr[ax][ay]]--;
9400
9401         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9402         {
9403           if (element == EL_AMOEBA_FULL)
9404             AmoebaToDiamond(ax, ay);
9405           else if (element == EL_BD_AMOEBA)
9406             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9407         }
9408       }
9409       return;
9410     }
9411     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9412     {
9413       // amoeba gets larger by growing in some direction
9414
9415       int new_group_nr = AmoebaNr[ax][ay];
9416
9417 #ifdef DEBUG
9418   if (new_group_nr == 0)
9419   {
9420     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9421           newax, neway);
9422     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9423
9424     return;
9425   }
9426 #endif
9427
9428       AmoebaNr[newax][neway] = new_group_nr;
9429       AmoebaCnt[new_group_nr]++;
9430       AmoebaCnt2[new_group_nr]++;
9431
9432       // if amoeba touches other amoeba(s) after growing, unify them
9433       AmoebaMerge(newax, neway);
9434
9435       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9436       {
9437         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9438         return;
9439       }
9440     }
9441   }
9442
9443   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9444       (neway == lev_fieldy - 1 && newax != ax))
9445   {
9446     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9447     Store[newax][neway] = element;
9448   }
9449   else if (neway == ay || element == EL_EMC_DRIPPER)
9450   {
9451     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9452
9453     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9454   }
9455   else
9456   {
9457     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9458     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9459     Store[ax][ay] = EL_AMOEBA_DROP;
9460     ContinueMoving(ax, ay);
9461     return;
9462   }
9463
9464   TEST_DrawLevelField(newax, neway);
9465 }
9466
9467 static void Life(int ax, int ay)
9468 {
9469   int x1, y1, x2, y2;
9470   int life_time = 40;
9471   int element = Tile[ax][ay];
9472   int graphic = el2img(element);
9473   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9474                          level.biomaze);
9475   boolean changed = FALSE;
9476
9477   if (IS_ANIMATED(graphic))
9478     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9479
9480   if (Stop[ax][ay])
9481     return;
9482
9483   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9484     MovDelay[ax][ay] = life_time;
9485
9486   if (MovDelay[ax][ay])         // wait some time before next cycle
9487   {
9488     MovDelay[ax][ay]--;
9489     if (MovDelay[ax][ay])
9490       return;
9491   }
9492
9493   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9494   {
9495     int xx = ax + x1, yy = ay + y1;
9496     int old_element = Tile[xx][yy];
9497     int num_neighbours = 0;
9498
9499     if (!IN_LEV_FIELD(xx, yy))
9500       continue;
9501
9502     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9503     {
9504       int x = xx + x2, y = yy + y2;
9505
9506       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9507         continue;
9508
9509       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9510       boolean is_neighbour = FALSE;
9511
9512       if (level.use_life_bugs)
9513         is_neighbour =
9514           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9515            (IS_FREE(x, y)                             &&  Stop[x][y]));
9516       else
9517         is_neighbour =
9518           (Last[x][y] == element || is_player_cell);
9519
9520       if (is_neighbour)
9521         num_neighbours++;
9522     }
9523
9524     boolean is_free = FALSE;
9525
9526     if (level.use_life_bugs)
9527       is_free = (IS_FREE(xx, yy));
9528     else
9529       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9530
9531     if (xx == ax && yy == ay)           // field in the middle
9532     {
9533       if (num_neighbours < life_parameter[0] ||
9534           num_neighbours > life_parameter[1])
9535       {
9536         Tile[xx][yy] = EL_EMPTY;
9537         if (Tile[xx][yy] != old_element)
9538           TEST_DrawLevelField(xx, yy);
9539         Stop[xx][yy] = TRUE;
9540         changed = TRUE;
9541       }
9542     }
9543     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9544     {                                   // free border field
9545       if (num_neighbours >= life_parameter[2] &&
9546           num_neighbours <= life_parameter[3])
9547       {
9548         Tile[xx][yy] = element;
9549         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9550         if (Tile[xx][yy] != old_element)
9551           TEST_DrawLevelField(xx, yy);
9552         Stop[xx][yy] = TRUE;
9553         changed = TRUE;
9554       }
9555     }
9556   }
9557
9558   if (changed)
9559     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9560                    SND_GAME_OF_LIFE_GROWING);
9561 }
9562
9563 static void InitRobotWheel(int x, int y)
9564 {
9565   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9566 }
9567
9568 static void RunRobotWheel(int x, int y)
9569 {
9570   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9571 }
9572
9573 static void StopRobotWheel(int x, int y)
9574 {
9575   if (game.robot_wheel_x == x &&
9576       game.robot_wheel_y == y)
9577   {
9578     game.robot_wheel_x = -1;
9579     game.robot_wheel_y = -1;
9580     game.robot_wheel_active = FALSE;
9581   }
9582 }
9583
9584 static void InitTimegateWheel(int x, int y)
9585 {
9586   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9587 }
9588
9589 static void RunTimegateWheel(int x, int y)
9590 {
9591   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9592 }
9593
9594 static void InitMagicBallDelay(int x, int y)
9595 {
9596   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9597 }
9598
9599 static void ActivateMagicBall(int bx, int by)
9600 {
9601   int x, y;
9602
9603   if (level.ball_random)
9604   {
9605     int pos_border = RND(8);    // select one of the eight border elements
9606     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9607     int xx = pos_content % 3;
9608     int yy = pos_content / 3;
9609
9610     x = bx - 1 + xx;
9611     y = by - 1 + yy;
9612
9613     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9614       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9615   }
9616   else
9617   {
9618     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9619     {
9620       int xx = x - bx + 1;
9621       int yy = y - by + 1;
9622
9623       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9624         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9625     }
9626   }
9627
9628   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9629 }
9630
9631 static void CheckExit(int x, int y)
9632 {
9633   if (game.gems_still_needed > 0 ||
9634       game.sokoban_fields_still_needed > 0 ||
9635       game.sokoban_objects_still_needed > 0 ||
9636       game.lights_still_needed > 0)
9637   {
9638     int element = Tile[x][y];
9639     int graphic = el2img(element);
9640
9641     if (IS_ANIMATED(graphic))
9642       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9643
9644     return;
9645   }
9646
9647   // do not re-open exit door closed after last player
9648   if (game.all_players_gone)
9649     return;
9650
9651   Tile[x][y] = EL_EXIT_OPENING;
9652
9653   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9654 }
9655
9656 static void CheckExitEM(int x, int y)
9657 {
9658   if (game.gems_still_needed > 0 ||
9659       game.sokoban_fields_still_needed > 0 ||
9660       game.sokoban_objects_still_needed > 0 ||
9661       game.lights_still_needed > 0)
9662   {
9663     int element = Tile[x][y];
9664     int graphic = el2img(element);
9665
9666     if (IS_ANIMATED(graphic))
9667       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9668
9669     return;
9670   }
9671
9672   // do not re-open exit door closed after last player
9673   if (game.all_players_gone)
9674     return;
9675
9676   Tile[x][y] = EL_EM_EXIT_OPENING;
9677
9678   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9679 }
9680
9681 static void CheckExitSteel(int x, int y)
9682 {
9683   if (game.gems_still_needed > 0 ||
9684       game.sokoban_fields_still_needed > 0 ||
9685       game.sokoban_objects_still_needed > 0 ||
9686       game.lights_still_needed > 0)
9687   {
9688     int element = Tile[x][y];
9689     int graphic = el2img(element);
9690
9691     if (IS_ANIMATED(graphic))
9692       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9693
9694     return;
9695   }
9696
9697   // do not re-open exit door closed after last player
9698   if (game.all_players_gone)
9699     return;
9700
9701   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9702
9703   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9704 }
9705
9706 static void CheckExitSteelEM(int x, int y)
9707 {
9708   if (game.gems_still_needed > 0 ||
9709       game.sokoban_fields_still_needed > 0 ||
9710       game.sokoban_objects_still_needed > 0 ||
9711       game.lights_still_needed > 0)
9712   {
9713     int element = Tile[x][y];
9714     int graphic = el2img(element);
9715
9716     if (IS_ANIMATED(graphic))
9717       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9718
9719     return;
9720   }
9721
9722   // do not re-open exit door closed after last player
9723   if (game.all_players_gone)
9724     return;
9725
9726   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9727
9728   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9729 }
9730
9731 static void CheckExitSP(int x, int y)
9732 {
9733   if (game.gems_still_needed > 0)
9734   {
9735     int element = Tile[x][y];
9736     int graphic = el2img(element);
9737
9738     if (IS_ANIMATED(graphic))
9739       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9740
9741     return;
9742   }
9743
9744   // do not re-open exit door closed after last player
9745   if (game.all_players_gone)
9746     return;
9747
9748   Tile[x][y] = EL_SP_EXIT_OPENING;
9749
9750   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9751 }
9752
9753 static void CloseAllOpenTimegates(void)
9754 {
9755   int x, y;
9756
9757   SCAN_PLAYFIELD(x, y)
9758   {
9759     int element = Tile[x][y];
9760
9761     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9762     {
9763       Tile[x][y] = EL_TIMEGATE_CLOSING;
9764
9765       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9766     }
9767   }
9768 }
9769
9770 static void DrawTwinkleOnField(int x, int y)
9771 {
9772   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9773     return;
9774
9775   if (Tile[x][y] == EL_BD_DIAMOND)
9776     return;
9777
9778   if (MovDelay[x][y] == 0)      // next animation frame
9779     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9780
9781   if (MovDelay[x][y] != 0)      // wait some time before next frame
9782   {
9783     MovDelay[x][y]--;
9784
9785     DrawLevelElementAnimation(x, y, Tile[x][y]);
9786
9787     if (MovDelay[x][y] != 0)
9788     {
9789       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9790                                            10 - MovDelay[x][y]);
9791
9792       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9793     }
9794   }
9795 }
9796
9797 static void WallGrowing(int x, int y)
9798 {
9799   int delay = 6;
9800
9801   if (!MovDelay[x][y])          // next animation frame
9802     MovDelay[x][y] = 3 * delay;
9803
9804   if (MovDelay[x][y])           // wait some time before next frame
9805   {
9806     MovDelay[x][y]--;
9807
9808     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9809     {
9810       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9811       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9812
9813       DrawLevelGraphic(x, y, graphic, frame);
9814     }
9815
9816     if (!MovDelay[x][y])
9817     {
9818       if (MovDir[x][y] == MV_LEFT)
9819       {
9820         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9821           TEST_DrawLevelField(x - 1, y);
9822       }
9823       else if (MovDir[x][y] == MV_RIGHT)
9824       {
9825         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9826           TEST_DrawLevelField(x + 1, y);
9827       }
9828       else if (MovDir[x][y] == MV_UP)
9829       {
9830         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9831           TEST_DrawLevelField(x, y - 1);
9832       }
9833       else
9834       {
9835         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9836           TEST_DrawLevelField(x, y + 1);
9837       }
9838
9839       Tile[x][y] = Store[x][y];
9840       Store[x][y] = 0;
9841       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9842       TEST_DrawLevelField(x, y);
9843     }
9844   }
9845 }
9846
9847 static void CheckWallGrowing(int ax, int ay)
9848 {
9849   int element = Tile[ax][ay];
9850   int graphic = el2img(element);
9851   boolean free_top    = FALSE;
9852   boolean free_bottom = FALSE;
9853   boolean free_left   = FALSE;
9854   boolean free_right  = FALSE;
9855   boolean stop_top    = FALSE;
9856   boolean stop_bottom = FALSE;
9857   boolean stop_left   = FALSE;
9858   boolean stop_right  = FALSE;
9859   boolean new_wall    = FALSE;
9860
9861   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9862                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9863                            element == EL_EXPANDABLE_STEELWALL_ANY);
9864
9865   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9866                              element == EL_EXPANDABLE_WALL_ANY ||
9867                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9868                              element == EL_EXPANDABLE_STEELWALL_ANY);
9869
9870   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9871                              element == EL_EXPANDABLE_WALL_ANY ||
9872                              element == EL_EXPANDABLE_WALL ||
9873                              element == EL_BD_EXPANDABLE_WALL ||
9874                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9875                              element == EL_EXPANDABLE_STEELWALL_ANY);
9876
9877   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9878                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9879
9880   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9881                              element == EL_EXPANDABLE_WALL ||
9882                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9883
9884   int wall_growing = (is_steelwall ?
9885                       EL_EXPANDABLE_STEELWALL_GROWING :
9886                       EL_EXPANDABLE_WALL_GROWING);
9887
9888   int gfx_wall_growing_up    = (is_steelwall ?
9889                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9890                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9891   int gfx_wall_growing_down  = (is_steelwall ?
9892                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9893                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9894   int gfx_wall_growing_left  = (is_steelwall ?
9895                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9896                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9897   int gfx_wall_growing_right = (is_steelwall ?
9898                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9899                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9900
9901   if (IS_ANIMATED(graphic))
9902     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9903
9904   if (!MovDelay[ax][ay])        // start building new wall
9905     MovDelay[ax][ay] = 6;
9906
9907   if (MovDelay[ax][ay])         // wait some time before building new wall
9908   {
9909     MovDelay[ax][ay]--;
9910     if (MovDelay[ax][ay])
9911       return;
9912   }
9913
9914   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9915     free_top = TRUE;
9916   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9917     free_bottom = TRUE;
9918   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9919     free_left = TRUE;
9920   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9921     free_right = TRUE;
9922
9923   if (grow_vertical)
9924   {
9925     if (free_top)
9926     {
9927       Tile[ax][ay - 1] = wall_growing;
9928       Store[ax][ay - 1] = element;
9929       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9930
9931       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9932         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9933
9934       new_wall = TRUE;
9935     }
9936
9937     if (free_bottom)
9938     {
9939       Tile[ax][ay + 1] = wall_growing;
9940       Store[ax][ay + 1] = element;
9941       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9942
9943       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9944         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9945
9946       new_wall = TRUE;
9947     }
9948   }
9949
9950   if (grow_horizontal)
9951   {
9952     if (free_left)
9953     {
9954       Tile[ax - 1][ay] = wall_growing;
9955       Store[ax - 1][ay] = element;
9956       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9957
9958       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9959         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9960
9961       new_wall = TRUE;
9962     }
9963
9964     if (free_right)
9965     {
9966       Tile[ax + 1][ay] = wall_growing;
9967       Store[ax + 1][ay] = element;
9968       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9969
9970       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9971         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9972
9973       new_wall = TRUE;
9974     }
9975   }
9976
9977   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9978     TEST_DrawLevelField(ax, ay);
9979
9980   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9981     stop_top = TRUE;
9982   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9983     stop_bottom = TRUE;
9984   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9985     stop_left = TRUE;
9986   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9987     stop_right = TRUE;
9988
9989   if (((stop_top && stop_bottom) || stop_horizontal) &&
9990       ((stop_left && stop_right) || stop_vertical))
9991     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9992
9993   if (new_wall)
9994     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9995 }
9996
9997 static void CheckForDragon(int x, int y)
9998 {
9999   int i, j;
10000   boolean dragon_found = FALSE;
10001   struct XY *xy = xy_topdown;
10002
10003   for (i = 0; i < NUM_DIRECTIONS; i++)
10004   {
10005     for (j = 0; j < 4; j++)
10006     {
10007       int xx = x + j * xy[i].x;
10008       int yy = y + j * xy[i].y;
10009
10010       if (IN_LEV_FIELD(xx, yy) &&
10011           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10012       {
10013         if (Tile[xx][yy] == EL_DRAGON)
10014           dragon_found = TRUE;
10015       }
10016       else
10017         break;
10018     }
10019   }
10020
10021   if (!dragon_found)
10022   {
10023     for (i = 0; i < NUM_DIRECTIONS; i++)
10024     {
10025       for (j = 0; j < 3; j++)
10026       {
10027         int xx = x + j * xy[i].x;
10028         int yy = y + j * xy[i].y;
10029
10030         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10031         {
10032           Tile[xx][yy] = EL_EMPTY;
10033           TEST_DrawLevelField(xx, yy);
10034         }
10035         else
10036           break;
10037       }
10038     }
10039   }
10040 }
10041
10042 static void InitBuggyBase(int x, int y)
10043 {
10044   int element = Tile[x][y];
10045   int activating_delay = FRAMES_PER_SECOND / 4;
10046
10047   ChangeDelay[x][y] =
10048     (element == EL_SP_BUGGY_BASE ?
10049      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10050      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10051      activating_delay :
10052      element == EL_SP_BUGGY_BASE_ACTIVE ?
10053      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10054 }
10055
10056 static void WarnBuggyBase(int x, int y)
10057 {
10058   int i;
10059   struct XY *xy = xy_topdown;
10060
10061   for (i = 0; i < NUM_DIRECTIONS; i++)
10062   {
10063     int xx = x + xy[i].x;
10064     int yy = y + xy[i].y;
10065
10066     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10067     {
10068       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10069
10070       break;
10071     }
10072   }
10073 }
10074
10075 static void InitTrap(int x, int y)
10076 {
10077   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10078 }
10079
10080 static void ActivateTrap(int x, int y)
10081 {
10082   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10083 }
10084
10085 static void ChangeActiveTrap(int x, int y)
10086 {
10087   int graphic = IMG_TRAP_ACTIVE;
10088
10089   // if new animation frame was drawn, correct crumbled sand border
10090   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10091     TEST_DrawLevelFieldCrumbled(x, y);
10092 }
10093
10094 static int getSpecialActionElement(int element, int number, int base_element)
10095 {
10096   return (element != EL_EMPTY ? element :
10097           number != -1 ? base_element + number - 1 :
10098           EL_EMPTY);
10099 }
10100
10101 static int getModifiedActionNumber(int value_old, int operator, int operand,
10102                                    int value_min, int value_max)
10103 {
10104   int value_new = (operator == CA_MODE_SET      ? operand :
10105                    operator == CA_MODE_ADD      ? value_old + operand :
10106                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10107                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10108                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10109                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10110                    value_old);
10111
10112   return (value_new < value_min ? value_min :
10113           value_new > value_max ? value_max :
10114           value_new);
10115 }
10116
10117 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10118 {
10119   struct ElementInfo *ei = &element_info[element];
10120   struct ElementChangeInfo *change = &ei->change_page[page];
10121   int target_element = change->target_element;
10122   int action_type = change->action_type;
10123   int action_mode = change->action_mode;
10124   int action_arg = change->action_arg;
10125   int action_element = change->action_element;
10126   int i;
10127
10128   if (!change->has_action)
10129     return;
10130
10131   // ---------- determine action paramater values -----------------------------
10132
10133   int level_time_value =
10134     (level.time > 0 ? TimeLeft :
10135      TimePlayed);
10136
10137   int action_arg_element_raw =
10138     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10139      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10140      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10141      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10142      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10143      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10144      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10145      EL_EMPTY);
10146   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10147
10148   int action_arg_direction =
10149     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10150      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10151      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10152      change->actual_trigger_side :
10153      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10154      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10155      MV_NONE);
10156
10157   int action_arg_number_min =
10158     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10159      CA_ARG_MIN);
10160
10161   int action_arg_number_max =
10162     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10163      action_type == CA_SET_LEVEL_GEMS ? 999 :
10164      action_type == CA_SET_LEVEL_TIME ? 9999 :
10165      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10166      action_type == CA_SET_CE_VALUE ? 9999 :
10167      action_type == CA_SET_CE_SCORE ? 9999 :
10168      CA_ARG_MAX);
10169
10170   int action_arg_number_reset =
10171     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10172      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10173      action_type == CA_SET_LEVEL_TIME ? level.time :
10174      action_type == CA_SET_LEVEL_SCORE ? 0 :
10175      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10176      action_type == CA_SET_CE_SCORE ? 0 :
10177      0);
10178
10179   int action_arg_number =
10180     (action_arg <= CA_ARG_MAX ? action_arg :
10181      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10182      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10183      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10184      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10185      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10186      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10187      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10188      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10189      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10190      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10191      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10192      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10193      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10194      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10195      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10196      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10197      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10198      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10199      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10200      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10201      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10202      -1);
10203
10204   int action_arg_number_old =
10205     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10206      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10207      action_type == CA_SET_LEVEL_SCORE ? game.score :
10208      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10209      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10210      0);
10211
10212   int action_arg_number_new =
10213     getModifiedActionNumber(action_arg_number_old,
10214                             action_mode, action_arg_number,
10215                             action_arg_number_min, action_arg_number_max);
10216
10217   int trigger_player_bits =
10218     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10219      change->actual_trigger_player_bits : change->trigger_player);
10220
10221   int action_arg_player_bits =
10222     (action_arg >= CA_ARG_PLAYER_1 &&
10223      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10224      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10225      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10226      PLAYER_BITS_ANY);
10227
10228   // ---------- execute action  -----------------------------------------------
10229
10230   switch (action_type)
10231   {
10232     case CA_NO_ACTION:
10233     {
10234       return;
10235     }
10236
10237     // ---------- level actions  ----------------------------------------------
10238
10239     case CA_RESTART_LEVEL:
10240     {
10241       game.restart_level = TRUE;
10242
10243       break;
10244     }
10245
10246     case CA_SHOW_ENVELOPE:
10247     {
10248       int element = getSpecialActionElement(action_arg_element,
10249                                             action_arg_number, EL_ENVELOPE_1);
10250
10251       if (IS_ENVELOPE(element))
10252         local_player->show_envelope = element;
10253
10254       break;
10255     }
10256
10257     case CA_SET_LEVEL_TIME:
10258     {
10259       if (level.time > 0)       // only modify limited time value
10260       {
10261         TimeLeft = action_arg_number_new;
10262
10263         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10264
10265         DisplayGameControlValues();
10266
10267         if (!TimeLeft && game.time_limit)
10268           for (i = 0; i < MAX_PLAYERS; i++)
10269             KillPlayer(&stored_player[i]);
10270       }
10271
10272       break;
10273     }
10274
10275     case CA_SET_LEVEL_SCORE:
10276     {
10277       game.score = action_arg_number_new;
10278
10279       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10280
10281       DisplayGameControlValues();
10282
10283       break;
10284     }
10285
10286     case CA_SET_LEVEL_GEMS:
10287     {
10288       game.gems_still_needed = action_arg_number_new;
10289
10290       game.snapshot.collected_item = TRUE;
10291
10292       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10293
10294       DisplayGameControlValues();
10295
10296       break;
10297     }
10298
10299     case CA_SET_LEVEL_WIND:
10300     {
10301       game.wind_direction = action_arg_direction;
10302
10303       break;
10304     }
10305
10306     case CA_SET_LEVEL_RANDOM_SEED:
10307     {
10308       // ensure that setting a new random seed while playing is predictable
10309       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10310
10311       break;
10312     }
10313
10314     // ---------- player actions  ---------------------------------------------
10315
10316     case CA_MOVE_PLAYER:
10317     case CA_MOVE_PLAYER_NEW:
10318     {
10319       // automatically move to the next field in specified direction
10320       for (i = 0; i < MAX_PLAYERS; i++)
10321         if (trigger_player_bits & (1 << i))
10322           if (action_type == CA_MOVE_PLAYER ||
10323               stored_player[i].MovPos == 0)
10324             stored_player[i].programmed_action = action_arg_direction;
10325
10326       break;
10327     }
10328
10329     case CA_EXIT_PLAYER:
10330     {
10331       for (i = 0; i < MAX_PLAYERS; i++)
10332         if (action_arg_player_bits & (1 << i))
10333           ExitPlayer(&stored_player[i]);
10334
10335       if (game.players_still_needed == 0)
10336         LevelSolved();
10337
10338       break;
10339     }
10340
10341     case CA_KILL_PLAYER:
10342     {
10343       for (i = 0; i < MAX_PLAYERS; i++)
10344         if (action_arg_player_bits & (1 << i))
10345           KillPlayer(&stored_player[i]);
10346
10347       break;
10348     }
10349
10350     case CA_SET_PLAYER_KEYS:
10351     {
10352       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10353       int element = getSpecialActionElement(action_arg_element,
10354                                             action_arg_number, EL_KEY_1);
10355
10356       if (IS_KEY(element))
10357       {
10358         for (i = 0; i < MAX_PLAYERS; i++)
10359         {
10360           if (trigger_player_bits & (1 << i))
10361           {
10362             stored_player[i].key[KEY_NR(element)] = key_state;
10363
10364             DrawGameDoorValues();
10365           }
10366         }
10367       }
10368
10369       break;
10370     }
10371
10372     case CA_SET_PLAYER_SPEED:
10373     {
10374       for (i = 0; i < MAX_PLAYERS; i++)
10375       {
10376         if (trigger_player_bits & (1 << i))
10377         {
10378           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10379
10380           if (action_arg == CA_ARG_SPEED_FASTER &&
10381               stored_player[i].cannot_move)
10382           {
10383             action_arg_number = STEPSIZE_VERY_SLOW;
10384           }
10385           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10386                    action_arg == CA_ARG_SPEED_FASTER)
10387           {
10388             action_arg_number = 2;
10389             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10390                            CA_MODE_MULTIPLY);
10391           }
10392           else if (action_arg == CA_ARG_NUMBER_RESET)
10393           {
10394             action_arg_number = level.initial_player_stepsize[i];
10395           }
10396
10397           move_stepsize =
10398             getModifiedActionNumber(move_stepsize,
10399                                     action_mode,
10400                                     action_arg_number,
10401                                     action_arg_number_min,
10402                                     action_arg_number_max);
10403
10404           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10405         }
10406       }
10407
10408       break;
10409     }
10410
10411     case CA_SET_PLAYER_SHIELD:
10412     {
10413       for (i = 0; i < MAX_PLAYERS; i++)
10414       {
10415         if (trigger_player_bits & (1 << i))
10416         {
10417           if (action_arg == CA_ARG_SHIELD_OFF)
10418           {
10419             stored_player[i].shield_normal_time_left = 0;
10420             stored_player[i].shield_deadly_time_left = 0;
10421           }
10422           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10423           {
10424             stored_player[i].shield_normal_time_left = 999999;
10425           }
10426           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10427           {
10428             stored_player[i].shield_normal_time_left = 999999;
10429             stored_player[i].shield_deadly_time_left = 999999;
10430           }
10431         }
10432       }
10433
10434       break;
10435     }
10436
10437     case CA_SET_PLAYER_GRAVITY:
10438     {
10439       for (i = 0; i < MAX_PLAYERS; i++)
10440       {
10441         if (trigger_player_bits & (1 << i))
10442         {
10443           stored_player[i].gravity =
10444             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10445              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10446              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10447              stored_player[i].gravity);
10448         }
10449       }
10450
10451       break;
10452     }
10453
10454     case CA_SET_PLAYER_ARTWORK:
10455     {
10456       for (i = 0; i < MAX_PLAYERS; i++)
10457       {
10458         if (trigger_player_bits & (1 << i))
10459         {
10460           int artwork_element = action_arg_element;
10461
10462           if (action_arg == CA_ARG_ELEMENT_RESET)
10463             artwork_element =
10464               (level.use_artwork_element[i] ? level.artwork_element[i] :
10465                stored_player[i].element_nr);
10466
10467           if (stored_player[i].artwork_element != artwork_element)
10468             stored_player[i].Frame = 0;
10469
10470           stored_player[i].artwork_element = artwork_element;
10471
10472           SetPlayerWaiting(&stored_player[i], FALSE);
10473
10474           // set number of special actions for bored and sleeping animation
10475           stored_player[i].num_special_action_bored =
10476             get_num_special_action(artwork_element,
10477                                    ACTION_BORING_1, ACTION_BORING_LAST);
10478           stored_player[i].num_special_action_sleeping =
10479             get_num_special_action(artwork_element,
10480                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10481         }
10482       }
10483
10484       break;
10485     }
10486
10487     case CA_SET_PLAYER_INVENTORY:
10488     {
10489       for (i = 0; i < MAX_PLAYERS; i++)
10490       {
10491         struct PlayerInfo *player = &stored_player[i];
10492         int j, k;
10493
10494         if (trigger_player_bits & (1 << i))
10495         {
10496           int inventory_element = action_arg_element;
10497
10498           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10499               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10500               action_arg == CA_ARG_ELEMENT_ACTION)
10501           {
10502             int element = inventory_element;
10503             int collect_count = element_info[element].collect_count_initial;
10504
10505             if (!IS_CUSTOM_ELEMENT(element))
10506               collect_count = 1;
10507
10508             if (collect_count == 0)
10509               player->inventory_infinite_element = element;
10510             else
10511               for (k = 0; k < collect_count; k++)
10512                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10513                   player->inventory_element[player->inventory_size++] =
10514                     element;
10515           }
10516           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10517                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10518                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10519           {
10520             if (player->inventory_infinite_element != EL_UNDEFINED &&
10521                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10522                                      action_arg_element_raw))
10523               player->inventory_infinite_element = EL_UNDEFINED;
10524
10525             for (k = 0, j = 0; j < player->inventory_size; j++)
10526             {
10527               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10528                                         action_arg_element_raw))
10529                 player->inventory_element[k++] = player->inventory_element[j];
10530             }
10531
10532             player->inventory_size = k;
10533           }
10534           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10535           {
10536             if (player->inventory_size > 0)
10537             {
10538               for (j = 0; j < player->inventory_size - 1; j++)
10539                 player->inventory_element[j] = player->inventory_element[j + 1];
10540
10541               player->inventory_size--;
10542             }
10543           }
10544           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10545           {
10546             if (player->inventory_size > 0)
10547               player->inventory_size--;
10548           }
10549           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10550           {
10551             player->inventory_infinite_element = EL_UNDEFINED;
10552             player->inventory_size = 0;
10553           }
10554           else if (action_arg == CA_ARG_INVENTORY_RESET)
10555           {
10556             player->inventory_infinite_element = EL_UNDEFINED;
10557             player->inventory_size = 0;
10558
10559             if (level.use_initial_inventory[i])
10560             {
10561               for (j = 0; j < level.initial_inventory_size[i]; j++)
10562               {
10563                 int element = level.initial_inventory_content[i][j];
10564                 int collect_count = element_info[element].collect_count_initial;
10565
10566                 if (!IS_CUSTOM_ELEMENT(element))
10567                   collect_count = 1;
10568
10569                 if (collect_count == 0)
10570                   player->inventory_infinite_element = element;
10571                 else
10572                   for (k = 0; k < collect_count; k++)
10573                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10574                       player->inventory_element[player->inventory_size++] =
10575                         element;
10576               }
10577             }
10578           }
10579         }
10580       }
10581
10582       break;
10583     }
10584
10585     // ---------- CE actions  -------------------------------------------------
10586
10587     case CA_SET_CE_VALUE:
10588     {
10589       int last_ce_value = CustomValue[x][y];
10590
10591       CustomValue[x][y] = action_arg_number_new;
10592
10593       if (CustomValue[x][y] != last_ce_value)
10594       {
10595         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10596         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10597
10598         if (CustomValue[x][y] == 0)
10599         {
10600           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10601           ChangeCount[x][y] = 0;        // allow at least one more change
10602
10603           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10604           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10605         }
10606       }
10607
10608       break;
10609     }
10610
10611     case CA_SET_CE_SCORE:
10612     {
10613       int last_ce_score = ei->collect_score;
10614
10615       ei->collect_score = action_arg_number_new;
10616
10617       if (ei->collect_score != last_ce_score)
10618       {
10619         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10620         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10621
10622         if (ei->collect_score == 0)
10623         {
10624           int xx, yy;
10625
10626           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10627           ChangeCount[x][y] = 0;        // allow at least one more change
10628
10629           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10630           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10631
10632           /*
10633             This is a very special case that seems to be a mixture between
10634             CheckElementChange() and CheckTriggeredElementChange(): while
10635             the first one only affects single elements that are triggered
10636             directly, the second one affects multiple elements in the playfield
10637             that are triggered indirectly by another element. This is a third
10638             case: Changing the CE score always affects multiple identical CEs,
10639             so every affected CE must be checked, not only the single CE for
10640             which the CE score was changed in the first place (as every instance
10641             of that CE shares the same CE score, and therefore also can change)!
10642           */
10643           SCAN_PLAYFIELD(xx, yy)
10644           {
10645             if (Tile[xx][yy] == element)
10646               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10647                                  CE_SCORE_GETS_ZERO);
10648           }
10649         }
10650       }
10651
10652       break;
10653     }
10654
10655     case CA_SET_CE_ARTWORK:
10656     {
10657       int artwork_element = action_arg_element;
10658       boolean reset_frame = FALSE;
10659       int xx, yy;
10660
10661       if (action_arg == CA_ARG_ELEMENT_RESET)
10662         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10663                            element);
10664
10665       if (ei->gfx_element != artwork_element)
10666         reset_frame = TRUE;
10667
10668       ei->gfx_element = artwork_element;
10669
10670       SCAN_PLAYFIELD(xx, yy)
10671       {
10672         if (Tile[xx][yy] == element)
10673         {
10674           if (reset_frame)
10675           {
10676             ResetGfxAnimation(xx, yy);
10677             ResetRandomAnimationValue(xx, yy);
10678           }
10679
10680           TEST_DrawLevelField(xx, yy);
10681         }
10682       }
10683
10684       break;
10685     }
10686
10687     // ---------- engine actions  ---------------------------------------------
10688
10689     case CA_SET_ENGINE_SCAN_MODE:
10690     {
10691       InitPlayfieldScanMode(action_arg);
10692
10693       break;
10694     }
10695
10696     default:
10697       break;
10698   }
10699 }
10700
10701 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10702 {
10703   int old_element = Tile[x][y];
10704   int new_element = GetElementFromGroupElement(element);
10705   int previous_move_direction = MovDir[x][y];
10706   int last_ce_value = CustomValue[x][y];
10707   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10708   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10709   boolean add_player_onto_element = (new_element_is_player &&
10710                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10711                                      IS_WALKABLE(old_element));
10712
10713   if (!add_player_onto_element)
10714   {
10715     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10716       RemoveMovingField(x, y);
10717     else
10718       RemoveField(x, y);
10719
10720     Tile[x][y] = new_element;
10721
10722     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10723       MovDir[x][y] = previous_move_direction;
10724
10725     if (element_info[new_element].use_last_ce_value)
10726       CustomValue[x][y] = last_ce_value;
10727
10728     InitField_WithBug1(x, y, FALSE);
10729
10730     new_element = Tile[x][y];   // element may have changed
10731
10732     ResetGfxAnimation(x, y);
10733     ResetRandomAnimationValue(x, y);
10734
10735     TEST_DrawLevelField(x, y);
10736
10737     if (GFX_CRUMBLED(new_element))
10738       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10739
10740     if (old_element == EL_EXPLOSION)
10741     {
10742       Store[x][y] = Store2[x][y] = 0;
10743
10744       // check if new element replaces an exploding player, requiring cleanup
10745       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10746         StorePlayer[x][y] = 0;
10747     }
10748
10749     // check if element under the player changes from accessible to unaccessible
10750     // (needed for special case of dropping element which then changes)
10751     // (must be checked after creating new element for walkable group elements)
10752     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10753         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10754     {
10755       KillPlayer(PLAYERINFO(x, y));
10756
10757       return;
10758     }
10759   }
10760
10761   // "ChangeCount" not set yet to allow "entered by player" change one time
10762   if (new_element_is_player)
10763     RelocatePlayer(x, y, new_element);
10764
10765   if (is_change)
10766     ChangeCount[x][y]++;        // count number of changes in the same frame
10767
10768   TestIfBadThingTouchesPlayer(x, y);
10769   TestIfPlayerTouchesCustomElement(x, y);
10770   TestIfElementTouchesCustomElement(x, y);
10771 }
10772
10773 static void CreateField(int x, int y, int element)
10774 {
10775   CreateFieldExt(x, y, element, FALSE);
10776 }
10777
10778 static void CreateElementFromChange(int x, int y, int element)
10779 {
10780   element = GET_VALID_RUNTIME_ELEMENT(element);
10781
10782   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10783   {
10784     int old_element = Tile[x][y];
10785
10786     // prevent changed element from moving in same engine frame
10787     // unless both old and new element can either fall or move
10788     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10789         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10790       Stop[x][y] = TRUE;
10791   }
10792
10793   CreateFieldExt(x, y, element, TRUE);
10794 }
10795
10796 static boolean ChangeElement(int x, int y, int element, int page)
10797 {
10798   struct ElementInfo *ei = &element_info[element];
10799   struct ElementChangeInfo *change = &ei->change_page[page];
10800   int ce_value = CustomValue[x][y];
10801   int ce_score = ei->collect_score;
10802   int target_element;
10803   int old_element = Tile[x][y];
10804
10805   // always use default change event to prevent running into a loop
10806   if (ChangeEvent[x][y] == -1)
10807     ChangeEvent[x][y] = CE_DELAY;
10808
10809   if (ChangeEvent[x][y] == CE_DELAY)
10810   {
10811     // reset actual trigger element, trigger player and action element
10812     change->actual_trigger_element = EL_EMPTY;
10813     change->actual_trigger_player = EL_EMPTY;
10814     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10815     change->actual_trigger_side = CH_SIDE_NONE;
10816     change->actual_trigger_ce_value = 0;
10817     change->actual_trigger_ce_score = 0;
10818     change->actual_trigger_x = -1;
10819     change->actual_trigger_y = -1;
10820   }
10821
10822   // do not change elements more than a specified maximum number of changes
10823   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10824     return FALSE;
10825
10826   ChangeCount[x][y]++;          // count number of changes in the same frame
10827
10828   if (ei->has_anim_event)
10829     HandleGlobalAnimEventByElementChange(element, page, x, y,
10830                                          change->actual_trigger_x,
10831                                          change->actual_trigger_y);
10832
10833   if (change->explode)
10834   {
10835     Bang(x, y);
10836
10837     return TRUE;
10838   }
10839
10840   if (change->use_target_content)
10841   {
10842     boolean complete_replace = TRUE;
10843     boolean can_replace[3][3];
10844     int xx, yy;
10845
10846     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10847     {
10848       boolean is_empty;
10849       boolean is_walkable;
10850       boolean is_diggable;
10851       boolean is_collectible;
10852       boolean is_removable;
10853       boolean is_destructible;
10854       int ex = x + xx - 1;
10855       int ey = y + yy - 1;
10856       int content_element = change->target_content.e[xx][yy];
10857       int e;
10858
10859       can_replace[xx][yy] = TRUE;
10860
10861       if (ex == x && ey == y)   // do not check changing element itself
10862         continue;
10863
10864       if (content_element == EL_EMPTY_SPACE)
10865       {
10866         can_replace[xx][yy] = FALSE;    // do not replace border with space
10867
10868         continue;
10869       }
10870
10871       if (!IN_LEV_FIELD(ex, ey))
10872       {
10873         can_replace[xx][yy] = FALSE;
10874         complete_replace = FALSE;
10875
10876         continue;
10877       }
10878
10879       e = Tile[ex][ey];
10880
10881       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10882         e = MovingOrBlocked2Element(ex, ey);
10883
10884       is_empty = (IS_FREE(ex, ey) ||
10885                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10886
10887       is_walkable     = (is_empty || IS_WALKABLE(e));
10888       is_diggable     = (is_empty || IS_DIGGABLE(e));
10889       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10890       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10891       is_removable    = (is_diggable || is_collectible);
10892
10893       can_replace[xx][yy] =
10894         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10895           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10896           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10897           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10898           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10899           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10900          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10901
10902       if (!can_replace[xx][yy])
10903         complete_replace = FALSE;
10904     }
10905
10906     if (!change->only_if_complete || complete_replace)
10907     {
10908       boolean something_has_changed = FALSE;
10909
10910       if (change->only_if_complete && change->use_random_replace &&
10911           RND(100) < change->random_percentage)
10912         return FALSE;
10913
10914       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10915       {
10916         int ex = x + xx - 1;
10917         int ey = y + yy - 1;
10918         int content_element;
10919
10920         if (can_replace[xx][yy] && (!change->use_random_replace ||
10921                                     RND(100) < change->random_percentage))
10922         {
10923           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10924             RemoveMovingField(ex, ey);
10925
10926           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10927
10928           content_element = change->target_content.e[xx][yy];
10929           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10930                                               ce_value, ce_score);
10931
10932           CreateElementFromChange(ex, ey, target_element);
10933
10934           something_has_changed = TRUE;
10935
10936           // for symmetry reasons, freeze newly created border elements
10937           if (ex != x || ey != y)
10938             Stop[ex][ey] = TRUE;        // no more moving in this frame
10939         }
10940       }
10941
10942       if (something_has_changed)
10943       {
10944         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10945         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10946       }
10947     }
10948   }
10949   else
10950   {
10951     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10952                                         ce_value, ce_score);
10953
10954     if (element == EL_DIAGONAL_GROWING ||
10955         element == EL_DIAGONAL_SHRINKING)
10956     {
10957       target_element = Store[x][y];
10958
10959       Store[x][y] = EL_EMPTY;
10960     }
10961
10962     // special case: element changes to player (and may be kept if walkable)
10963     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10964       CreateElementFromChange(x, y, EL_EMPTY);
10965
10966     CreateElementFromChange(x, y, target_element);
10967
10968     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10969     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10970   }
10971
10972   // this uses direct change before indirect change
10973   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10974
10975   return TRUE;
10976 }
10977
10978 static void HandleElementChange(int x, int y, int page)
10979 {
10980   int element = MovingOrBlocked2Element(x, y);
10981   struct ElementInfo *ei = &element_info[element];
10982   struct ElementChangeInfo *change = &ei->change_page[page];
10983   boolean handle_action_before_change = FALSE;
10984
10985 #ifdef DEBUG
10986   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10987       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10988   {
10989     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10990           x, y, element, element_info[element].token_name);
10991     Debug("game:playing:HandleElementChange", "This should never happen!");
10992   }
10993 #endif
10994
10995   // this can happen with classic bombs on walkable, changing elements
10996   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10997   {
10998     return;
10999   }
11000
11001   if (ChangeDelay[x][y] == 0)           // initialize element change
11002   {
11003     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11004
11005     if (change->can_change)
11006     {
11007       // !!! not clear why graphic animation should be reset at all here !!!
11008       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11009       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11010
11011       /*
11012         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11013
11014         When using an animation frame delay of 1 (this only happens with
11015         "sp_zonk.moving.left/right" in the classic graphics), the default
11016         (non-moving) animation shows wrong animation frames (while the
11017         moving animation, like "sp_zonk.moving.left/right", is correct,
11018         so this graphical bug never shows up with the classic graphics).
11019         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11020         be drawn instead of the correct frames 0,1,2,3. This is caused by
11021         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11022         an element change: First when the change delay ("ChangeDelay[][]")
11023         counter has reached zero after decrementing, then a second time in
11024         the next frame (after "GfxFrame[][]" was already incremented) when
11025         "ChangeDelay[][]" is reset to the initial delay value again.
11026
11027         This causes frame 0 to be drawn twice, while the last frame won't
11028         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11029
11030         As some animations may already be cleverly designed around this bug
11031         (at least the "Snake Bite" snake tail animation does this), it cannot
11032         simply be fixed here without breaking such existing animations.
11033         Unfortunately, it cannot easily be detected if a graphics set was
11034         designed "before" or "after" the bug was fixed. As a workaround,
11035         a new graphics set option "game.graphics_engine_version" was added
11036         to be able to specify the game's major release version for which the
11037         graphics set was designed, which can then be used to decide if the
11038         bugfix should be used (version 4 and above) or not (version 3 or
11039         below, or if no version was specified at all, as with old sets).
11040
11041         (The wrong/fixed animation frames can be tested with the test level set
11042         "test_gfxframe" and level "000", which contains a specially prepared
11043         custom element at level position (x/y) == (11/9) which uses the zonk
11044         animation mentioned above. Using "game.graphics_engine_version: 4"
11045         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11046         This can also be seen from the debug output for this test element.)
11047       */
11048
11049       // when a custom element is about to change (for example by change delay),
11050       // do not reset graphic animation when the custom element is moving
11051       if (game.graphics_engine_version < 4 &&
11052           !IS_MOVING(x, y))
11053       {
11054         ResetGfxAnimation(x, y);
11055         ResetRandomAnimationValue(x, y);
11056       }
11057
11058       if (change->pre_change_function)
11059         change->pre_change_function(x, y);
11060     }
11061   }
11062
11063   ChangeDelay[x][y]--;
11064
11065   if (ChangeDelay[x][y] != 0)           // continue element change
11066   {
11067     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11068
11069     // also needed if CE can not change, but has CE delay with CE action
11070     if (IS_ANIMATED(graphic))
11071       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11072
11073     if (change->can_change)
11074     {
11075       if (change->change_function)
11076         change->change_function(x, y);
11077     }
11078   }
11079   else                                  // finish element change
11080   {
11081     if (ChangePage[x][y] != -1)         // remember page from delayed change
11082     {
11083       page = ChangePage[x][y];
11084       ChangePage[x][y] = -1;
11085
11086       change = &ei->change_page[page];
11087     }
11088
11089     if (IS_MOVING(x, y))                // never change a running system ;-)
11090     {
11091       ChangeDelay[x][y] = 1;            // try change after next move step
11092       ChangePage[x][y] = page;          // remember page to use for change
11093
11094       return;
11095     }
11096
11097     // special case: set new level random seed before changing element
11098     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11099       handle_action_before_change = TRUE;
11100
11101     if (change->has_action && handle_action_before_change)
11102       ExecuteCustomElementAction(x, y, element, page);
11103
11104     if (change->can_change)
11105     {
11106       if (ChangeElement(x, y, element, page))
11107       {
11108         if (change->post_change_function)
11109           change->post_change_function(x, y);
11110       }
11111     }
11112
11113     if (change->has_action && !handle_action_before_change)
11114       ExecuteCustomElementAction(x, y, element, page);
11115   }
11116 }
11117
11118 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11119                                               int trigger_element,
11120                                               int trigger_event,
11121                                               int trigger_player,
11122                                               int trigger_side,
11123                                               int trigger_page)
11124 {
11125   boolean change_done_any = FALSE;
11126   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11127   int i;
11128
11129   if (!(trigger_events[trigger_element][trigger_event]))
11130     return FALSE;
11131
11132   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11133
11134   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11135   {
11136     int element = EL_CUSTOM_START + i;
11137     boolean change_done = FALSE;
11138     int p;
11139
11140     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11141         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11142       continue;
11143
11144     for (p = 0; p < element_info[element].num_change_pages; p++)
11145     {
11146       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11147
11148       if (change->can_change_or_has_action &&
11149           change->has_event[trigger_event] &&
11150           change->trigger_side & trigger_side &&
11151           change->trigger_player & trigger_player &&
11152           change->trigger_page & trigger_page_bits &&
11153           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11154       {
11155         change->actual_trigger_element = trigger_element;
11156         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11157         change->actual_trigger_player_bits = trigger_player;
11158         change->actual_trigger_side = trigger_side;
11159         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11160         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11161         change->actual_trigger_x = trigger_x;
11162         change->actual_trigger_y = trigger_y;
11163
11164         if ((change->can_change && !change_done) || change->has_action)
11165         {
11166           int x, y;
11167
11168           SCAN_PLAYFIELD(x, y)
11169           {
11170             if (Tile[x][y] == element)
11171             {
11172               if (change->can_change && !change_done)
11173               {
11174                 // if element already changed in this frame, not only prevent
11175                 // another element change (checked in ChangeElement()), but
11176                 // also prevent additional element actions for this element
11177
11178                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11179                     !level.use_action_after_change_bug)
11180                   continue;
11181
11182                 ChangeDelay[x][y] = 1;
11183                 ChangeEvent[x][y] = trigger_event;
11184
11185                 HandleElementChange(x, y, p);
11186               }
11187               else if (change->has_action)
11188               {
11189                 // if element already changed in this frame, not only prevent
11190                 // another element change (checked in ChangeElement()), but
11191                 // also prevent additional element actions for this element
11192
11193                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11194                     !level.use_action_after_change_bug)
11195                   continue;
11196
11197                 ExecuteCustomElementAction(x, y, element, p);
11198                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11199               }
11200             }
11201           }
11202
11203           if (change->can_change)
11204           {
11205             change_done = TRUE;
11206             change_done_any = TRUE;
11207           }
11208         }
11209       }
11210     }
11211   }
11212
11213   RECURSION_LOOP_DETECTION_END();
11214
11215   return change_done_any;
11216 }
11217
11218 static boolean CheckElementChangeExt(int x, int y,
11219                                      int element,
11220                                      int trigger_element,
11221                                      int trigger_event,
11222                                      int trigger_player,
11223                                      int trigger_side)
11224 {
11225   boolean change_done = FALSE;
11226   int p;
11227
11228   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11229       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11230     return FALSE;
11231
11232   if (Tile[x][y] == EL_BLOCKED)
11233   {
11234     Blocked2Moving(x, y, &x, &y);
11235     element = Tile[x][y];
11236   }
11237
11238   // check if element has already changed or is about to change after moving
11239   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11240        Tile[x][y] != element) ||
11241
11242       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11243        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11244         ChangePage[x][y] != -1)))
11245     return FALSE;
11246
11247   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11248
11249   for (p = 0; p < element_info[element].num_change_pages; p++)
11250   {
11251     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11252
11253     /* check trigger element for all events where the element that is checked
11254        for changing interacts with a directly adjacent element -- this is
11255        different to element changes that affect other elements to change on the
11256        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11257     boolean check_trigger_element =
11258       (trigger_event == CE_NEXT_TO_X ||
11259        trigger_event == CE_TOUCHING_X ||
11260        trigger_event == CE_HITTING_X ||
11261        trigger_event == CE_HIT_BY_X ||
11262        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11263
11264     if (change->can_change_or_has_action &&
11265         change->has_event[trigger_event] &&
11266         change->trigger_side & trigger_side &&
11267         change->trigger_player & trigger_player &&
11268         (!check_trigger_element ||
11269          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11270     {
11271       change->actual_trigger_element = trigger_element;
11272       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11273       change->actual_trigger_player_bits = trigger_player;
11274       change->actual_trigger_side = trigger_side;
11275       change->actual_trigger_ce_value = CustomValue[x][y];
11276       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11277       change->actual_trigger_x = x;
11278       change->actual_trigger_y = y;
11279
11280       // special case: trigger element not at (x,y) position for some events
11281       if (check_trigger_element)
11282       {
11283         static struct
11284         {
11285           int dx, dy;
11286         } move_xy[] =
11287           {
11288             {  0,  0 },
11289             { -1,  0 },
11290             { +1,  0 },
11291             {  0,  0 },
11292             {  0, -1 },
11293             {  0,  0 }, { 0, 0 }, { 0, 0 },
11294             {  0, +1 }
11295           };
11296
11297         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11298         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11299
11300         change->actual_trigger_ce_value = CustomValue[xx][yy];
11301         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11302         change->actual_trigger_x = xx;
11303         change->actual_trigger_y = yy;
11304       }
11305
11306       if (change->can_change && !change_done)
11307       {
11308         ChangeDelay[x][y] = 1;
11309         ChangeEvent[x][y] = trigger_event;
11310
11311         HandleElementChange(x, y, p);
11312
11313         change_done = TRUE;
11314       }
11315       else if (change->has_action)
11316       {
11317         ExecuteCustomElementAction(x, y, element, p);
11318         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11319       }
11320     }
11321   }
11322
11323   RECURSION_LOOP_DETECTION_END();
11324
11325   return change_done;
11326 }
11327
11328 static void PlayPlayerSound(struct PlayerInfo *player)
11329 {
11330   int jx = player->jx, jy = player->jy;
11331   int sound_element = player->artwork_element;
11332   int last_action = player->last_action_waiting;
11333   int action = player->action_waiting;
11334
11335   if (player->is_waiting)
11336   {
11337     if (action != last_action)
11338       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11339     else
11340       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11341   }
11342   else
11343   {
11344     if (action != last_action)
11345       StopSound(element_info[sound_element].sound[last_action]);
11346
11347     if (last_action == ACTION_SLEEPING)
11348       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11349   }
11350 }
11351
11352 static void PlayAllPlayersSound(void)
11353 {
11354   int i;
11355
11356   for (i = 0; i < MAX_PLAYERS; i++)
11357     if (stored_player[i].active)
11358       PlayPlayerSound(&stored_player[i]);
11359 }
11360
11361 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11362 {
11363   boolean last_waiting = player->is_waiting;
11364   int move_dir = player->MovDir;
11365
11366   player->dir_waiting = move_dir;
11367   player->last_action_waiting = player->action_waiting;
11368
11369   if (is_waiting)
11370   {
11371     if (!last_waiting)          // not waiting -> waiting
11372     {
11373       player->is_waiting = TRUE;
11374
11375       player->frame_counter_bored =
11376         FrameCounter +
11377         game.player_boring_delay_fixed +
11378         GetSimpleRandom(game.player_boring_delay_random);
11379       player->frame_counter_sleeping =
11380         FrameCounter +
11381         game.player_sleeping_delay_fixed +
11382         GetSimpleRandom(game.player_sleeping_delay_random);
11383
11384       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11385     }
11386
11387     if (game.player_sleeping_delay_fixed +
11388         game.player_sleeping_delay_random > 0 &&
11389         player->anim_delay_counter == 0 &&
11390         player->post_delay_counter == 0 &&
11391         FrameCounter >= player->frame_counter_sleeping)
11392       player->is_sleeping = TRUE;
11393     else if (game.player_boring_delay_fixed +
11394              game.player_boring_delay_random > 0 &&
11395              FrameCounter >= player->frame_counter_bored)
11396       player->is_bored = TRUE;
11397
11398     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11399                               player->is_bored ? ACTION_BORING :
11400                               ACTION_WAITING);
11401
11402     if (player->is_sleeping && player->use_murphy)
11403     {
11404       // special case for sleeping Murphy when leaning against non-free tile
11405
11406       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11407           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11408            !IS_MOVING(player->jx - 1, player->jy)))
11409         move_dir = MV_LEFT;
11410       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11411                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11412                 !IS_MOVING(player->jx + 1, player->jy)))
11413         move_dir = MV_RIGHT;
11414       else
11415         player->is_sleeping = FALSE;
11416
11417       player->dir_waiting = move_dir;
11418     }
11419
11420     if (player->is_sleeping)
11421     {
11422       if (player->num_special_action_sleeping > 0)
11423       {
11424         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11425         {
11426           int last_special_action = player->special_action_sleeping;
11427           int num_special_action = player->num_special_action_sleeping;
11428           int special_action =
11429             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11430              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11431              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11432              last_special_action + 1 : ACTION_SLEEPING);
11433           int special_graphic =
11434             el_act_dir2img(player->artwork_element, special_action, move_dir);
11435
11436           player->anim_delay_counter =
11437             graphic_info[special_graphic].anim_delay_fixed +
11438             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11439           player->post_delay_counter =
11440             graphic_info[special_graphic].post_delay_fixed +
11441             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11442
11443           player->special_action_sleeping = special_action;
11444         }
11445
11446         if (player->anim_delay_counter > 0)
11447         {
11448           player->action_waiting = player->special_action_sleeping;
11449           player->anim_delay_counter--;
11450         }
11451         else if (player->post_delay_counter > 0)
11452         {
11453           player->post_delay_counter--;
11454         }
11455       }
11456     }
11457     else if (player->is_bored)
11458     {
11459       if (player->num_special_action_bored > 0)
11460       {
11461         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11462         {
11463           int special_action =
11464             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11465           int special_graphic =
11466             el_act_dir2img(player->artwork_element, special_action, move_dir);
11467
11468           player->anim_delay_counter =
11469             graphic_info[special_graphic].anim_delay_fixed +
11470             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11471           player->post_delay_counter =
11472             graphic_info[special_graphic].post_delay_fixed +
11473             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11474
11475           player->special_action_bored = special_action;
11476         }
11477
11478         if (player->anim_delay_counter > 0)
11479         {
11480           player->action_waiting = player->special_action_bored;
11481           player->anim_delay_counter--;
11482         }
11483         else if (player->post_delay_counter > 0)
11484         {
11485           player->post_delay_counter--;
11486         }
11487       }
11488     }
11489   }
11490   else if (last_waiting)        // waiting -> not waiting
11491   {
11492     player->is_waiting = FALSE;
11493     player->is_bored = FALSE;
11494     player->is_sleeping = FALSE;
11495
11496     player->frame_counter_bored = -1;
11497     player->frame_counter_sleeping = -1;
11498
11499     player->anim_delay_counter = 0;
11500     player->post_delay_counter = 0;
11501
11502     player->dir_waiting = player->MovDir;
11503     player->action_waiting = ACTION_DEFAULT;
11504
11505     player->special_action_bored = ACTION_DEFAULT;
11506     player->special_action_sleeping = ACTION_DEFAULT;
11507   }
11508 }
11509
11510 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11511 {
11512   if ((!player->is_moving  && player->was_moving) ||
11513       (player->MovPos == 0 && player->was_moving) ||
11514       (player->is_snapping && !player->was_snapping) ||
11515       (player->is_dropping && !player->was_dropping))
11516   {
11517     if (!CheckSaveEngineSnapshotToList())
11518       return;
11519
11520     player->was_moving = FALSE;
11521     player->was_snapping = TRUE;
11522     player->was_dropping = TRUE;
11523   }
11524   else
11525   {
11526     if (player->is_moving)
11527       player->was_moving = TRUE;
11528
11529     if (!player->is_snapping)
11530       player->was_snapping = FALSE;
11531
11532     if (!player->is_dropping)
11533       player->was_dropping = FALSE;
11534   }
11535
11536   static struct MouseActionInfo mouse_action_last = { 0 };
11537   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11538   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11539
11540   if (new_released)
11541     CheckSaveEngineSnapshotToList();
11542
11543   mouse_action_last = mouse_action;
11544 }
11545
11546 static void CheckSingleStepMode(struct PlayerInfo *player)
11547 {
11548   if (tape.single_step && tape.recording && !tape.pausing)
11549   {
11550     // as it is called "single step mode", just return to pause mode when the
11551     // player stopped moving after one tile (or never starts moving at all)
11552     // (reverse logic needed here in case single step mode used in team mode)
11553     if (player->is_moving ||
11554         player->is_pushing ||
11555         player->is_dropping_pressed ||
11556         player->effective_mouse_action.button)
11557       game.enter_single_step_mode = FALSE;
11558   }
11559
11560   CheckSaveEngineSnapshot(player);
11561 }
11562
11563 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11564 {
11565   int left      = player_action & JOY_LEFT;
11566   int right     = player_action & JOY_RIGHT;
11567   int up        = player_action & JOY_UP;
11568   int down      = player_action & JOY_DOWN;
11569   int button1   = player_action & JOY_BUTTON_1;
11570   int button2   = player_action & JOY_BUTTON_2;
11571   int dx        = (left ? -1 : right ? 1 : 0);
11572   int dy        = (up   ? -1 : down  ? 1 : 0);
11573
11574   if (!player->active || tape.pausing)
11575     return 0;
11576
11577   if (player_action)
11578   {
11579     if (button1)
11580       SnapField(player, dx, dy);
11581     else
11582     {
11583       if (button2)
11584         DropElement(player);
11585
11586       MovePlayer(player, dx, dy);
11587     }
11588
11589     CheckSingleStepMode(player);
11590
11591     SetPlayerWaiting(player, FALSE);
11592
11593     return player_action;
11594   }
11595   else
11596   {
11597     // no actions for this player (no input at player's configured device)
11598
11599     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11600     SnapField(player, 0, 0);
11601     CheckGravityMovementWhenNotMoving(player);
11602
11603     if (player->MovPos == 0)
11604       SetPlayerWaiting(player, TRUE);
11605
11606     if (player->MovPos == 0)    // needed for tape.playing
11607       player->is_moving = FALSE;
11608
11609     player->is_dropping = FALSE;
11610     player->is_dropping_pressed = FALSE;
11611     player->drop_pressed_delay = 0;
11612
11613     CheckSingleStepMode(player);
11614
11615     return 0;
11616   }
11617 }
11618
11619 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11620                                          byte *tape_action)
11621 {
11622   if (!tape.use_mouse_actions)
11623     return;
11624
11625   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11626   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11627   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11628 }
11629
11630 static void SetTapeActionFromMouseAction(byte *tape_action,
11631                                          struct MouseActionInfo *mouse_action)
11632 {
11633   if (!tape.use_mouse_actions)
11634     return;
11635
11636   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11637   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11638   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11639 }
11640
11641 static void CheckLevelSolved(void)
11642 {
11643   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11644   {
11645     if (game_bd.level_solved &&
11646         !game_bd.game_over)                             // game won
11647     {
11648       LevelSolved();
11649
11650       game_bd.game_over = TRUE;
11651
11652       game.all_players_gone = TRUE;
11653     }
11654
11655     if (game_bd.game_over)                              // game lost
11656       game.all_players_gone = TRUE;
11657   }
11658   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11659   {
11660     if (game_em.level_solved &&
11661         !game_em.game_over)                             // game won
11662     {
11663       LevelSolved();
11664
11665       game_em.game_over = TRUE;
11666
11667       game.all_players_gone = TRUE;
11668     }
11669
11670     if (game_em.game_over)                              // game lost
11671       game.all_players_gone = TRUE;
11672   }
11673   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11674   {
11675     if (game_sp.level_solved &&
11676         !game_sp.game_over)                             // game won
11677     {
11678       LevelSolved();
11679
11680       game_sp.game_over = TRUE;
11681
11682       game.all_players_gone = TRUE;
11683     }
11684
11685     if (game_sp.game_over)                              // game lost
11686       game.all_players_gone = TRUE;
11687   }
11688   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11689   {
11690     if (game_mm.level_solved &&
11691         !game_mm.game_over)                             // game won
11692     {
11693       LevelSolved();
11694
11695       game_mm.game_over = TRUE;
11696
11697       game.all_players_gone = TRUE;
11698     }
11699
11700     if (game_mm.game_over)                              // game lost
11701       game.all_players_gone = TRUE;
11702   }
11703 }
11704
11705 static void PlayTimeoutSound(int seconds_left)
11706 {
11707   // try to use individual "running out of time" sound for each second left
11708   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11709
11710   // if special sound per second not defined, use default sound
11711   if (getSoundInfoEntryFilename(sound) == NULL)
11712     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11713
11714   // if out of time, but player still alive, play special "timeout" sound, if defined
11715   if (seconds_left == 0 && !checkGameFailed())
11716     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11717       sound = SND_GAME_TIMEOUT;
11718
11719   PlaySound(sound);
11720 }
11721
11722 static void CheckLevelTime_StepCounter(void)
11723 {
11724   int i;
11725
11726   TimePlayed++;
11727
11728   if (TimeLeft > 0)
11729   {
11730     TimeLeft--;
11731
11732     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11733       PlayTimeoutSound(TimeLeft);
11734
11735     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11736
11737     DisplayGameControlValues();
11738
11739     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11740       for (i = 0; i < MAX_PLAYERS; i++)
11741         KillPlayer(&stored_player[i]);
11742   }
11743   else if (game.no_level_time_limit && !game.all_players_gone)
11744   {
11745     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11746
11747     DisplayGameControlValues();
11748   }
11749 }
11750
11751 static void CheckLevelTime(void)
11752 {
11753   int i;
11754
11755   if (TimeFrames >= FRAMES_PER_SECOND)
11756   {
11757     TimeFrames = 0;
11758
11759     for (i = 0; i < MAX_PLAYERS; i++)
11760     {
11761       struct PlayerInfo *player = &stored_player[i];
11762
11763       if (SHIELD_ON(player))
11764       {
11765         player->shield_normal_time_left--;
11766
11767         if (player->shield_deadly_time_left > 0)
11768           player->shield_deadly_time_left--;
11769       }
11770     }
11771
11772     if (!game.LevelSolved && !level.use_step_counter)
11773     {
11774       TimePlayed++;
11775
11776       if (TimeLeft > 0)
11777       {
11778         TimeLeft--;
11779
11780         if (TimeLeft <= 10 && game.time_limit)
11781           PlayTimeoutSound(TimeLeft);
11782
11783         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11784            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11785
11786         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11787
11788         if (!TimeLeft && game.time_limit)
11789         {
11790           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11791             game_em.lev->killed_out_of_time = TRUE;
11792           else
11793             for (i = 0; i < MAX_PLAYERS; i++)
11794               KillPlayer(&stored_player[i]);
11795         }
11796       }
11797       else if (game.no_level_time_limit && !game.all_players_gone)
11798       {
11799         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11800       }
11801
11802       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11803     }
11804   }
11805
11806   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11807   {
11808     TapeTimeFrames = 0;
11809     TapeTime++;
11810
11811     if (tape.recording || tape.playing)
11812       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11813   }
11814
11815   if (tape.recording || tape.playing)
11816     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11817
11818   UpdateAndDisplayGameControlValues();
11819 }
11820
11821 void AdvanceFrameAndPlayerCounters(int player_nr)
11822 {
11823   int i;
11824
11825   // advance frame counters (global frame counter and tape time frame counter)
11826   FrameCounter++;
11827   TapeTimeFrames++;
11828
11829   // advance time frame counter (used to control available time to solve level)
11830   TimeFrames++;
11831
11832   // advance player counters (counters for move delay, move animation etc.)
11833   for (i = 0; i < MAX_PLAYERS; i++)
11834   {
11835     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11836     int move_delay_value = stored_player[i].move_delay_value;
11837     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11838
11839     if (!advance_player_counters)       // not all players may be affected
11840       continue;
11841
11842     if (move_frames == 0)       // less than one move per game frame
11843     {
11844       int stepsize = TILEX / move_delay_value;
11845       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11846       int count = (stored_player[i].is_moving ?
11847                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11848
11849       if (count % delay == 0)
11850         move_frames = 1;
11851     }
11852
11853     stored_player[i].Frame += move_frames;
11854
11855     if (stored_player[i].MovPos != 0)
11856       stored_player[i].StepFrame += move_frames;
11857
11858     if (stored_player[i].move_delay > 0)
11859       stored_player[i].move_delay--;
11860
11861     // due to bugs in previous versions, counter must count up, not down
11862     if (stored_player[i].push_delay != -1)
11863       stored_player[i].push_delay++;
11864
11865     if (stored_player[i].drop_delay > 0)
11866       stored_player[i].drop_delay--;
11867
11868     if (stored_player[i].is_dropping_pressed)
11869       stored_player[i].drop_pressed_delay++;
11870   }
11871 }
11872
11873 void AdvanceFrameCounter(void)
11874 {
11875   FrameCounter++;
11876 }
11877
11878 void AdvanceGfxFrame(void)
11879 {
11880   int x, y;
11881
11882   SCAN_PLAYFIELD(x, y)
11883   {
11884     GfxFrame[x][y]++;
11885   }
11886 }
11887
11888 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11889                               struct MouseActionInfo *mouse_action_last)
11890 {
11891   if (mouse_action->button)
11892   {
11893     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11894     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11895     int x = mouse_action->lx;
11896     int y = mouse_action->ly;
11897     int element = Tile[x][y];
11898
11899     if (new_button)
11900     {
11901       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11902       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11903                                          ch_button);
11904     }
11905
11906     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11907     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11908                                        ch_button);
11909
11910     if (level.use_step_counter)
11911     {
11912       boolean counted_click = FALSE;
11913
11914       // element clicked that can change when clicked/pressed
11915       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11916           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11917            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11918         counted_click = TRUE;
11919
11920       // element clicked that can trigger change when clicked/pressed
11921       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11922           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11923         counted_click = TRUE;
11924
11925       if (new_button && counted_click)
11926         CheckLevelTime_StepCounter();
11927     }
11928   }
11929 }
11930
11931 void StartGameActions(boolean init_network_game, boolean record_tape,
11932                       int random_seed)
11933 {
11934   unsigned int new_random_seed = InitRND(random_seed);
11935
11936   if (record_tape)
11937     TapeStartRecording(new_random_seed);
11938
11939   if (setup.auto_pause_on_start && !tape.pausing)
11940     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11941
11942   if (init_network_game)
11943   {
11944     SendToServer_LevelFile();
11945     SendToServer_StartPlaying();
11946
11947     return;
11948   }
11949
11950   InitGame();
11951 }
11952
11953 static void GameActionsExt(void)
11954 {
11955 #if 0
11956   static unsigned int game_frame_delay = 0;
11957 #endif
11958   unsigned int game_frame_delay_value;
11959   byte *recorded_player_action;
11960   byte summarized_player_action = 0;
11961   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11962   int i;
11963
11964   // detect endless loops, caused by custom element programming
11965   if (recursion_loop_detected && recursion_loop_depth == 0)
11966   {
11967     char *message = getStringCat3("Internal Error! Element ",
11968                                   EL_NAME(recursion_loop_element),
11969                                   " caused endless loop! Quit the game?");
11970
11971     Warn("element '%s' caused endless loop in game engine",
11972          EL_NAME(recursion_loop_element));
11973
11974     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11975
11976     recursion_loop_detected = FALSE;    // if game should be continued
11977
11978     free(message);
11979
11980     return;
11981   }
11982
11983   if (game.restart_level)
11984     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11985
11986   CheckLevelSolved();
11987
11988   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11989     GameWon();
11990
11991   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11992     TapeStop();
11993
11994   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11995     return;
11996
11997   game_frame_delay_value =
11998     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11999
12000   if (tape.playing && tape.warp_forward && !tape.pausing)
12001     game_frame_delay_value = 0;
12002
12003   SetVideoFrameDelay(game_frame_delay_value);
12004
12005   // (de)activate virtual buttons depending on current game status
12006   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12007   {
12008     if (game.all_players_gone)  // if no players there to be controlled anymore
12009       SetOverlayActive(FALSE);
12010     else if (!tape.playing)     // if game continues after tape stopped playing
12011       SetOverlayActive(TRUE);
12012   }
12013
12014 #if 0
12015 #if 0
12016   // ---------- main game synchronization point ----------
12017
12018   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12019
12020   Debug("game:playing:skip", "skip == %d", skip);
12021
12022 #else
12023   // ---------- main game synchronization point ----------
12024
12025   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12026 #endif
12027 #endif
12028
12029   if (network_playing && !network_player_action_received)
12030   {
12031     // try to get network player actions in time
12032
12033     // last chance to get network player actions without main loop delay
12034     HandleNetworking();
12035
12036     // game was quit by network peer
12037     if (game_status != GAME_MODE_PLAYING)
12038       return;
12039
12040     // check if network player actions still missing and game still running
12041     if (!network_player_action_received && !checkGameEnded())
12042       return;           // failed to get network player actions in time
12043
12044     // do not yet reset "network_player_action_received" (for tape.pausing)
12045   }
12046
12047   if (tape.pausing)
12048     return;
12049
12050   // at this point we know that we really continue executing the game
12051
12052   network_player_action_received = FALSE;
12053
12054   // when playing tape, read previously recorded player input from tape data
12055   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12056
12057   local_player->effective_mouse_action = local_player->mouse_action;
12058
12059   if (recorded_player_action != NULL)
12060     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12061                                  recorded_player_action);
12062
12063   // TapePlayAction() may return NULL when toggling to "pause before death"
12064   if (tape.pausing)
12065     return;
12066
12067   if (tape.set_centered_player)
12068   {
12069     game.centered_player_nr_next = tape.centered_player_nr_next;
12070     game.set_centered_player = TRUE;
12071   }
12072
12073   for (i = 0; i < MAX_PLAYERS; i++)
12074   {
12075     summarized_player_action |= stored_player[i].action;
12076
12077     if (!network_playing && (game.team_mode || tape.playing))
12078       stored_player[i].effective_action = stored_player[i].action;
12079   }
12080
12081   if (network_playing && !checkGameEnded())
12082     SendToServer_MovePlayer(summarized_player_action);
12083
12084   // summarize all actions at local players mapped input device position
12085   // (this allows using different input devices in single player mode)
12086   if (!network.enabled && !game.team_mode)
12087     stored_player[map_player_action[local_player->index_nr]].effective_action =
12088       summarized_player_action;
12089
12090   // summarize all actions at centered player in local team mode
12091   if (tape.recording &&
12092       setup.team_mode && !network.enabled &&
12093       setup.input_on_focus &&
12094       game.centered_player_nr != -1)
12095   {
12096     for (i = 0; i < MAX_PLAYERS; i++)
12097       stored_player[map_player_action[i]].effective_action =
12098         (i == game.centered_player_nr ? summarized_player_action : 0);
12099   }
12100
12101   if (recorded_player_action != NULL)
12102     for (i = 0; i < MAX_PLAYERS; i++)
12103       stored_player[i].effective_action = recorded_player_action[i];
12104
12105   for (i = 0; i < MAX_PLAYERS; i++)
12106   {
12107     tape_action[i] = stored_player[i].effective_action;
12108
12109     /* (this may happen in the RND game engine if a player was not present on
12110        the playfield on level start, but appeared later from a custom element */
12111     if (setup.team_mode &&
12112         tape.recording &&
12113         tape_action[i] &&
12114         !tape.player_participates[i])
12115       tape.player_participates[i] = TRUE;
12116   }
12117
12118   SetTapeActionFromMouseAction(tape_action,
12119                                &local_player->effective_mouse_action);
12120
12121   // only record actions from input devices, but not programmed actions
12122   if (tape.recording)
12123     TapeRecordAction(tape_action);
12124
12125   // remember if game was played (especially after tape stopped playing)
12126   if (!tape.playing && summarized_player_action && !checkGameFailed())
12127     game.GamePlayed = TRUE;
12128
12129 #if USE_NEW_PLAYER_ASSIGNMENTS
12130   // !!! also map player actions in single player mode !!!
12131   // if (game.team_mode)
12132   if (1)
12133   {
12134     byte mapped_action[MAX_PLAYERS];
12135
12136 #if DEBUG_PLAYER_ACTIONS
12137     for (i = 0; i < MAX_PLAYERS; i++)
12138       DebugContinued("", "%d, ", stored_player[i].effective_action);
12139 #endif
12140
12141     for (i = 0; i < MAX_PLAYERS; i++)
12142       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12143
12144     for (i = 0; i < MAX_PLAYERS; i++)
12145       stored_player[i].effective_action = mapped_action[i];
12146
12147 #if DEBUG_PLAYER_ACTIONS
12148     DebugContinued("", "=> ");
12149     for (i = 0; i < MAX_PLAYERS; i++)
12150       DebugContinued("", "%d, ", stored_player[i].effective_action);
12151     DebugContinued("game:playing:player", "\n");
12152 #endif
12153   }
12154 #if DEBUG_PLAYER_ACTIONS
12155   else
12156   {
12157     for (i = 0; i < MAX_PLAYERS; i++)
12158       DebugContinued("", "%d, ", stored_player[i].effective_action);
12159     DebugContinued("game:playing:player", "\n");
12160   }
12161 #endif
12162 #endif
12163
12164   for (i = 0; i < MAX_PLAYERS; i++)
12165   {
12166     // allow engine snapshot in case of changed movement attempt
12167     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12168         (stored_player[i].effective_action & KEY_MOTION))
12169       game.snapshot.changed_action = TRUE;
12170
12171     // allow engine snapshot in case of snapping/dropping attempt
12172     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12173         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12174       game.snapshot.changed_action = TRUE;
12175
12176     game.snapshot.last_action[i] = stored_player[i].effective_action;
12177   }
12178
12179   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12180   {
12181     GameActions_EM_Main();
12182   }
12183   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12184   {
12185     GameActions_SP_Main();
12186   }
12187   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12188   {
12189     GameActions_MM_Main();
12190   }
12191   else
12192   {
12193     GameActions_RND_Main();
12194   }
12195
12196   BlitScreenToBitmap(backbuffer);
12197
12198   CheckLevelSolved();
12199   CheckLevelTime();
12200
12201   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12202
12203   if (global.show_frames_per_second)
12204   {
12205     static unsigned int fps_counter = 0;
12206     static int fps_frames = 0;
12207     unsigned int fps_delay_ms = Counter() - fps_counter;
12208
12209     fps_frames++;
12210
12211     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12212     {
12213       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12214
12215       fps_frames = 0;
12216       fps_counter = Counter();
12217
12218       // always draw FPS to screen after FPS value was updated
12219       redraw_mask |= REDRAW_FPS;
12220     }
12221
12222     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12223     if (GetDrawDeactivationMask() == REDRAW_NONE)
12224       redraw_mask |= REDRAW_FPS;
12225   }
12226 }
12227
12228 static void GameActions_CheckSaveEngineSnapshot(void)
12229 {
12230   if (!game.snapshot.save_snapshot)
12231     return;
12232
12233   // clear flag for saving snapshot _before_ saving snapshot
12234   game.snapshot.save_snapshot = FALSE;
12235
12236   SaveEngineSnapshotToList();
12237 }
12238
12239 void GameActions(void)
12240 {
12241   GameActionsExt();
12242
12243   GameActions_CheckSaveEngineSnapshot();
12244 }
12245
12246 void GameActions_EM_Main(void)
12247 {
12248   byte effective_action[MAX_PLAYERS];
12249   int i;
12250
12251   for (i = 0; i < MAX_PLAYERS; i++)
12252     effective_action[i] = stored_player[i].effective_action;
12253
12254   GameActions_EM(effective_action);
12255 }
12256
12257 void GameActions_SP_Main(void)
12258 {
12259   byte effective_action[MAX_PLAYERS];
12260   int i;
12261
12262   for (i = 0; i < MAX_PLAYERS; i++)
12263     effective_action[i] = stored_player[i].effective_action;
12264
12265   GameActions_SP(effective_action);
12266
12267   for (i = 0; i < MAX_PLAYERS; i++)
12268   {
12269     if (stored_player[i].force_dropping)
12270       stored_player[i].action |= KEY_BUTTON_DROP;
12271
12272     stored_player[i].force_dropping = FALSE;
12273   }
12274 }
12275
12276 void GameActions_MM_Main(void)
12277 {
12278   AdvanceGfxFrame();
12279
12280   GameActions_MM(local_player->effective_mouse_action);
12281 }
12282
12283 void GameActions_RND_Main(void)
12284 {
12285   GameActions_RND();
12286 }
12287
12288 void GameActions_RND(void)
12289 {
12290   static struct MouseActionInfo mouse_action_last = { 0 };
12291   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12292   int magic_wall_x = 0, magic_wall_y = 0;
12293   int i, x, y, element, graphic, last_gfx_frame;
12294
12295   InitPlayfieldScanModeVars();
12296
12297   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12298   {
12299     SCAN_PLAYFIELD(x, y)
12300     {
12301       ChangeCount[x][y] = 0;
12302       ChangeEvent[x][y] = -1;
12303     }
12304   }
12305
12306   if (game.set_centered_player)
12307   {
12308     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12309
12310     // switching to "all players" only possible if all players fit to screen
12311     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12312     {
12313       game.centered_player_nr_next = game.centered_player_nr;
12314       game.set_centered_player = FALSE;
12315     }
12316
12317     // do not switch focus to non-existing (or non-active) player
12318     if (game.centered_player_nr_next >= 0 &&
12319         !stored_player[game.centered_player_nr_next].active)
12320     {
12321       game.centered_player_nr_next = game.centered_player_nr;
12322       game.set_centered_player = FALSE;
12323     }
12324   }
12325
12326   if (game.set_centered_player &&
12327       ScreenMovPos == 0)        // screen currently aligned at tile position
12328   {
12329     int sx, sy;
12330
12331     if (game.centered_player_nr_next == -1)
12332     {
12333       setScreenCenteredToAllPlayers(&sx, &sy);
12334     }
12335     else
12336     {
12337       sx = stored_player[game.centered_player_nr_next].jx;
12338       sy = stored_player[game.centered_player_nr_next].jy;
12339     }
12340
12341     game.centered_player_nr = game.centered_player_nr_next;
12342     game.set_centered_player = FALSE;
12343
12344     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12345     DrawGameDoorValues();
12346   }
12347
12348   // check single step mode (set flag and clear again if any player is active)
12349   game.enter_single_step_mode =
12350     (tape.single_step && tape.recording && !tape.pausing);
12351
12352   for (i = 0; i < MAX_PLAYERS; i++)
12353   {
12354     int actual_player_action = stored_player[i].effective_action;
12355
12356 #if 1
12357     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12358        - rnd_equinox_tetrachloride 048
12359        - rnd_equinox_tetrachloride_ii 096
12360        - rnd_emanuel_schmieg 002
12361        - doctor_sloan_ww 001, 020
12362     */
12363     if (stored_player[i].MovPos == 0)
12364       CheckGravityMovement(&stored_player[i]);
12365 #endif
12366
12367     // overwrite programmed action with tape action
12368     if (stored_player[i].programmed_action)
12369       actual_player_action = stored_player[i].programmed_action;
12370
12371     PlayerActions(&stored_player[i], actual_player_action);
12372
12373     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12374   }
12375
12376   // single step pause mode may already have been toggled by "ScrollPlayer()"
12377   if (game.enter_single_step_mode && !tape.pausing)
12378     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12379
12380   ScrollScreen(NULL, SCROLL_GO_ON);
12381
12382   /* for backwards compatibility, the following code emulates a fixed bug that
12383      occured when pushing elements (causing elements that just made their last
12384      pushing step to already (if possible) make their first falling step in the
12385      same game frame, which is bad); this code is also needed to use the famous
12386      "spring push bug" which is used in older levels and might be wanted to be
12387      used also in newer levels, but in this case the buggy pushing code is only
12388      affecting the "spring" element and no other elements */
12389
12390   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12391   {
12392     for (i = 0; i < MAX_PLAYERS; i++)
12393     {
12394       struct PlayerInfo *player = &stored_player[i];
12395       int x = player->jx;
12396       int y = player->jy;
12397
12398       if (player->active && player->is_pushing && player->is_moving &&
12399           IS_MOVING(x, y) &&
12400           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12401            Tile[x][y] == EL_SPRING))
12402       {
12403         ContinueMoving(x, y);
12404
12405         // continue moving after pushing (this is actually a bug)
12406         if (!IS_MOVING(x, y))
12407           Stop[x][y] = FALSE;
12408       }
12409     }
12410   }
12411
12412   SCAN_PLAYFIELD(x, y)
12413   {
12414     Last[x][y] = Tile[x][y];
12415
12416     ChangeCount[x][y] = 0;
12417     ChangeEvent[x][y] = -1;
12418
12419     // this must be handled before main playfield loop
12420     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12421     {
12422       MovDelay[x][y]--;
12423       if (MovDelay[x][y] <= 0)
12424         RemoveField(x, y);
12425     }
12426
12427     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12428     {
12429       MovDelay[x][y]--;
12430       if (MovDelay[x][y] <= 0)
12431       {
12432         int element = Store[x][y];
12433         int move_direction = MovDir[x][y];
12434         int player_index_bit = Store2[x][y];
12435
12436         Store[x][y] = 0;
12437         Store2[x][y] = 0;
12438
12439         RemoveField(x, y);
12440         TEST_DrawLevelField(x, y);
12441
12442         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12443
12444         if (IS_ENVELOPE(element))
12445           local_player->show_envelope = element;
12446       }
12447     }
12448
12449 #if DEBUG
12450     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12451     {
12452       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12453             x, y);
12454       Debug("game:playing:GameActions_RND", "This should never happen!");
12455
12456       ChangePage[x][y] = -1;
12457     }
12458 #endif
12459
12460     Stop[x][y] = FALSE;
12461     if (WasJustMoving[x][y] > 0)
12462       WasJustMoving[x][y]--;
12463     if (WasJustFalling[x][y] > 0)
12464       WasJustFalling[x][y]--;
12465     if (CheckCollision[x][y] > 0)
12466       CheckCollision[x][y]--;
12467     if (CheckImpact[x][y] > 0)
12468       CheckImpact[x][y]--;
12469
12470     GfxFrame[x][y]++;
12471
12472     /* reset finished pushing action (not done in ContinueMoving() to allow
12473        continuous pushing animation for elements with zero push delay) */
12474     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12475     {
12476       ResetGfxAnimation(x, y);
12477       TEST_DrawLevelField(x, y);
12478     }
12479
12480 #if DEBUG
12481     if (IS_BLOCKED(x, y))
12482     {
12483       int oldx, oldy;
12484
12485       Blocked2Moving(x, y, &oldx, &oldy);
12486       if (!IS_MOVING(oldx, oldy))
12487       {
12488         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12489         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12490         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12491         Debug("game:playing:GameActions_RND", "This should never happen!");
12492       }
12493     }
12494 #endif
12495   }
12496
12497   HandleMouseAction(&mouse_action, &mouse_action_last);
12498
12499   SCAN_PLAYFIELD(x, y)
12500   {
12501     element = Tile[x][y];
12502     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12503     last_gfx_frame = GfxFrame[x][y];
12504
12505     if (element == EL_EMPTY)
12506       graphic = el2img(GfxElementEmpty[x][y]);
12507
12508     ResetGfxFrame(x, y);
12509
12510     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12511       DrawLevelGraphicAnimation(x, y, graphic);
12512
12513     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12514         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12515       ResetRandomAnimationValue(x, y);
12516
12517     SetRandomAnimationValue(x, y);
12518
12519     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12520
12521     if (IS_INACTIVE(element))
12522     {
12523       if (IS_ANIMATED(graphic))
12524         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12525
12526       continue;
12527     }
12528
12529     // this may take place after moving, so 'element' may have changed
12530     if (IS_CHANGING(x, y) &&
12531         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12532     {
12533       int page = element_info[element].event_page_nr[CE_DELAY];
12534
12535       HandleElementChange(x, y, page);
12536
12537       element = Tile[x][y];
12538       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12539     }
12540
12541     CheckNextToConditions(x, y);
12542
12543     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12544     {
12545       StartMoving(x, y);
12546
12547       element = Tile[x][y];
12548       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12549
12550       if (IS_ANIMATED(graphic) &&
12551           !IS_MOVING(x, y) &&
12552           !Stop[x][y])
12553         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12554
12555       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12556         TEST_DrawTwinkleOnField(x, y);
12557     }
12558     else if (element == EL_ACID)
12559     {
12560       if (!Stop[x][y])
12561         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12562     }
12563     else if ((element == EL_EXIT_OPEN ||
12564               element == EL_EM_EXIT_OPEN ||
12565               element == EL_SP_EXIT_OPEN ||
12566               element == EL_STEEL_EXIT_OPEN ||
12567               element == EL_EM_STEEL_EXIT_OPEN ||
12568               element == EL_SP_TERMINAL ||
12569               element == EL_SP_TERMINAL_ACTIVE ||
12570               element == EL_EXTRA_TIME ||
12571               element == EL_SHIELD_NORMAL ||
12572               element == EL_SHIELD_DEADLY) &&
12573              IS_ANIMATED(graphic))
12574       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12575     else if (IS_MOVING(x, y))
12576       ContinueMoving(x, y);
12577     else if (IS_ACTIVE_BOMB(element))
12578       CheckDynamite(x, y);
12579     else if (element == EL_AMOEBA_GROWING)
12580       AmoebaGrowing(x, y);
12581     else if (element == EL_AMOEBA_SHRINKING)
12582       AmoebaShrinking(x, y);
12583
12584 #if !USE_NEW_AMOEBA_CODE
12585     else if (IS_AMOEBALIVE(element))
12586       AmoebaReproduce(x, y);
12587 #endif
12588
12589     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12590       Life(x, y);
12591     else if (element == EL_EXIT_CLOSED)
12592       CheckExit(x, y);
12593     else if (element == EL_EM_EXIT_CLOSED)
12594       CheckExitEM(x, y);
12595     else if (element == EL_STEEL_EXIT_CLOSED)
12596       CheckExitSteel(x, y);
12597     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12598       CheckExitSteelEM(x, y);
12599     else if (element == EL_SP_EXIT_CLOSED)
12600       CheckExitSP(x, y);
12601     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12602              element == EL_EXPANDABLE_STEELWALL_GROWING)
12603       WallGrowing(x, y);
12604     else if (element == EL_EXPANDABLE_WALL ||
12605              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12606              element == EL_EXPANDABLE_WALL_VERTICAL ||
12607              element == EL_EXPANDABLE_WALL_ANY ||
12608              element == EL_BD_EXPANDABLE_WALL ||
12609              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12610              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12611              element == EL_EXPANDABLE_STEELWALL_ANY)
12612       CheckWallGrowing(x, y);
12613     else if (element == EL_FLAMES)
12614       CheckForDragon(x, y);
12615     else if (element == EL_EXPLOSION)
12616       ; // drawing of correct explosion animation is handled separately
12617     else if (element == EL_ELEMENT_SNAPPING ||
12618              element == EL_DIAGONAL_SHRINKING ||
12619              element == EL_DIAGONAL_GROWING)
12620     {
12621       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12622
12623       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12624     }
12625     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12626       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12627
12628     if (IS_BELT_ACTIVE(element))
12629       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12630
12631     if (game.magic_wall_active)
12632     {
12633       int jx = local_player->jx, jy = local_player->jy;
12634
12635       // play the element sound at the position nearest to the player
12636       if ((element == EL_MAGIC_WALL_FULL ||
12637            element == EL_MAGIC_WALL_ACTIVE ||
12638            element == EL_MAGIC_WALL_EMPTYING ||
12639            element == EL_BD_MAGIC_WALL_FULL ||
12640            element == EL_BD_MAGIC_WALL_ACTIVE ||
12641            element == EL_BD_MAGIC_WALL_EMPTYING ||
12642            element == EL_DC_MAGIC_WALL_FULL ||
12643            element == EL_DC_MAGIC_WALL_ACTIVE ||
12644            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12645           ABS(x - jx) + ABS(y - jy) <
12646           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12647       {
12648         magic_wall_x = x;
12649         magic_wall_y = y;
12650       }
12651     }
12652   }
12653
12654 #if USE_NEW_AMOEBA_CODE
12655   // new experimental amoeba growth stuff
12656   if (!(FrameCounter % 8))
12657   {
12658     static unsigned int random = 1684108901;
12659
12660     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12661     {
12662       x = RND(lev_fieldx);
12663       y = RND(lev_fieldy);
12664       element = Tile[x][y];
12665
12666       if (!IS_PLAYER(x, y) &&
12667           (element == EL_EMPTY ||
12668            CAN_GROW_INTO(element) ||
12669            element == EL_QUICKSAND_EMPTY ||
12670            element == EL_QUICKSAND_FAST_EMPTY ||
12671            element == EL_ACID_SPLASH_LEFT ||
12672            element == EL_ACID_SPLASH_RIGHT))
12673       {
12674         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12675             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12676             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12677             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12678           Tile[x][y] = EL_AMOEBA_DROP;
12679       }
12680
12681       random = random * 129 + 1;
12682     }
12683   }
12684 #endif
12685
12686   game.explosions_delayed = FALSE;
12687
12688   SCAN_PLAYFIELD(x, y)
12689   {
12690     element = Tile[x][y];
12691
12692     if (ExplodeField[x][y])
12693       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12694     else if (element == EL_EXPLOSION)
12695       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12696
12697     ExplodeField[x][y] = EX_TYPE_NONE;
12698   }
12699
12700   game.explosions_delayed = TRUE;
12701
12702   if (game.magic_wall_active)
12703   {
12704     if (!(game.magic_wall_time_left % 4))
12705     {
12706       int element = Tile[magic_wall_x][magic_wall_y];
12707
12708       if (element == EL_BD_MAGIC_WALL_FULL ||
12709           element == EL_BD_MAGIC_WALL_ACTIVE ||
12710           element == EL_BD_MAGIC_WALL_EMPTYING)
12711         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12712       else if (element == EL_DC_MAGIC_WALL_FULL ||
12713                element == EL_DC_MAGIC_WALL_ACTIVE ||
12714                element == EL_DC_MAGIC_WALL_EMPTYING)
12715         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12716       else
12717         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12718     }
12719
12720     if (game.magic_wall_time_left > 0)
12721     {
12722       game.magic_wall_time_left--;
12723
12724       if (!game.magic_wall_time_left)
12725       {
12726         SCAN_PLAYFIELD(x, y)
12727         {
12728           element = Tile[x][y];
12729
12730           if (element == EL_MAGIC_WALL_ACTIVE ||
12731               element == EL_MAGIC_WALL_FULL)
12732           {
12733             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12734             TEST_DrawLevelField(x, y);
12735           }
12736           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12737                    element == EL_BD_MAGIC_WALL_FULL)
12738           {
12739             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12740             TEST_DrawLevelField(x, y);
12741           }
12742           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12743                    element == EL_DC_MAGIC_WALL_FULL)
12744           {
12745             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12746             TEST_DrawLevelField(x, y);
12747           }
12748         }
12749
12750         game.magic_wall_active = FALSE;
12751       }
12752     }
12753   }
12754
12755   if (game.light_time_left > 0)
12756   {
12757     game.light_time_left--;
12758
12759     if (game.light_time_left == 0)
12760       RedrawAllLightSwitchesAndInvisibleElements();
12761   }
12762
12763   if (game.timegate_time_left > 0)
12764   {
12765     game.timegate_time_left--;
12766
12767     if (game.timegate_time_left == 0)
12768       CloseAllOpenTimegates();
12769   }
12770
12771   if (game.lenses_time_left > 0)
12772   {
12773     game.lenses_time_left--;
12774
12775     if (game.lenses_time_left == 0)
12776       RedrawAllInvisibleElementsForLenses();
12777   }
12778
12779   if (game.magnify_time_left > 0)
12780   {
12781     game.magnify_time_left--;
12782
12783     if (game.magnify_time_left == 0)
12784       RedrawAllInvisibleElementsForMagnifier();
12785   }
12786
12787   for (i = 0; i < MAX_PLAYERS; i++)
12788   {
12789     struct PlayerInfo *player = &stored_player[i];
12790
12791     if (SHIELD_ON(player))
12792     {
12793       if (player->shield_deadly_time_left)
12794         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12795       else if (player->shield_normal_time_left)
12796         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12797     }
12798   }
12799
12800 #if USE_DELAYED_GFX_REDRAW
12801   SCAN_PLAYFIELD(x, y)
12802   {
12803     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12804     {
12805       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12806          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12807
12808       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12809         DrawLevelField(x, y);
12810
12811       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12812         DrawLevelFieldCrumbled(x, y);
12813
12814       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12815         DrawLevelFieldCrumbledNeighbours(x, y);
12816
12817       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12818         DrawTwinkleOnField(x, y);
12819     }
12820
12821     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12822   }
12823 #endif
12824
12825   DrawAllPlayers();
12826   PlayAllPlayersSound();
12827
12828   for (i = 0; i < MAX_PLAYERS; i++)
12829   {
12830     struct PlayerInfo *player = &stored_player[i];
12831
12832     if (player->show_envelope != 0 && (!player->active ||
12833                                        player->MovPos == 0))
12834     {
12835       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12836
12837       player->show_envelope = 0;
12838     }
12839   }
12840
12841   // use random number generator in every frame to make it less predictable
12842   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12843     RND(1);
12844
12845   mouse_action_last = mouse_action;
12846 }
12847
12848 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12849 {
12850   int min_x = x, min_y = y, max_x = x, max_y = y;
12851   int scr_fieldx = getScreenFieldSizeX();
12852   int scr_fieldy = getScreenFieldSizeY();
12853   int i;
12854
12855   for (i = 0; i < MAX_PLAYERS; i++)
12856   {
12857     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12858
12859     if (!stored_player[i].active || &stored_player[i] == player)
12860       continue;
12861
12862     min_x = MIN(min_x, jx);
12863     min_y = MIN(min_y, jy);
12864     max_x = MAX(max_x, jx);
12865     max_y = MAX(max_y, jy);
12866   }
12867
12868   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12869 }
12870
12871 static boolean AllPlayersInVisibleScreen(void)
12872 {
12873   int i;
12874
12875   for (i = 0; i < MAX_PLAYERS; i++)
12876   {
12877     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12878
12879     if (!stored_player[i].active)
12880       continue;
12881
12882     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12883       return FALSE;
12884   }
12885
12886   return TRUE;
12887 }
12888
12889 void ScrollLevel(int dx, int dy)
12890 {
12891   int scroll_offset = 2 * TILEX_VAR;
12892   int x, y;
12893
12894   BlitBitmap(drawto_field, drawto_field,
12895              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12896              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12897              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12898              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12899              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12900              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12901
12902   if (dx != 0)
12903   {
12904     x = (dx == 1 ? BX1 : BX2);
12905     for (y = BY1; y <= BY2; y++)
12906       DrawScreenField(x, y);
12907   }
12908
12909   if (dy != 0)
12910   {
12911     y = (dy == 1 ? BY1 : BY2);
12912     for (x = BX1; x <= BX2; x++)
12913       DrawScreenField(x, y);
12914   }
12915
12916   redraw_mask |= REDRAW_FIELD;
12917 }
12918
12919 static boolean canFallDown(struct PlayerInfo *player)
12920 {
12921   int jx = player->jx, jy = player->jy;
12922
12923   return (IN_LEV_FIELD(jx, jy + 1) &&
12924           (IS_FREE(jx, jy + 1) ||
12925            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12926           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12927           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12928 }
12929
12930 static boolean canPassField(int x, int y, int move_dir)
12931 {
12932   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12933   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12934   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12935   int nextx = x + dx;
12936   int nexty = y + dy;
12937   int element = Tile[x][y];
12938
12939   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12940           !CAN_MOVE(element) &&
12941           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12942           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12943           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12944 }
12945
12946 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12947 {
12948   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12949   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12950   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12951   int newx = x + dx;
12952   int newy = y + dy;
12953
12954   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12955           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12956           (IS_DIGGABLE(Tile[newx][newy]) ||
12957            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12958            canPassField(newx, newy, move_dir)));
12959 }
12960
12961 static void CheckGravityMovement(struct PlayerInfo *player)
12962 {
12963   if (player->gravity && !player->programmed_action)
12964   {
12965     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12966     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12967     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12968     int jx = player->jx, jy = player->jy;
12969     boolean player_is_moving_to_valid_field =
12970       (!player_is_snapping &&
12971        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12972         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12973     boolean player_can_fall_down = canFallDown(player);
12974
12975     if (player_can_fall_down &&
12976         !player_is_moving_to_valid_field)
12977       player->programmed_action = MV_DOWN;
12978   }
12979 }
12980
12981 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12982 {
12983   return CheckGravityMovement(player);
12984
12985   if (player->gravity && !player->programmed_action)
12986   {
12987     int jx = player->jx, jy = player->jy;
12988     boolean field_under_player_is_free =
12989       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12990     boolean player_is_standing_on_valid_field =
12991       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12992        (IS_WALKABLE(Tile[jx][jy]) &&
12993         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12994
12995     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12996       player->programmed_action = MV_DOWN;
12997   }
12998 }
12999
13000 /*
13001   MovePlayerOneStep()
13002   -----------------------------------------------------------------------------
13003   dx, dy:               direction (non-diagonal) to try to move the player to
13004   real_dx, real_dy:     direction as read from input device (can be diagonal)
13005 */
13006
13007 boolean MovePlayerOneStep(struct PlayerInfo *player,
13008                           int dx, int dy, int real_dx, int real_dy)
13009 {
13010   int jx = player->jx, jy = player->jy;
13011   int new_jx = jx + dx, new_jy = jy + dy;
13012   int can_move;
13013   boolean player_can_move = !player->cannot_move;
13014
13015   if (!player->active || (!dx && !dy))
13016     return MP_NO_ACTION;
13017
13018   player->MovDir = (dx < 0 ? MV_LEFT :
13019                     dx > 0 ? MV_RIGHT :
13020                     dy < 0 ? MV_UP :
13021                     dy > 0 ? MV_DOWN :  MV_NONE);
13022
13023   if (!IN_LEV_FIELD(new_jx, new_jy))
13024     return MP_NO_ACTION;
13025
13026   if (!player_can_move)
13027   {
13028     if (player->MovPos == 0)
13029     {
13030       player->is_moving = FALSE;
13031       player->is_digging = FALSE;
13032       player->is_collecting = FALSE;
13033       player->is_snapping = FALSE;
13034       player->is_pushing = FALSE;
13035     }
13036   }
13037
13038   if (!network.enabled && game.centered_player_nr == -1 &&
13039       !AllPlayersInSight(player, new_jx, new_jy))
13040     return MP_NO_ACTION;
13041
13042   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13043   if (can_move != MP_MOVING)
13044     return can_move;
13045
13046   // check if DigField() has caused relocation of the player
13047   if (player->jx != jx || player->jy != jy)
13048     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13049
13050   StorePlayer[jx][jy] = 0;
13051   player->last_jx = jx;
13052   player->last_jy = jy;
13053   player->jx = new_jx;
13054   player->jy = new_jy;
13055   StorePlayer[new_jx][new_jy] = player->element_nr;
13056
13057   if (player->move_delay_value_next != -1)
13058   {
13059     player->move_delay_value = player->move_delay_value_next;
13060     player->move_delay_value_next = -1;
13061   }
13062
13063   player->MovPos =
13064     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13065
13066   player->step_counter++;
13067
13068   PlayerVisit[jx][jy] = FrameCounter;
13069
13070   player->is_moving = TRUE;
13071
13072 #if 1
13073   // should better be called in MovePlayer(), but this breaks some tapes
13074   ScrollPlayer(player, SCROLL_INIT);
13075 #endif
13076
13077   return MP_MOVING;
13078 }
13079
13080 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13081 {
13082   int jx = player->jx, jy = player->jy;
13083   int old_jx = jx, old_jy = jy;
13084   int moved = MP_NO_ACTION;
13085
13086   if (!player->active)
13087     return FALSE;
13088
13089   if (!dx && !dy)
13090   {
13091     if (player->MovPos == 0)
13092     {
13093       player->is_moving = FALSE;
13094       player->is_digging = FALSE;
13095       player->is_collecting = FALSE;
13096       player->is_snapping = FALSE;
13097       player->is_pushing = FALSE;
13098     }
13099
13100     return FALSE;
13101   }
13102
13103   if (player->move_delay > 0)
13104     return FALSE;
13105
13106   player->move_delay = -1;              // set to "uninitialized" value
13107
13108   // store if player is automatically moved to next field
13109   player->is_auto_moving = (player->programmed_action != MV_NONE);
13110
13111   // remove the last programmed player action
13112   player->programmed_action = 0;
13113
13114   if (player->MovPos)
13115   {
13116     // should only happen if pre-1.2 tape recordings are played
13117     // this is only for backward compatibility
13118
13119     int original_move_delay_value = player->move_delay_value;
13120
13121 #if DEBUG
13122     Debug("game:playing:MovePlayer",
13123           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13124           tape.counter);
13125 #endif
13126
13127     // scroll remaining steps with finest movement resolution
13128     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13129
13130     while (player->MovPos)
13131     {
13132       ScrollPlayer(player, SCROLL_GO_ON);
13133       ScrollScreen(NULL, SCROLL_GO_ON);
13134
13135       AdvanceFrameAndPlayerCounters(player->index_nr);
13136
13137       DrawAllPlayers();
13138       BackToFront_WithFrameDelay(0);
13139     }
13140
13141     player->move_delay_value = original_move_delay_value;
13142   }
13143
13144   player->is_active = FALSE;
13145
13146   if (player->last_move_dir & MV_HORIZONTAL)
13147   {
13148     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13149       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13150   }
13151   else
13152   {
13153     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13154       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13155   }
13156
13157   if (!moved && !player->is_active)
13158   {
13159     player->is_moving = FALSE;
13160     player->is_digging = FALSE;
13161     player->is_collecting = FALSE;
13162     player->is_snapping = FALSE;
13163     player->is_pushing = FALSE;
13164   }
13165
13166   jx = player->jx;
13167   jy = player->jy;
13168
13169   if (moved & MP_MOVING && !ScreenMovPos &&
13170       (player->index_nr == game.centered_player_nr ||
13171        game.centered_player_nr == -1))
13172   {
13173     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13174
13175     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13176     {
13177       // actual player has left the screen -- scroll in that direction
13178       if (jx != old_jx)         // player has moved horizontally
13179         scroll_x += (jx - old_jx);
13180       else                      // player has moved vertically
13181         scroll_y += (jy - old_jy);
13182     }
13183     else
13184     {
13185       int offset_raw = game.scroll_delay_value;
13186
13187       if (jx != old_jx)         // player has moved horizontally
13188       {
13189         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13190         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13191         int new_scroll_x = jx - MIDPOSX + offset_x;
13192
13193         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13194             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13195           scroll_x = new_scroll_x;
13196
13197         // don't scroll over playfield boundaries
13198         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13199
13200         // don't scroll more than one field at a time
13201         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13202
13203         // don't scroll against the player's moving direction
13204         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13205             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13206           scroll_x = old_scroll_x;
13207       }
13208       else                      // player has moved vertically
13209       {
13210         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13211         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13212         int new_scroll_y = jy - MIDPOSY + offset_y;
13213
13214         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13215             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13216           scroll_y = new_scroll_y;
13217
13218         // don't scroll over playfield boundaries
13219         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13220
13221         // don't scroll more than one field at a time
13222         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13223
13224         // don't scroll against the player's moving direction
13225         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13226             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13227           scroll_y = old_scroll_y;
13228       }
13229     }
13230
13231     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13232     {
13233       if (!network.enabled && game.centered_player_nr == -1 &&
13234           !AllPlayersInVisibleScreen())
13235       {
13236         scroll_x = old_scroll_x;
13237         scroll_y = old_scroll_y;
13238       }
13239       else
13240       {
13241         ScrollScreen(player, SCROLL_INIT);
13242         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13243       }
13244     }
13245   }
13246
13247   player->StepFrame = 0;
13248
13249   if (moved & MP_MOVING)
13250   {
13251     if (old_jx != jx && old_jy == jy)
13252       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13253     else if (old_jx == jx && old_jy != jy)
13254       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13255
13256     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13257
13258     player->last_move_dir = player->MovDir;
13259     player->is_moving = TRUE;
13260     player->is_snapping = FALSE;
13261     player->is_switching = FALSE;
13262     player->is_dropping = FALSE;
13263     player->is_dropping_pressed = FALSE;
13264     player->drop_pressed_delay = 0;
13265
13266 #if 0
13267     // should better be called here than above, but this breaks some tapes
13268     ScrollPlayer(player, SCROLL_INIT);
13269 #endif
13270   }
13271   else
13272   {
13273     CheckGravityMovementWhenNotMoving(player);
13274
13275     player->is_moving = FALSE;
13276
13277     /* at this point, the player is allowed to move, but cannot move right now
13278        (e.g. because of something blocking the way) -- ensure that the player
13279        is also allowed to move in the next frame (in old versions before 3.1.1,
13280        the player was forced to wait again for eight frames before next try) */
13281
13282     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13283       player->move_delay = 0;   // allow direct movement in the next frame
13284   }
13285
13286   if (player->move_delay == -1)         // not yet initialized by DigField()
13287     player->move_delay = player->move_delay_value;
13288
13289   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13290   {
13291     TestIfPlayerTouchesBadThing(jx, jy);
13292     TestIfPlayerTouchesCustomElement(jx, jy);
13293   }
13294
13295   if (!player->active)
13296     RemovePlayer(player);
13297
13298   return moved;
13299 }
13300
13301 void ScrollPlayer(struct PlayerInfo *player, int mode)
13302 {
13303   int jx = player->jx, jy = player->jy;
13304   int last_jx = player->last_jx, last_jy = player->last_jy;
13305   int move_stepsize = TILEX / player->move_delay_value;
13306
13307   if (!player->active)
13308     return;
13309
13310   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13311     return;
13312
13313   if (mode == SCROLL_INIT)
13314   {
13315     player->actual_frame_counter.count = FrameCounter;
13316     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13317
13318     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13319         Tile[last_jx][last_jy] == EL_EMPTY)
13320     {
13321       int last_field_block_delay = 0;   // start with no blocking at all
13322       int block_delay_adjustment = player->block_delay_adjustment;
13323
13324       // if player blocks last field, add delay for exactly one move
13325       if (player->block_last_field)
13326       {
13327         last_field_block_delay += player->move_delay_value;
13328
13329         // when blocking enabled, prevent moving up despite gravity
13330         if (player->gravity && player->MovDir == MV_UP)
13331           block_delay_adjustment = -1;
13332       }
13333
13334       // add block delay adjustment (also possible when not blocking)
13335       last_field_block_delay += block_delay_adjustment;
13336
13337       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13338       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13339     }
13340
13341     if (player->MovPos != 0)    // player has not yet reached destination
13342       return;
13343   }
13344   else if (!FrameReached(&player->actual_frame_counter))
13345     return;
13346
13347   if (player->MovPos != 0)
13348   {
13349     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13350     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13351
13352     // before DrawPlayer() to draw correct player graphic for this case
13353     if (player->MovPos == 0)
13354       CheckGravityMovement(player);
13355   }
13356
13357   if (player->MovPos == 0)      // player reached destination field
13358   {
13359     if (player->move_delay_reset_counter > 0)
13360     {
13361       player->move_delay_reset_counter--;
13362
13363       if (player->move_delay_reset_counter == 0)
13364       {
13365         // continue with normal speed after quickly moving through gate
13366         HALVE_PLAYER_SPEED(player);
13367
13368         // be able to make the next move without delay
13369         player->move_delay = 0;
13370       }
13371     }
13372
13373     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13374         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13375         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13376         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13377         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13378         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13379         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13380         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13381     {
13382       ExitPlayer(player);
13383
13384       if (game.players_still_needed == 0 &&
13385           (game.friends_still_needed == 0 ||
13386            IS_SP_ELEMENT(Tile[jx][jy])))
13387         LevelSolved();
13388     }
13389
13390     player->last_jx = jx;
13391     player->last_jy = jy;
13392
13393     // this breaks one level: "machine", level 000
13394     {
13395       int move_direction = player->MovDir;
13396       int enter_side = MV_DIR_OPPOSITE(move_direction);
13397       int leave_side = move_direction;
13398       int old_jx = last_jx;
13399       int old_jy = last_jy;
13400       int old_element = Tile[old_jx][old_jy];
13401       int new_element = Tile[jx][jy];
13402
13403       if (IS_CUSTOM_ELEMENT(old_element))
13404         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13405                                    CE_LEFT_BY_PLAYER,
13406                                    player->index_bit, leave_side);
13407
13408       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13409                                           CE_PLAYER_LEAVES_X,
13410                                           player->index_bit, leave_side);
13411
13412       // needed because pushed element has not yet reached its destination,
13413       // so it would trigger a change event at its previous field location
13414       if (!player->is_pushing)
13415       {
13416         if (IS_CUSTOM_ELEMENT(new_element))
13417           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13418                                      player->index_bit, enter_side);
13419
13420         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13421                                             CE_PLAYER_ENTERS_X,
13422                                             player->index_bit, enter_side);
13423       }
13424
13425       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13426                                         CE_MOVE_OF_X, move_direction);
13427     }
13428
13429     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13430     {
13431       TestIfPlayerTouchesBadThing(jx, jy);
13432       TestIfPlayerTouchesCustomElement(jx, jy);
13433
13434       // needed because pushed element has not yet reached its destination,
13435       // so it would trigger a change event at its previous field location
13436       if (!player->is_pushing)
13437         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13438
13439       if (level.finish_dig_collect &&
13440           (player->is_digging || player->is_collecting))
13441       {
13442         int last_element = player->last_removed_element;
13443         int move_direction = player->MovDir;
13444         int enter_side = MV_DIR_OPPOSITE(move_direction);
13445         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13446                             CE_PLAYER_COLLECTS_X);
13447
13448         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13449                                             player->index_bit, enter_side);
13450
13451         player->last_removed_element = EL_UNDEFINED;
13452       }
13453
13454       if (!player->active)
13455         RemovePlayer(player);
13456     }
13457
13458     if (level.use_step_counter)
13459       CheckLevelTime_StepCounter();
13460
13461     if (tape.single_step && tape.recording && !tape.pausing &&
13462         !player->programmed_action)
13463       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13464
13465     if (!player->programmed_action)
13466       CheckSaveEngineSnapshot(player);
13467   }
13468 }
13469
13470 void ScrollScreen(struct PlayerInfo *player, int mode)
13471 {
13472   static DelayCounter screen_frame_counter = { 0 };
13473
13474   if (mode == SCROLL_INIT)
13475   {
13476     // set scrolling step size according to actual player's moving speed
13477     ScrollStepSize = TILEX / player->move_delay_value;
13478
13479     screen_frame_counter.count = FrameCounter;
13480     screen_frame_counter.value = 1;
13481
13482     ScreenMovDir = player->MovDir;
13483     ScreenMovPos = player->MovPos;
13484     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13485     return;
13486   }
13487   else if (!FrameReached(&screen_frame_counter))
13488     return;
13489
13490   if (ScreenMovPos)
13491   {
13492     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13493     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13494     redraw_mask |= REDRAW_FIELD;
13495   }
13496   else
13497     ScreenMovDir = MV_NONE;
13498 }
13499
13500 void CheckNextToConditions(int x, int y)
13501 {
13502   int element = Tile[x][y];
13503
13504   if (IS_PLAYER(x, y))
13505     TestIfPlayerNextToCustomElement(x, y);
13506
13507   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13508       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13509     TestIfElementNextToCustomElement(x, y);
13510 }
13511
13512 void TestIfPlayerNextToCustomElement(int x, int y)
13513 {
13514   struct XY *xy = xy_topdown;
13515   static int trigger_sides[4][2] =
13516   {
13517     // center side       border side
13518     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13519     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13520     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13521     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13522   };
13523   int i;
13524
13525   if (!IS_PLAYER(x, y))
13526     return;
13527
13528   struct PlayerInfo *player = PLAYERINFO(x, y);
13529
13530   if (player->is_moving)
13531     return;
13532
13533   for (i = 0; i < NUM_DIRECTIONS; i++)
13534   {
13535     int xx = x + xy[i].x;
13536     int yy = y + xy[i].y;
13537     int border_side = trigger_sides[i][1];
13538     int border_element;
13539
13540     if (!IN_LEV_FIELD(xx, yy))
13541       continue;
13542
13543     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13544       continue;         // center and border element not connected
13545
13546     border_element = Tile[xx][yy];
13547
13548     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13549                                player->index_bit, border_side);
13550     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13551                                         CE_PLAYER_NEXT_TO_X,
13552                                         player->index_bit, border_side);
13553
13554     /* use player element that is initially defined in the level playfield,
13555        not the player element that corresponds to the runtime player number
13556        (example: a level that contains EL_PLAYER_3 as the only player would
13557        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13558
13559     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13560                              CE_NEXT_TO_X, border_side);
13561   }
13562 }
13563
13564 void TestIfPlayerTouchesCustomElement(int x, int y)
13565 {
13566   struct XY *xy = xy_topdown;
13567   static int trigger_sides[4][2] =
13568   {
13569     // center side       border side
13570     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13571     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13572     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13573     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13574   };
13575   static int touch_dir[4] =
13576   {
13577     MV_LEFT | MV_RIGHT,
13578     MV_UP   | MV_DOWN,
13579     MV_UP   | MV_DOWN,
13580     MV_LEFT | MV_RIGHT
13581   };
13582   int center_element = Tile[x][y];      // should always be non-moving!
13583   int i;
13584
13585   for (i = 0; i < NUM_DIRECTIONS; i++)
13586   {
13587     int xx = x + xy[i].x;
13588     int yy = y + xy[i].y;
13589     int center_side = trigger_sides[i][0];
13590     int border_side = trigger_sides[i][1];
13591     int border_element;
13592
13593     if (!IN_LEV_FIELD(xx, yy))
13594       continue;
13595
13596     if (IS_PLAYER(x, y))                // player found at center element
13597     {
13598       struct PlayerInfo *player = PLAYERINFO(x, y);
13599
13600       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13601         border_element = Tile[xx][yy];          // may be moving!
13602       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13603         border_element = Tile[xx][yy];
13604       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13605         border_element = MovingOrBlocked2Element(xx, yy);
13606       else
13607         continue;               // center and border element do not touch
13608
13609       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13610                                  player->index_bit, border_side);
13611       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13612                                           CE_PLAYER_TOUCHES_X,
13613                                           player->index_bit, border_side);
13614
13615       {
13616         /* use player element that is initially defined in the level playfield,
13617            not the player element that corresponds to the runtime player number
13618            (example: a level that contains EL_PLAYER_3 as the only player would
13619            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13620         int player_element = PLAYERINFO(x, y)->initial_element;
13621
13622         // as element "X" is the player here, check opposite (center) side
13623         CheckElementChangeBySide(xx, yy, border_element, player_element,
13624                                  CE_TOUCHING_X, center_side);
13625       }
13626     }
13627     else if (IS_PLAYER(xx, yy))         // player found at border element
13628     {
13629       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13630
13631       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13632       {
13633         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13634           continue;             // center and border element do not touch
13635       }
13636
13637       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13638                                  player->index_bit, center_side);
13639       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13640                                           CE_PLAYER_TOUCHES_X,
13641                                           player->index_bit, center_side);
13642
13643       {
13644         /* use player element that is initially defined in the level playfield,
13645            not the player element that corresponds to the runtime player number
13646            (example: a level that contains EL_PLAYER_3 as the only player would
13647            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13648         int player_element = PLAYERINFO(xx, yy)->initial_element;
13649
13650         // as element "X" is the player here, check opposite (border) side
13651         CheckElementChangeBySide(x, y, center_element, player_element,
13652                                  CE_TOUCHING_X, border_side);
13653       }
13654
13655       break;
13656     }
13657   }
13658 }
13659
13660 void TestIfElementNextToCustomElement(int x, int y)
13661 {
13662   struct XY *xy = xy_topdown;
13663   static int trigger_sides[4][2] =
13664   {
13665     // center side      border side
13666     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13667     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13668     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13669     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13670   };
13671   int center_element = Tile[x][y];      // should always be non-moving!
13672   int i;
13673
13674   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13675     return;
13676
13677   for (i = 0; i < NUM_DIRECTIONS; i++)
13678   {
13679     int xx = x + xy[i].x;
13680     int yy = y + xy[i].y;
13681     int border_side = trigger_sides[i][1];
13682     int border_element;
13683
13684     if (!IN_LEV_FIELD(xx, yy))
13685       continue;
13686
13687     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13688       continue;                 // center and border element not connected
13689
13690     border_element = Tile[xx][yy];
13691
13692     // check for change of center element (but change it only once)
13693     if (CheckElementChangeBySide(x, y, center_element, border_element,
13694                                  CE_NEXT_TO_X, border_side))
13695       break;
13696   }
13697 }
13698
13699 void TestIfElementTouchesCustomElement(int x, int y)
13700 {
13701   struct XY *xy = xy_topdown;
13702   static int trigger_sides[4][2] =
13703   {
13704     // center side      border side
13705     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13706     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13707     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13708     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13709   };
13710   static int touch_dir[4] =
13711   {
13712     MV_LEFT | MV_RIGHT,
13713     MV_UP   | MV_DOWN,
13714     MV_UP   | MV_DOWN,
13715     MV_LEFT | MV_RIGHT
13716   };
13717   boolean change_center_element = FALSE;
13718   int center_element = Tile[x][y];      // should always be non-moving!
13719   int border_element_old[NUM_DIRECTIONS];
13720   int i;
13721
13722   for (i = 0; i < NUM_DIRECTIONS; i++)
13723   {
13724     int xx = x + xy[i].x;
13725     int yy = y + xy[i].y;
13726     int border_element;
13727
13728     border_element_old[i] = -1;
13729
13730     if (!IN_LEV_FIELD(xx, yy))
13731       continue;
13732
13733     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13734       border_element = Tile[xx][yy];    // may be moving!
13735     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13736       border_element = Tile[xx][yy];
13737     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13738       border_element = MovingOrBlocked2Element(xx, yy);
13739     else
13740       continue;                 // center and border element do not touch
13741
13742     border_element_old[i] = border_element;
13743   }
13744
13745   for (i = 0; i < NUM_DIRECTIONS; i++)
13746   {
13747     int xx = x + xy[i].x;
13748     int yy = y + xy[i].y;
13749     int center_side = trigger_sides[i][0];
13750     int border_element = border_element_old[i];
13751
13752     if (border_element == -1)
13753       continue;
13754
13755     // check for change of border element
13756     CheckElementChangeBySide(xx, yy, border_element, center_element,
13757                              CE_TOUCHING_X, center_side);
13758
13759     // (center element cannot be player, so we don't have to check this here)
13760   }
13761
13762   for (i = 0; i < NUM_DIRECTIONS; i++)
13763   {
13764     int xx = x + xy[i].x;
13765     int yy = y + xy[i].y;
13766     int border_side = trigger_sides[i][1];
13767     int border_element = border_element_old[i];
13768
13769     if (border_element == -1)
13770       continue;
13771
13772     // check for change of center element (but change it only once)
13773     if (!change_center_element)
13774       change_center_element =
13775         CheckElementChangeBySide(x, y, center_element, border_element,
13776                                  CE_TOUCHING_X, border_side);
13777
13778     if (IS_PLAYER(xx, yy))
13779     {
13780       /* use player element that is initially defined in the level playfield,
13781          not the player element that corresponds to the runtime player number
13782          (example: a level that contains EL_PLAYER_3 as the only player would
13783          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13784       int player_element = PLAYERINFO(xx, yy)->initial_element;
13785
13786       // as element "X" is the player here, check opposite (border) side
13787       CheckElementChangeBySide(x, y, center_element, player_element,
13788                                CE_TOUCHING_X, border_side);
13789     }
13790   }
13791 }
13792
13793 void TestIfElementHitsCustomElement(int x, int y, int direction)
13794 {
13795   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13796   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13797   int hitx = x + dx, hity = y + dy;
13798   int hitting_element = Tile[x][y];
13799   int touched_element;
13800
13801   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13802     return;
13803
13804   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13805                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13806
13807   if (IN_LEV_FIELD(hitx, hity))
13808   {
13809     int opposite_direction = MV_DIR_OPPOSITE(direction);
13810     int hitting_side = direction;
13811     int touched_side = opposite_direction;
13812     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13813                           MovDir[hitx][hity] != direction ||
13814                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13815
13816     object_hit = TRUE;
13817
13818     if (object_hit)
13819     {
13820       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13821                                CE_HITTING_X, touched_side);
13822
13823       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13824                                CE_HIT_BY_X, hitting_side);
13825
13826       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13827                                CE_HIT_BY_SOMETHING, opposite_direction);
13828
13829       if (IS_PLAYER(hitx, hity))
13830       {
13831         /* use player element that is initially defined in the level playfield,
13832            not the player element that corresponds to the runtime player number
13833            (example: a level that contains EL_PLAYER_3 as the only player would
13834            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13835         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13836
13837         CheckElementChangeBySide(x, y, hitting_element, player_element,
13838                                  CE_HITTING_X, touched_side);
13839       }
13840     }
13841   }
13842
13843   // "hitting something" is also true when hitting the playfield border
13844   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13845                            CE_HITTING_SOMETHING, direction);
13846 }
13847
13848 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13849 {
13850   int i, kill_x = -1, kill_y = -1;
13851
13852   int bad_element = -1;
13853   struct XY *test_xy = xy_topdown;
13854   static int test_dir[4] =
13855   {
13856     MV_UP,
13857     MV_LEFT,
13858     MV_RIGHT,
13859     MV_DOWN
13860   };
13861
13862   for (i = 0; i < NUM_DIRECTIONS; i++)
13863   {
13864     int test_x, test_y, test_move_dir, test_element;
13865
13866     test_x = good_x + test_xy[i].x;
13867     test_y = good_y + test_xy[i].y;
13868
13869     if (!IN_LEV_FIELD(test_x, test_y))
13870       continue;
13871
13872     test_move_dir =
13873       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13874
13875     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13876
13877     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13878        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13879     */
13880     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13881         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13882     {
13883       kill_x = test_x;
13884       kill_y = test_y;
13885       bad_element = test_element;
13886
13887       break;
13888     }
13889   }
13890
13891   if (kill_x != -1 || kill_y != -1)
13892   {
13893     if (IS_PLAYER(good_x, good_y))
13894     {
13895       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13896
13897       if (player->shield_deadly_time_left > 0 &&
13898           !IS_INDESTRUCTIBLE(bad_element))
13899         Bang(kill_x, kill_y);
13900       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13901         KillPlayer(player);
13902     }
13903     else
13904       Bang(good_x, good_y);
13905   }
13906 }
13907
13908 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13909 {
13910   int i, kill_x = -1, kill_y = -1;
13911   int bad_element = Tile[bad_x][bad_y];
13912   struct XY *test_xy = xy_topdown;
13913   static int touch_dir[4] =
13914   {
13915     MV_LEFT | MV_RIGHT,
13916     MV_UP   | MV_DOWN,
13917     MV_UP   | MV_DOWN,
13918     MV_LEFT | MV_RIGHT
13919   };
13920   static int test_dir[4] =
13921   {
13922     MV_UP,
13923     MV_LEFT,
13924     MV_RIGHT,
13925     MV_DOWN
13926   };
13927
13928   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13929     return;
13930
13931   for (i = 0; i < NUM_DIRECTIONS; i++)
13932   {
13933     int test_x, test_y, test_move_dir, test_element;
13934
13935     test_x = bad_x + test_xy[i].x;
13936     test_y = bad_y + test_xy[i].y;
13937
13938     if (!IN_LEV_FIELD(test_x, test_y))
13939       continue;
13940
13941     test_move_dir =
13942       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13943
13944     test_element = Tile[test_x][test_y];
13945
13946     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13947        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13948     */
13949     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13950         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13951     {
13952       // good thing is player or penguin that does not move away
13953       if (IS_PLAYER(test_x, test_y))
13954       {
13955         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13956
13957         if (bad_element == EL_ROBOT && player->is_moving)
13958           continue;     // robot does not kill player if he is moving
13959
13960         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13961         {
13962           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13963             continue;           // center and border element do not touch
13964         }
13965
13966         kill_x = test_x;
13967         kill_y = test_y;
13968
13969         break;
13970       }
13971       else if (test_element == EL_PENGUIN)
13972       {
13973         kill_x = test_x;
13974         kill_y = test_y;
13975
13976         break;
13977       }
13978     }
13979   }
13980
13981   if (kill_x != -1 || kill_y != -1)
13982   {
13983     if (IS_PLAYER(kill_x, kill_y))
13984     {
13985       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13986
13987       if (player->shield_deadly_time_left > 0 &&
13988           !IS_INDESTRUCTIBLE(bad_element))
13989         Bang(bad_x, bad_y);
13990       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13991         KillPlayer(player);
13992     }
13993     else
13994       Bang(kill_x, kill_y);
13995   }
13996 }
13997
13998 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13999 {
14000   int bad_element = Tile[bad_x][bad_y];
14001   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14002   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14003   int test_x = bad_x + dx, test_y = bad_y + dy;
14004   int test_move_dir, test_element;
14005   int kill_x = -1, kill_y = -1;
14006
14007   if (!IN_LEV_FIELD(test_x, test_y))
14008     return;
14009
14010   test_move_dir =
14011     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14012
14013   test_element = Tile[test_x][test_y];
14014
14015   if (test_move_dir != bad_move_dir)
14016   {
14017     // good thing can be player or penguin that does not move away
14018     if (IS_PLAYER(test_x, test_y))
14019     {
14020       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14021
14022       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14023          player as being hit when he is moving towards the bad thing, because
14024          the "get hit by" condition would be lost after the player stops) */
14025       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14026         return;         // player moves away from bad thing
14027
14028       kill_x = test_x;
14029       kill_y = test_y;
14030     }
14031     else if (test_element == EL_PENGUIN)
14032     {
14033       kill_x = test_x;
14034       kill_y = test_y;
14035     }
14036   }
14037
14038   if (kill_x != -1 || kill_y != -1)
14039   {
14040     if (IS_PLAYER(kill_x, kill_y))
14041     {
14042       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14043
14044       if (player->shield_deadly_time_left > 0 &&
14045           !IS_INDESTRUCTIBLE(bad_element))
14046         Bang(bad_x, bad_y);
14047       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14048         KillPlayer(player);
14049     }
14050     else
14051       Bang(kill_x, kill_y);
14052   }
14053 }
14054
14055 void TestIfPlayerTouchesBadThing(int x, int y)
14056 {
14057   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14058 }
14059
14060 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14061 {
14062   TestIfGoodThingHitsBadThing(x, y, move_dir);
14063 }
14064
14065 void TestIfBadThingTouchesPlayer(int x, int y)
14066 {
14067   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14068 }
14069
14070 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14071 {
14072   TestIfBadThingHitsGoodThing(x, y, move_dir);
14073 }
14074
14075 void TestIfFriendTouchesBadThing(int x, int y)
14076 {
14077   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14078 }
14079
14080 void TestIfBadThingTouchesFriend(int x, int y)
14081 {
14082   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14083 }
14084
14085 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14086 {
14087   int i, kill_x = bad_x, kill_y = bad_y;
14088   struct XY *xy = xy_topdown;
14089
14090   for (i = 0; i < NUM_DIRECTIONS; i++)
14091   {
14092     int x, y, element;
14093
14094     x = bad_x + xy[i].x;
14095     y = bad_y + xy[i].y;
14096     if (!IN_LEV_FIELD(x, y))
14097       continue;
14098
14099     element = Tile[x][y];
14100     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14101         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14102     {
14103       kill_x = x;
14104       kill_y = y;
14105       break;
14106     }
14107   }
14108
14109   if (kill_x != bad_x || kill_y != bad_y)
14110     Bang(bad_x, bad_y);
14111 }
14112
14113 void KillPlayer(struct PlayerInfo *player)
14114 {
14115   int jx = player->jx, jy = player->jy;
14116
14117   if (!player->active)
14118     return;
14119
14120 #if 0
14121   Debug("game:playing:KillPlayer",
14122         "0: killed == %d, active == %d, reanimated == %d",
14123         player->killed, player->active, player->reanimated);
14124 #endif
14125
14126   /* the following code was introduced to prevent an infinite loop when calling
14127      -> Bang()
14128      -> CheckTriggeredElementChangeExt()
14129      -> ExecuteCustomElementAction()
14130      -> KillPlayer()
14131      -> (infinitely repeating the above sequence of function calls)
14132      which occurs when killing the player while having a CE with the setting
14133      "kill player X when explosion of <player X>"; the solution using a new
14134      field "player->killed" was chosen for backwards compatibility, although
14135      clever use of the fields "player->active" etc. would probably also work */
14136 #if 1
14137   if (player->killed)
14138     return;
14139 #endif
14140
14141   player->killed = TRUE;
14142
14143   // remove accessible field at the player's position
14144   RemoveField(jx, jy);
14145
14146   // deactivate shield (else Bang()/Explode() would not work right)
14147   player->shield_normal_time_left = 0;
14148   player->shield_deadly_time_left = 0;
14149
14150 #if 0
14151   Debug("game:playing:KillPlayer",
14152         "1: killed == %d, active == %d, reanimated == %d",
14153         player->killed, player->active, player->reanimated);
14154 #endif
14155
14156   Bang(jx, jy);
14157
14158 #if 0
14159   Debug("game:playing:KillPlayer",
14160         "2: killed == %d, active == %d, reanimated == %d",
14161         player->killed, player->active, player->reanimated);
14162 #endif
14163
14164   if (player->reanimated)       // killed player may have been reanimated
14165     player->killed = player->reanimated = FALSE;
14166   else
14167     BuryPlayer(player);
14168 }
14169
14170 static void KillPlayerUnlessEnemyProtected(int x, int y)
14171 {
14172   if (!PLAYER_ENEMY_PROTECTED(x, y))
14173     KillPlayer(PLAYERINFO(x, y));
14174 }
14175
14176 static void KillPlayerUnlessExplosionProtected(int x, int y)
14177 {
14178   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14179     KillPlayer(PLAYERINFO(x, y));
14180 }
14181
14182 void BuryPlayer(struct PlayerInfo *player)
14183 {
14184   int jx = player->jx, jy = player->jy;
14185
14186   if (!player->active)
14187     return;
14188
14189   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14190
14191   RemovePlayer(player);
14192
14193   player->buried = TRUE;
14194
14195   if (game.all_players_gone)
14196     game.GameOver = TRUE;
14197 }
14198
14199 void RemovePlayer(struct PlayerInfo *player)
14200 {
14201   int jx = player->jx, jy = player->jy;
14202   int i, found = FALSE;
14203
14204   player->present = FALSE;
14205   player->active = FALSE;
14206
14207   // required for some CE actions (even if the player is not active anymore)
14208   player->MovPos = 0;
14209
14210   if (!ExplodeField[jx][jy])
14211     StorePlayer[jx][jy] = 0;
14212
14213   if (player->is_moving)
14214     TEST_DrawLevelField(player->last_jx, player->last_jy);
14215
14216   for (i = 0; i < MAX_PLAYERS; i++)
14217     if (stored_player[i].active)
14218       found = TRUE;
14219
14220   if (!found)
14221   {
14222     game.all_players_gone = TRUE;
14223     game.GameOver = TRUE;
14224   }
14225
14226   game.exit_x = game.robot_wheel_x = jx;
14227   game.exit_y = game.robot_wheel_y = jy;
14228 }
14229
14230 void ExitPlayer(struct PlayerInfo *player)
14231 {
14232   DrawPlayer(player);   // needed here only to cleanup last field
14233   RemovePlayer(player);
14234
14235   if (game.players_still_needed > 0)
14236     game.players_still_needed--;
14237 }
14238
14239 static void SetFieldForSnapping(int x, int y, int element, int direction,
14240                                 int player_index_bit)
14241 {
14242   struct ElementInfo *ei = &element_info[element];
14243   int direction_bit = MV_DIR_TO_BIT(direction);
14244   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14245   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14246                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14247
14248   Tile[x][y] = EL_ELEMENT_SNAPPING;
14249   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14250   MovDir[x][y] = direction;
14251   Store[x][y] = element;
14252   Store2[x][y] = player_index_bit;
14253
14254   ResetGfxAnimation(x, y);
14255
14256   GfxElement[x][y] = element;
14257   GfxAction[x][y] = action;
14258   GfxDir[x][y] = direction;
14259   GfxFrame[x][y] = -1;
14260 }
14261
14262 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14263                                    int player_index_bit)
14264 {
14265   TestIfElementTouchesCustomElement(x, y);      // for empty space
14266
14267   if (level.finish_dig_collect)
14268   {
14269     int dig_side = MV_DIR_OPPOSITE(direction);
14270     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14271                         CE_PLAYER_COLLECTS_X);
14272
14273     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14274                                         player_index_bit, dig_side);
14275     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14276                                         player_index_bit, dig_side);
14277   }
14278 }
14279
14280 /*
14281   =============================================================================
14282   checkDiagonalPushing()
14283   -----------------------------------------------------------------------------
14284   check if diagonal input device direction results in pushing of object
14285   (by checking if the alternative direction is walkable, diggable, ...)
14286   =============================================================================
14287 */
14288
14289 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14290                                     int x, int y, int real_dx, int real_dy)
14291 {
14292   int jx, jy, dx, dy, xx, yy;
14293
14294   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14295     return TRUE;
14296
14297   // diagonal direction: check alternative direction
14298   jx = player->jx;
14299   jy = player->jy;
14300   dx = x - jx;
14301   dy = y - jy;
14302   xx = jx + (dx == 0 ? real_dx : 0);
14303   yy = jy + (dy == 0 ? real_dy : 0);
14304
14305   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14306 }
14307
14308 /*
14309   =============================================================================
14310   DigField()
14311   -----------------------------------------------------------------------------
14312   x, y:                 field next to player (non-diagonal) to try to dig to
14313   real_dx, real_dy:     direction as read from input device (can be diagonal)
14314   =============================================================================
14315 */
14316
14317 static int DigField(struct PlayerInfo *player,
14318                     int oldx, int oldy, int x, int y,
14319                     int real_dx, int real_dy, int mode)
14320 {
14321   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14322   boolean player_was_pushing = player->is_pushing;
14323   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14324   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14325   int jx = oldx, jy = oldy;
14326   int dx = x - jx, dy = y - jy;
14327   int nextx = x + dx, nexty = y + dy;
14328   int move_direction = (dx == -1 ? MV_LEFT  :
14329                         dx == +1 ? MV_RIGHT :
14330                         dy == -1 ? MV_UP    :
14331                         dy == +1 ? MV_DOWN  : MV_NONE);
14332   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14333   int dig_side = MV_DIR_OPPOSITE(move_direction);
14334   int old_element = Tile[jx][jy];
14335   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14336   int collect_count;
14337
14338   if (is_player)                // function can also be called by EL_PENGUIN
14339   {
14340     if (player->MovPos == 0)
14341     {
14342       player->is_digging = FALSE;
14343       player->is_collecting = FALSE;
14344     }
14345
14346     if (player->MovPos == 0)    // last pushing move finished
14347       player->is_pushing = FALSE;
14348
14349     if (mode == DF_NO_PUSH)     // player just stopped pushing
14350     {
14351       player->is_switching = FALSE;
14352       player->push_delay = -1;
14353
14354       return MP_NO_ACTION;
14355     }
14356   }
14357   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14358     old_element = Back[jx][jy];
14359
14360   // in case of element dropped at player position, check background
14361   else if (Back[jx][jy] != EL_EMPTY &&
14362            game.engine_version >= VERSION_IDENT(2,2,0,0))
14363     old_element = Back[jx][jy];
14364
14365   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14366     return MP_NO_ACTION;        // field has no opening in this direction
14367
14368   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14369     return MP_NO_ACTION;        // field has no opening in this direction
14370
14371   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14372   {
14373     SplashAcid(x, y);
14374
14375     Tile[jx][jy] = player->artwork_element;
14376     InitMovingField(jx, jy, MV_DOWN);
14377     Store[jx][jy] = EL_ACID;
14378     ContinueMoving(jx, jy);
14379     BuryPlayer(player);
14380
14381     return MP_DONT_RUN_INTO;
14382   }
14383
14384   if (player_can_move && DONT_RUN_INTO(element))
14385   {
14386     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14387
14388     return MP_DONT_RUN_INTO;
14389   }
14390
14391   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14392     return MP_NO_ACTION;
14393
14394   collect_count = element_info[element].collect_count_initial;
14395
14396   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14397     return MP_NO_ACTION;
14398
14399   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14400     player_can_move = player_can_move_or_snap;
14401
14402   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14403       game.engine_version >= VERSION_IDENT(2,2,0,0))
14404   {
14405     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14406                                player->index_bit, dig_side);
14407     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14408                                         player->index_bit, dig_side);
14409
14410     if (element == EL_DC_LANDMINE)
14411       Bang(x, y);
14412
14413     if (Tile[x][y] != element)          // field changed by snapping
14414       return MP_ACTION;
14415
14416     return MP_NO_ACTION;
14417   }
14418
14419   if (player->gravity && is_player && !player->is_auto_moving &&
14420       canFallDown(player) && move_direction != MV_DOWN &&
14421       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14422     return MP_NO_ACTION;        // player cannot walk here due to gravity
14423
14424   if (player_can_move &&
14425       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14426   {
14427     int sound_element = SND_ELEMENT(element);
14428     int sound_action = ACTION_WALKING;
14429
14430     if (IS_RND_GATE(element))
14431     {
14432       if (!player->key[RND_GATE_NR(element)])
14433         return MP_NO_ACTION;
14434     }
14435     else if (IS_RND_GATE_GRAY(element))
14436     {
14437       if (!player->key[RND_GATE_GRAY_NR(element)])
14438         return MP_NO_ACTION;
14439     }
14440     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14441     {
14442       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14443         return MP_NO_ACTION;
14444     }
14445     else if (element == EL_EXIT_OPEN ||
14446              element == EL_EM_EXIT_OPEN ||
14447              element == EL_EM_EXIT_OPENING ||
14448              element == EL_STEEL_EXIT_OPEN ||
14449              element == EL_EM_STEEL_EXIT_OPEN ||
14450              element == EL_EM_STEEL_EXIT_OPENING ||
14451              element == EL_SP_EXIT_OPEN ||
14452              element == EL_SP_EXIT_OPENING)
14453     {
14454       sound_action = ACTION_PASSING;    // player is passing exit
14455     }
14456     else if (element == EL_EMPTY)
14457     {
14458       sound_action = ACTION_MOVING;             // nothing to walk on
14459     }
14460
14461     // play sound from background or player, whatever is available
14462     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14463       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14464     else
14465       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14466   }
14467   else if (player_can_move &&
14468            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14469   {
14470     if (!ACCESS_FROM(element, opposite_direction))
14471       return MP_NO_ACTION;      // field not accessible from this direction
14472
14473     if (CAN_MOVE(element))      // only fixed elements can be passed!
14474       return MP_NO_ACTION;
14475
14476     if (IS_EM_GATE(element))
14477     {
14478       if (!player->key[EM_GATE_NR(element)])
14479         return MP_NO_ACTION;
14480     }
14481     else if (IS_EM_GATE_GRAY(element))
14482     {
14483       if (!player->key[EM_GATE_GRAY_NR(element)])
14484         return MP_NO_ACTION;
14485     }
14486     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14487     {
14488       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14489         return MP_NO_ACTION;
14490     }
14491     else if (IS_EMC_GATE(element))
14492     {
14493       if (!player->key[EMC_GATE_NR(element)])
14494         return MP_NO_ACTION;
14495     }
14496     else if (IS_EMC_GATE_GRAY(element))
14497     {
14498       if (!player->key[EMC_GATE_GRAY_NR(element)])
14499         return MP_NO_ACTION;
14500     }
14501     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14502     {
14503       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14504         return MP_NO_ACTION;
14505     }
14506     else if (element == EL_DC_GATE_WHITE ||
14507              element == EL_DC_GATE_WHITE_GRAY ||
14508              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14509     {
14510       if (player->num_white_keys == 0)
14511         return MP_NO_ACTION;
14512
14513       player->num_white_keys--;
14514     }
14515     else if (IS_SP_PORT(element))
14516     {
14517       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14518           element == EL_SP_GRAVITY_PORT_RIGHT ||
14519           element == EL_SP_GRAVITY_PORT_UP ||
14520           element == EL_SP_GRAVITY_PORT_DOWN)
14521         player->gravity = !player->gravity;
14522       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14523                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14524                element == EL_SP_GRAVITY_ON_PORT_UP ||
14525                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14526         player->gravity = TRUE;
14527       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14528                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14529                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14530                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14531         player->gravity = FALSE;
14532     }
14533
14534     // automatically move to the next field with double speed
14535     player->programmed_action = move_direction;
14536
14537     if (player->move_delay_reset_counter == 0)
14538     {
14539       player->move_delay_reset_counter = 2;     // two double speed steps
14540
14541       DOUBLE_PLAYER_SPEED(player);
14542     }
14543
14544     PlayLevelSoundAction(x, y, ACTION_PASSING);
14545   }
14546   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14547   {
14548     RemoveField(x, y);
14549
14550     if (mode != DF_SNAP)
14551     {
14552       GfxElement[x][y] = GFX_ELEMENT(element);
14553       player->is_digging = TRUE;
14554     }
14555
14556     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14557
14558     // use old behaviour for old levels (digging)
14559     if (!level.finish_dig_collect)
14560     {
14561       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14562                                           player->index_bit, dig_side);
14563
14564       // if digging triggered player relocation, finish digging tile
14565       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14566         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14567     }
14568
14569     if (mode == DF_SNAP)
14570     {
14571       if (level.block_snap_field)
14572         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14573       else
14574         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14575
14576       // use old behaviour for old levels (snapping)
14577       if (!level.finish_dig_collect)
14578         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14579                                             player->index_bit, dig_side);
14580     }
14581   }
14582   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14583   {
14584     RemoveField(x, y);
14585
14586     if (is_player && mode != DF_SNAP)
14587     {
14588       GfxElement[x][y] = element;
14589       player->is_collecting = TRUE;
14590     }
14591
14592     if (element == EL_SPEED_PILL)
14593     {
14594       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14595     }
14596     else if (element == EL_EXTRA_TIME && level.time > 0)
14597     {
14598       TimeLeft += level.extra_time;
14599
14600       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14601
14602       DisplayGameControlValues();
14603     }
14604     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14605     {
14606       int shield_time = (element == EL_SHIELD_DEADLY ?
14607                          level.shield_deadly_time :
14608                          level.shield_normal_time);
14609
14610       player->shield_normal_time_left += shield_time;
14611       if (element == EL_SHIELD_DEADLY)
14612         player->shield_deadly_time_left += shield_time;
14613     }
14614     else if (element == EL_DYNAMITE ||
14615              element == EL_EM_DYNAMITE ||
14616              element == EL_SP_DISK_RED)
14617     {
14618       if (player->inventory_size < MAX_INVENTORY_SIZE)
14619         player->inventory_element[player->inventory_size++] = element;
14620
14621       DrawGameDoorValues();
14622     }
14623     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14624     {
14625       player->dynabomb_count++;
14626       player->dynabombs_left++;
14627     }
14628     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14629     {
14630       player->dynabomb_size++;
14631     }
14632     else if (element == EL_DYNABOMB_INCREASE_POWER)
14633     {
14634       player->dynabomb_xl = TRUE;
14635     }
14636     else if (IS_KEY(element))
14637     {
14638       player->key[KEY_NR(element)] = TRUE;
14639
14640       DrawGameDoorValues();
14641     }
14642     else if (element == EL_DC_KEY_WHITE)
14643     {
14644       player->num_white_keys++;
14645
14646       // display white keys?
14647       // DrawGameDoorValues();
14648     }
14649     else if (IS_ENVELOPE(element))
14650     {
14651       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14652
14653       if (!wait_for_snapping)
14654         player->show_envelope = element;
14655     }
14656     else if (element == EL_EMC_LENSES)
14657     {
14658       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14659
14660       RedrawAllInvisibleElementsForLenses();
14661     }
14662     else if (element == EL_EMC_MAGNIFIER)
14663     {
14664       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14665
14666       RedrawAllInvisibleElementsForMagnifier();
14667     }
14668     else if (IS_DROPPABLE(element) ||
14669              IS_THROWABLE(element))     // can be collected and dropped
14670     {
14671       int i;
14672
14673       if (collect_count == 0)
14674         player->inventory_infinite_element = element;
14675       else
14676         for (i = 0; i < collect_count; i++)
14677           if (player->inventory_size < MAX_INVENTORY_SIZE)
14678             player->inventory_element[player->inventory_size++] = element;
14679
14680       DrawGameDoorValues();
14681     }
14682     else if (collect_count > 0)
14683     {
14684       game.gems_still_needed -= collect_count;
14685       if (game.gems_still_needed < 0)
14686         game.gems_still_needed = 0;
14687
14688       game.snapshot.collected_item = TRUE;
14689
14690       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14691
14692       DisplayGameControlValues();
14693     }
14694
14695     RaiseScoreElement(element);
14696     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14697
14698     // use old behaviour for old levels (collecting)
14699     if (!level.finish_dig_collect && is_player)
14700     {
14701       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14702                                           player->index_bit, dig_side);
14703
14704       // if collecting triggered player relocation, finish collecting tile
14705       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14706         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14707     }
14708
14709     if (mode == DF_SNAP)
14710     {
14711       if (level.block_snap_field)
14712         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14713       else
14714         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14715
14716       // use old behaviour for old levels (snapping)
14717       if (!level.finish_dig_collect)
14718         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14719                                             player->index_bit, dig_side);
14720     }
14721   }
14722   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14723   {
14724     if (mode == DF_SNAP && element != EL_BD_ROCK)
14725       return MP_NO_ACTION;
14726
14727     if (CAN_FALL(element) && dy)
14728       return MP_NO_ACTION;
14729
14730     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14731         !(element == EL_SPRING && level.use_spring_bug))
14732       return MP_NO_ACTION;
14733
14734     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14735         ((move_direction & MV_VERTICAL &&
14736           ((element_info[element].move_pattern & MV_LEFT &&
14737             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14738            (element_info[element].move_pattern & MV_RIGHT &&
14739             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14740          (move_direction & MV_HORIZONTAL &&
14741           ((element_info[element].move_pattern & MV_UP &&
14742             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14743            (element_info[element].move_pattern & MV_DOWN &&
14744             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14745       return MP_NO_ACTION;
14746
14747     // do not push elements already moving away faster than player
14748     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14749         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14750       return MP_NO_ACTION;
14751
14752     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14753     {
14754       if (player->push_delay_value == -1 || !player_was_pushing)
14755         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14756     }
14757     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14758     {
14759       if (player->push_delay_value == -1)
14760         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14761     }
14762     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14763     {
14764       if (!player->is_pushing)
14765         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14766     }
14767
14768     player->is_pushing = TRUE;
14769     player->is_active = TRUE;
14770
14771     if (!(IN_LEV_FIELD(nextx, nexty) &&
14772           (IS_FREE(nextx, nexty) ||
14773            (IS_SB_ELEMENT(element) &&
14774             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14775            (IS_CUSTOM_ELEMENT(element) &&
14776             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14777       return MP_NO_ACTION;
14778
14779     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14780       return MP_NO_ACTION;
14781
14782     if (player->push_delay == -1)       // new pushing; restart delay
14783       player->push_delay = 0;
14784
14785     if (player->push_delay < player->push_delay_value &&
14786         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14787         element != EL_SPRING && element != EL_BALLOON)
14788     {
14789       // make sure that there is no move delay before next try to push
14790       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14791         player->move_delay = 0;
14792
14793       return MP_NO_ACTION;
14794     }
14795
14796     if (IS_CUSTOM_ELEMENT(element) &&
14797         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14798     {
14799       if (!DigFieldByCE(nextx, nexty, element))
14800         return MP_NO_ACTION;
14801     }
14802
14803     if (IS_SB_ELEMENT(element))
14804     {
14805       boolean sokoban_task_solved = FALSE;
14806
14807       if (element == EL_SOKOBAN_FIELD_FULL)
14808       {
14809         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14810
14811         IncrementSokobanFieldsNeeded();
14812         IncrementSokobanObjectsNeeded();
14813       }
14814
14815       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14816       {
14817         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14818
14819         DecrementSokobanFieldsNeeded();
14820         DecrementSokobanObjectsNeeded();
14821
14822         // sokoban object was pushed from empty field to sokoban field
14823         if (Back[x][y] == EL_EMPTY)
14824           sokoban_task_solved = TRUE;
14825       }
14826
14827       Tile[x][y] = EL_SOKOBAN_OBJECT;
14828
14829       if (Back[x][y] == Back[nextx][nexty])
14830         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14831       else if (Back[x][y] != 0)
14832         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14833                                     ACTION_EMPTYING);
14834       else
14835         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14836                                     ACTION_FILLING);
14837
14838       if (sokoban_task_solved &&
14839           game.sokoban_fields_still_needed == 0 &&
14840           game.sokoban_objects_still_needed == 0 &&
14841           level.auto_exit_sokoban)
14842       {
14843         game.players_still_needed = 0;
14844
14845         LevelSolved();
14846
14847         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14848       }
14849     }
14850     else
14851       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14852
14853     InitMovingField(x, y, move_direction);
14854     GfxAction[x][y] = ACTION_PUSHING;
14855
14856     if (mode == DF_SNAP)
14857       ContinueMoving(x, y);
14858     else
14859       MovPos[x][y] = (dx != 0 ? dx : dy);
14860
14861     Pushed[x][y] = TRUE;
14862     Pushed[nextx][nexty] = TRUE;
14863
14864     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14865       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14866     else
14867       player->push_delay_value = -1;    // get new value later
14868
14869     // check for element change _after_ element has been pushed
14870     if (game.use_change_when_pushing_bug)
14871     {
14872       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14873                                  player->index_bit, dig_side);
14874       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14875                                           player->index_bit, dig_side);
14876     }
14877   }
14878   else if (IS_SWITCHABLE(element))
14879   {
14880     if (PLAYER_SWITCHING(player, x, y))
14881     {
14882       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14883                                           player->index_bit, dig_side);
14884
14885       return MP_ACTION;
14886     }
14887
14888     player->is_switching = TRUE;
14889     player->switch_x = x;
14890     player->switch_y = y;
14891
14892     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14893
14894     if (element == EL_ROBOT_WHEEL)
14895     {
14896       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14897
14898       game.robot_wheel_x = x;
14899       game.robot_wheel_y = y;
14900       game.robot_wheel_active = TRUE;
14901
14902       TEST_DrawLevelField(x, y);
14903     }
14904     else if (element == EL_SP_TERMINAL)
14905     {
14906       int xx, yy;
14907
14908       SCAN_PLAYFIELD(xx, yy)
14909       {
14910         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14911         {
14912           Bang(xx, yy);
14913         }
14914         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14915         {
14916           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14917
14918           ResetGfxAnimation(xx, yy);
14919           TEST_DrawLevelField(xx, yy);
14920         }
14921       }
14922     }
14923     else if (IS_BELT_SWITCH(element))
14924     {
14925       ToggleBeltSwitch(x, y);
14926     }
14927     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14928              element == EL_SWITCHGATE_SWITCH_DOWN ||
14929              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14930              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14931     {
14932       ToggleSwitchgateSwitch();
14933     }
14934     else if (element == EL_LIGHT_SWITCH ||
14935              element == EL_LIGHT_SWITCH_ACTIVE)
14936     {
14937       ToggleLightSwitch(x, y);
14938     }
14939     else if (element == EL_TIMEGATE_SWITCH ||
14940              element == EL_DC_TIMEGATE_SWITCH)
14941     {
14942       ActivateTimegateSwitch(x, y);
14943     }
14944     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14945              element == EL_BALLOON_SWITCH_RIGHT ||
14946              element == EL_BALLOON_SWITCH_UP    ||
14947              element == EL_BALLOON_SWITCH_DOWN  ||
14948              element == EL_BALLOON_SWITCH_NONE  ||
14949              element == EL_BALLOON_SWITCH_ANY)
14950     {
14951       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14952                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14953                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14954                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14955                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14956                              move_direction);
14957     }
14958     else if (element == EL_LAMP)
14959     {
14960       Tile[x][y] = EL_LAMP_ACTIVE;
14961       game.lights_still_needed--;
14962
14963       ResetGfxAnimation(x, y);
14964       TEST_DrawLevelField(x, y);
14965     }
14966     else if (element == EL_TIME_ORB_FULL)
14967     {
14968       Tile[x][y] = EL_TIME_ORB_EMPTY;
14969
14970       if (level.time > 0 || level.use_time_orb_bug)
14971       {
14972         TimeLeft += level.time_orb_time;
14973         game.no_level_time_limit = FALSE;
14974
14975         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14976
14977         DisplayGameControlValues();
14978       }
14979
14980       ResetGfxAnimation(x, y);
14981       TEST_DrawLevelField(x, y);
14982     }
14983     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14984              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14985     {
14986       int xx, yy;
14987
14988       game.ball_active = !game.ball_active;
14989
14990       SCAN_PLAYFIELD(xx, yy)
14991       {
14992         int e = Tile[xx][yy];
14993
14994         if (game.ball_active)
14995         {
14996           if (e == EL_EMC_MAGIC_BALL)
14997             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14998           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14999             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15000         }
15001         else
15002         {
15003           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15004             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15005           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15006             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15007         }
15008       }
15009     }
15010
15011     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15012                                         player->index_bit, dig_side);
15013
15014     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15015                                         player->index_bit, dig_side);
15016
15017     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15018                                         player->index_bit, dig_side);
15019
15020     return MP_ACTION;
15021   }
15022   else
15023   {
15024     if (!PLAYER_SWITCHING(player, x, y))
15025     {
15026       player->is_switching = TRUE;
15027       player->switch_x = x;
15028       player->switch_y = y;
15029
15030       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15031                                  player->index_bit, dig_side);
15032       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15033                                           player->index_bit, dig_side);
15034
15035       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15036                                  player->index_bit, dig_side);
15037       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15038                                           player->index_bit, dig_side);
15039     }
15040
15041     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15042                                player->index_bit, dig_side);
15043     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15044                                         player->index_bit, dig_side);
15045
15046     return MP_NO_ACTION;
15047   }
15048
15049   player->push_delay = -1;
15050
15051   if (is_player)                // function can also be called by EL_PENGUIN
15052   {
15053     if (Tile[x][y] != element)          // really digged/collected something
15054     {
15055       player->is_collecting = !player->is_digging;
15056       player->is_active = TRUE;
15057
15058       player->last_removed_element = element;
15059     }
15060   }
15061
15062   return MP_MOVING;
15063 }
15064
15065 static boolean DigFieldByCE(int x, int y, int digging_element)
15066 {
15067   int element = Tile[x][y];
15068
15069   if (!IS_FREE(x, y))
15070   {
15071     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15072                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15073                   ACTION_BREAKING);
15074
15075     // no element can dig solid indestructible elements
15076     if (IS_INDESTRUCTIBLE(element) &&
15077         !IS_DIGGABLE(element) &&
15078         !IS_COLLECTIBLE(element))
15079       return FALSE;
15080
15081     if (AmoebaNr[x][y] &&
15082         (element == EL_AMOEBA_FULL ||
15083          element == EL_BD_AMOEBA ||
15084          element == EL_AMOEBA_GROWING))
15085     {
15086       AmoebaCnt[AmoebaNr[x][y]]--;
15087       AmoebaCnt2[AmoebaNr[x][y]]--;
15088     }
15089
15090     if (IS_MOVING(x, y))
15091       RemoveMovingField(x, y);
15092     else
15093     {
15094       RemoveField(x, y);
15095       TEST_DrawLevelField(x, y);
15096     }
15097
15098     // if digged element was about to explode, prevent the explosion
15099     ExplodeField[x][y] = EX_TYPE_NONE;
15100
15101     PlayLevelSoundAction(x, y, action);
15102   }
15103
15104   Store[x][y] = EL_EMPTY;
15105
15106   // this makes it possible to leave the removed element again
15107   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15108     Store[x][y] = element;
15109
15110   return TRUE;
15111 }
15112
15113 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15114 {
15115   int jx = player->jx, jy = player->jy;
15116   int x = jx + dx, y = jy + dy;
15117   int snap_direction = (dx == -1 ? MV_LEFT  :
15118                         dx == +1 ? MV_RIGHT :
15119                         dy == -1 ? MV_UP    :
15120                         dy == +1 ? MV_DOWN  : MV_NONE);
15121   boolean can_continue_snapping = (level.continuous_snapping &&
15122                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15123
15124   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15125     return FALSE;
15126
15127   if (!player->active || !IN_LEV_FIELD(x, y))
15128     return FALSE;
15129
15130   if (dx && dy)
15131     return FALSE;
15132
15133   if (!dx && !dy)
15134   {
15135     if (player->MovPos == 0)
15136       player->is_pushing = FALSE;
15137
15138     player->is_snapping = FALSE;
15139
15140     if (player->MovPos == 0)
15141     {
15142       player->is_moving = FALSE;
15143       player->is_digging = FALSE;
15144       player->is_collecting = FALSE;
15145     }
15146
15147     return FALSE;
15148   }
15149
15150   // prevent snapping with already pressed snap key when not allowed
15151   if (player->is_snapping && !can_continue_snapping)
15152     return FALSE;
15153
15154   player->MovDir = snap_direction;
15155
15156   if (player->MovPos == 0)
15157   {
15158     player->is_moving = FALSE;
15159     player->is_digging = FALSE;
15160     player->is_collecting = FALSE;
15161   }
15162
15163   player->is_dropping = FALSE;
15164   player->is_dropping_pressed = FALSE;
15165   player->drop_pressed_delay = 0;
15166
15167   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15168     return FALSE;
15169
15170   player->is_snapping = TRUE;
15171   player->is_active = TRUE;
15172
15173   if (player->MovPos == 0)
15174   {
15175     player->is_moving = FALSE;
15176     player->is_digging = FALSE;
15177     player->is_collecting = FALSE;
15178   }
15179
15180   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15181     TEST_DrawLevelField(player->last_jx, player->last_jy);
15182
15183   TEST_DrawLevelField(x, y);
15184
15185   return TRUE;
15186 }
15187
15188 static boolean DropElement(struct PlayerInfo *player)
15189 {
15190   int old_element, new_element;
15191   int dropx = player->jx, dropy = player->jy;
15192   int drop_direction = player->MovDir;
15193   int drop_side = drop_direction;
15194   int drop_element = get_next_dropped_element(player);
15195
15196   /* do not drop an element on top of another element; when holding drop key
15197      pressed without moving, dropped element must move away before the next
15198      element can be dropped (this is especially important if the next element
15199      is dynamite, which can be placed on background for historical reasons) */
15200   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15201     return MP_ACTION;
15202
15203   if (IS_THROWABLE(drop_element))
15204   {
15205     dropx += GET_DX_FROM_DIR(drop_direction);
15206     dropy += GET_DY_FROM_DIR(drop_direction);
15207
15208     if (!IN_LEV_FIELD(dropx, dropy))
15209       return FALSE;
15210   }
15211
15212   old_element = Tile[dropx][dropy];     // old element at dropping position
15213   new_element = drop_element;           // default: no change when dropping
15214
15215   // check if player is active, not moving and ready to drop
15216   if (!player->active || player->MovPos || player->drop_delay > 0)
15217     return FALSE;
15218
15219   // check if player has anything that can be dropped
15220   if (new_element == EL_UNDEFINED)
15221     return FALSE;
15222
15223   // only set if player has anything that can be dropped
15224   player->is_dropping_pressed = TRUE;
15225
15226   // check if drop key was pressed long enough for EM style dynamite
15227   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15228     return FALSE;
15229
15230   // check if anything can be dropped at the current position
15231   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15232     return FALSE;
15233
15234   // collected custom elements can only be dropped on empty fields
15235   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15236     return FALSE;
15237
15238   if (old_element != EL_EMPTY)
15239     Back[dropx][dropy] = old_element;   // store old element on this field
15240
15241   ResetGfxAnimation(dropx, dropy);
15242   ResetRandomAnimationValue(dropx, dropy);
15243
15244   if (player->inventory_size > 0 ||
15245       player->inventory_infinite_element != EL_UNDEFINED)
15246   {
15247     if (player->inventory_size > 0)
15248     {
15249       player->inventory_size--;
15250
15251       DrawGameDoorValues();
15252
15253       if (new_element == EL_DYNAMITE)
15254         new_element = EL_DYNAMITE_ACTIVE;
15255       else if (new_element == EL_EM_DYNAMITE)
15256         new_element = EL_EM_DYNAMITE_ACTIVE;
15257       else if (new_element == EL_SP_DISK_RED)
15258         new_element = EL_SP_DISK_RED_ACTIVE;
15259     }
15260
15261     Tile[dropx][dropy] = new_element;
15262
15263     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15264       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15265                           el2img(Tile[dropx][dropy]), 0);
15266
15267     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15268
15269     // needed if previous element just changed to "empty" in the last frame
15270     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15271
15272     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15273                                player->index_bit, drop_side);
15274     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15275                                         CE_PLAYER_DROPS_X,
15276                                         player->index_bit, drop_side);
15277
15278     TestIfElementTouchesCustomElement(dropx, dropy);
15279   }
15280   else          // player is dropping a dyna bomb
15281   {
15282     player->dynabombs_left--;
15283
15284     Tile[dropx][dropy] = new_element;
15285
15286     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15287       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15288                           el2img(Tile[dropx][dropy]), 0);
15289
15290     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15291   }
15292
15293   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15294     InitField_WithBug1(dropx, dropy, FALSE);
15295
15296   new_element = Tile[dropx][dropy];     // element might have changed
15297
15298   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15299       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15300   {
15301     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15302       MovDir[dropx][dropy] = drop_direction;
15303
15304     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15305
15306     // do not cause impact style collision by dropping elements that can fall
15307     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15308   }
15309
15310   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15311   player->is_dropping = TRUE;
15312
15313   player->drop_pressed_delay = 0;
15314   player->is_dropping_pressed = FALSE;
15315
15316   player->drop_x = dropx;
15317   player->drop_y = dropy;
15318
15319   return TRUE;
15320 }
15321
15322 // ----------------------------------------------------------------------------
15323 // game sound playing functions
15324 // ----------------------------------------------------------------------------
15325
15326 static int *loop_sound_frame = NULL;
15327 static int *loop_sound_volume = NULL;
15328
15329 void InitPlayLevelSound(void)
15330 {
15331   int num_sounds = getSoundListSize();
15332
15333   checked_free(loop_sound_frame);
15334   checked_free(loop_sound_volume);
15335
15336   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15337   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15338 }
15339
15340 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15341 {
15342   int sx = SCREENX(x), sy = SCREENY(y);
15343   int volume, stereo_position;
15344   int max_distance = 8;
15345   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15346
15347   if ((!setup.sound_simple && !is_loop_sound) ||
15348       (!setup.sound_loops && is_loop_sound))
15349     return;
15350
15351   if (!IN_LEV_FIELD(x, y) ||
15352       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15353       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15354     return;
15355
15356   volume = SOUND_MAX_VOLUME;
15357
15358   if (!IN_SCR_FIELD(sx, sy))
15359   {
15360     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15361     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15362
15363     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15364   }
15365
15366   stereo_position = (SOUND_MAX_LEFT +
15367                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15368                      (SCR_FIELDX + 2 * max_distance));
15369
15370   if (is_loop_sound)
15371   {
15372     /* This assures that quieter loop sounds do not overwrite louder ones,
15373        while restarting sound volume comparison with each new game frame. */
15374
15375     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15376       return;
15377
15378     loop_sound_volume[nr] = volume;
15379     loop_sound_frame[nr] = FrameCounter;
15380   }
15381
15382   PlaySoundExt(nr, volume, stereo_position, type);
15383 }
15384
15385 static void PlayLevelSound(int x, int y, int nr)
15386 {
15387   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15388 }
15389
15390 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15391 {
15392   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15393                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15394                  y < LEVELY(BY1) ? LEVELY(BY1) :
15395                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15396                  sound_action);
15397 }
15398
15399 static void PlayLevelSoundAction(int x, int y, int action)
15400 {
15401   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15402 }
15403
15404 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15405 {
15406   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15407
15408   if (sound_effect != SND_UNDEFINED)
15409     PlayLevelSound(x, y, sound_effect);
15410 }
15411
15412 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15413                                               int action)
15414 {
15415   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15416
15417   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15418     PlayLevelSound(x, y, sound_effect);
15419 }
15420
15421 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15422 {
15423   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15424
15425   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15426     PlayLevelSound(x, y, sound_effect);
15427 }
15428
15429 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15430 {
15431   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15432
15433   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15434     StopSound(sound_effect);
15435 }
15436
15437 static int getLevelMusicNr(void)
15438 {
15439   int level_pos = level_nr - leveldir_current->first_level;
15440
15441   if (levelset.music[level_nr] != MUS_UNDEFINED)
15442     return levelset.music[level_nr];            // from config file
15443   else
15444     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15445 }
15446
15447 static void FadeLevelSounds(void)
15448 {
15449   FadeSounds();
15450 }
15451
15452 static void FadeLevelMusic(void)
15453 {
15454   int music_nr = getLevelMusicNr();
15455   char *curr_music = getCurrentlyPlayingMusicFilename();
15456   char *next_music = getMusicInfoEntryFilename(music_nr);
15457
15458   if (!strEqual(curr_music, next_music))
15459     FadeMusic();
15460 }
15461
15462 void FadeLevelSoundsAndMusic(void)
15463 {
15464   FadeLevelSounds();
15465   FadeLevelMusic();
15466 }
15467
15468 static void PlayLevelMusic(void)
15469 {
15470   int music_nr = getLevelMusicNr();
15471   char *curr_music = getCurrentlyPlayingMusicFilename();
15472   char *next_music = getMusicInfoEntryFilename(music_nr);
15473
15474   if (!strEqual(curr_music, next_music))
15475     PlayMusicLoop(music_nr);
15476 }
15477
15478 static int getSoundAction_BD(int sample)
15479 {
15480   switch (sample)
15481   {
15482     case GD_S_STONE:
15483     case GD_S_NUT:
15484     case GD_S_DIRT_BALL:
15485     case GD_S_NITRO:
15486     case GD_S_FALLING_WALL:
15487       return ACTION_IMPACT;
15488
15489     case GD_S_NUT_CRACK:
15490       return ACTION_BREAKING;
15491
15492     case GD_S_EXPANDING_WALL:
15493     case GD_S_WALL_REAPPEAR:
15494     case GD_S_SLIME:
15495     case GD_S_LAVA:
15496     case GD_S_ACID_SPREAD:
15497       return ACTION_GROWING;
15498
15499     case GD_S_DIAMOND_COLLECT:
15500     case GD_S_SKELETON_COLLECT:
15501     case GD_S_PNEUMATIC_COLLECT:
15502     case GD_S_BOMB_COLLECT:
15503     case GD_S_CLOCK_COLLECT:
15504     case GD_S_SWEET_COLLECT:
15505     case GD_S_KEY_COLLECT:
15506     case GD_S_DIAMOND_KEY_COLLECT:
15507       return ACTION_COLLECTING;
15508
15509     case GD_S_BOMB_PLACE:
15510     case GD_S_REPLICATOR:
15511       return ACTION_DROPPING;
15512
15513     case GD_S_BLADDER_MOVE:
15514       return ACTION_MOVING;
15515
15516     case GD_S_BLADDER_SPENDER:
15517     case GD_S_BLADDER_CONVERT:
15518     case GD_S_GRAVITY_CHANGE:
15519       return ACTION_CHANGING;
15520
15521     case GD_S_BITER_EAT:
15522       return ACTION_EATING;
15523
15524     case GD_S_DOOR_OPEN:
15525     case GD_S_CRACK:
15526       return ACTION_OPENING;
15527
15528     case GD_S_WALK_EARTH:
15529       return ACTION_DIGGING;
15530
15531     case GD_S_WALK_EMPTY:
15532       return ACTION_WALKING;
15533
15534     case GD_S_SWITCH_BITER:
15535     case GD_S_SWITCH_CREATURES:
15536     case GD_S_SWITCH_GRAVITY:
15537     case GD_S_SWITCH_EXPANDING:
15538     case GD_S_SWITCH_CONVEYOR:
15539     case GD_S_SWITCH_REPLICATOR:
15540     case GD_S_STIRRING:
15541       return ACTION_ACTIVATING;
15542
15543     case GD_S_BOX_PUSH:
15544       return ACTION_PUSHING;
15545
15546     case GD_S_TELEPORTER:
15547       return ACTION_PASSING;
15548
15549     case GD_S_EXPLOSION:
15550     case GD_S_BOMB_EXPLOSION:
15551     case GD_S_GHOST_EXPLOSION:
15552     case GD_S_VOODOO_EXPLOSION:
15553     case GD_S_NITRO_EXPLOSION:
15554       return ACTION_EXPLODING;
15555
15556     case GD_S_COVER:
15557     case GD_S_AMOEBA:
15558     case GD_S_AMOEBA_MAGIC:
15559     case GD_S_MAGIC_WALL:
15560     case GD_S_PNEUMATIC_HAMMER:
15561     case GD_S_WATER:
15562       return ACTION_ACTIVE;
15563
15564     case GD_S_DIAMOND_RANDOM:
15565     case GD_S_DIAMOND_1:
15566     case GD_S_DIAMOND_2:
15567     case GD_S_DIAMOND_3:
15568     case GD_S_DIAMOND_4:
15569     case GD_S_DIAMOND_5:
15570     case GD_S_DIAMOND_6:
15571     case GD_S_DIAMOND_7:
15572     case GD_S_DIAMOND_8:
15573     case GD_S_TIMEOUT_0:
15574     case GD_S_TIMEOUT_1:
15575     case GD_S_TIMEOUT_2:
15576     case GD_S_TIMEOUT_3:
15577     case GD_S_TIMEOUT_4:
15578     case GD_S_TIMEOUT_5:
15579     case GD_S_TIMEOUT_6:
15580     case GD_S_TIMEOUT_7:
15581     case GD_S_TIMEOUT_8:
15582     case GD_S_TIMEOUT_9:
15583     case GD_S_TIMEOUT_10:
15584     case GD_S_BONUS_LIFE:
15585       // kludge to prevent playing as loop sound
15586       return ACTION_OTHER;
15587
15588     case GD_S_FINISHED:
15589       return ACTION_DEFAULT;
15590
15591     default:
15592       return ACTION_DEFAULT;
15593   }
15594 }
15595
15596 static int getSoundEffect_BD(int element_bd, int sample)
15597 {
15598   int sound_action = getSoundAction_BD(sample);
15599   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15600   int nr;
15601
15602   // standard sounds
15603   if (sound_action != ACTION_OTHER &&
15604       sound_action != ACTION_DEFAULT)
15605     return sound_effect;
15606
15607   // special sounds
15608   switch (sample)
15609   {
15610     case GD_S_DIAMOND_RANDOM:
15611       nr = GetSimpleRandom(8);
15612       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15613       break;
15614
15615     case GD_S_DIAMOND_1:
15616     case GD_S_DIAMOND_2:
15617     case GD_S_DIAMOND_3:
15618     case GD_S_DIAMOND_4:
15619     case GD_S_DIAMOND_5:
15620     case GD_S_DIAMOND_6:
15621     case GD_S_DIAMOND_7:
15622     case GD_S_DIAMOND_8:
15623       nr = sample - GD_S_DIAMOND_1;
15624       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15625       break;
15626
15627     case GD_S_TIMEOUT_0:
15628     case GD_S_TIMEOUT_1:
15629     case GD_S_TIMEOUT_2:
15630     case GD_S_TIMEOUT_3:
15631     case GD_S_TIMEOUT_4:
15632     case GD_S_TIMEOUT_5:
15633     case GD_S_TIMEOUT_6:
15634     case GD_S_TIMEOUT_7:
15635     case GD_S_TIMEOUT_8:
15636     case GD_S_TIMEOUT_9:
15637     case GD_S_TIMEOUT_10:
15638       nr = sample - GD_S_TIMEOUT_0;
15639       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15640
15641       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15642         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15643       break;
15644
15645     case GD_S_FINISHED:
15646       sound_effect = SND_GAME_LEVELTIME_BONUS;
15647       break;
15648
15649     case GD_S_BONUS_LIFE:
15650       sound_effect = SND_GAME_HEALTH_BONUS;
15651       break;
15652
15653     default:
15654       sound_effect = SND_UNDEFINED;
15655       break;
15656   }
15657
15658   return sound_effect;
15659 }
15660
15661 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15662 {
15663   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15664   int sound_effect = getSoundEffect_BD(element, sample);
15665   int sound_action = getSoundAction_BD(sample);
15666   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15667   int offset = 0;
15668   int x = xx - offset;
15669   int y = yy - offset;
15670
15671   if (sound_action == ACTION_OTHER)
15672     is_loop_sound = FALSE;
15673
15674   if (sound_effect != SND_UNDEFINED)
15675     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15676 }
15677
15678 void StopSound_BD(int element_bd, int sample)
15679 {
15680   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15681   int sound_effect = getSoundEffect_BD(element, sample);
15682
15683   if (sound_effect != SND_UNDEFINED)
15684     StopSound(sound_effect);
15685 }
15686
15687 boolean isSoundPlaying_BD(int element_bd, int sample)
15688 {
15689   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15690   int sound_effect = getSoundEffect_BD(element, sample);
15691
15692   if (sound_effect != SND_UNDEFINED)
15693     return isSoundPlaying(sound_effect);
15694
15695   return FALSE;
15696 }
15697
15698 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15699 {
15700   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15701   int offset = 0;
15702   int x = xx - offset;
15703   int y = yy - offset;
15704
15705   switch (sample)
15706   {
15707     case SOUND_blank:
15708       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15709       break;
15710
15711     case SOUND_roll:
15712       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15713       break;
15714
15715     case SOUND_stone:
15716       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15717       break;
15718
15719     case SOUND_nut:
15720       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15721       break;
15722
15723     case SOUND_crack:
15724       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15725       break;
15726
15727     case SOUND_bug:
15728       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15729       break;
15730
15731     case SOUND_tank:
15732       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15733       break;
15734
15735     case SOUND_android_clone:
15736       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15737       break;
15738
15739     case SOUND_android_move:
15740       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15741       break;
15742
15743     case SOUND_spring:
15744       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15745       break;
15746
15747     case SOUND_slurp:
15748       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15749       break;
15750
15751     case SOUND_eater:
15752       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15753       break;
15754
15755     case SOUND_eater_eat:
15756       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15757       break;
15758
15759     case SOUND_alien:
15760       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15761       break;
15762
15763     case SOUND_collect:
15764       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15765       break;
15766
15767     case SOUND_diamond:
15768       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15769       break;
15770
15771     case SOUND_squash:
15772       // !!! CHECK THIS !!!
15773 #if 1
15774       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15775 #else
15776       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15777 #endif
15778       break;
15779
15780     case SOUND_wonderfall:
15781       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15782       break;
15783
15784     case SOUND_drip:
15785       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15786       break;
15787
15788     case SOUND_push:
15789       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15790       break;
15791
15792     case SOUND_dirt:
15793       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15794       break;
15795
15796     case SOUND_acid:
15797       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15798       break;
15799
15800     case SOUND_ball:
15801       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15802       break;
15803
15804     case SOUND_slide:
15805       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15806       break;
15807
15808     case SOUND_wonder:
15809       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15810       break;
15811
15812     case SOUND_door:
15813       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15814       break;
15815
15816     case SOUND_exit_open:
15817       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15818       break;
15819
15820     case SOUND_exit_leave:
15821       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15822       break;
15823
15824     case SOUND_dynamite:
15825       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15826       break;
15827
15828     case SOUND_tick:
15829       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15830       break;
15831
15832     case SOUND_press:
15833       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15834       break;
15835
15836     case SOUND_wheel:
15837       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15838       break;
15839
15840     case SOUND_boom:
15841       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15842       break;
15843
15844     case SOUND_die:
15845       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15846       break;
15847
15848     case SOUND_time:
15849       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15850       break;
15851
15852     default:
15853       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15854       break;
15855   }
15856 }
15857
15858 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15859 {
15860   int element = map_element_SP_to_RND(element_sp);
15861   int action = map_action_SP_to_RND(action_sp);
15862   int offset = (setup.sp_show_border_elements ? 0 : 1);
15863   int x = xx - offset;
15864   int y = yy - offset;
15865
15866   PlayLevelSoundElementAction(x, y, element, action);
15867 }
15868
15869 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15870 {
15871   int element = map_element_MM_to_RND(element_mm);
15872   int action = map_action_MM_to_RND(action_mm);
15873   int offset = 0;
15874   int x = xx - offset;
15875   int y = yy - offset;
15876
15877   if (!IS_MM_ELEMENT(element))
15878     element = EL_MM_DEFAULT;
15879
15880   PlayLevelSoundElementAction(x, y, element, action);
15881 }
15882
15883 void PlaySound_MM(int sound_mm)
15884 {
15885   int sound = map_sound_MM_to_RND(sound_mm);
15886
15887   if (sound == SND_UNDEFINED)
15888     return;
15889
15890   PlaySound(sound);
15891 }
15892
15893 void PlaySoundLoop_MM(int sound_mm)
15894 {
15895   int sound = map_sound_MM_to_RND(sound_mm);
15896
15897   if (sound == SND_UNDEFINED)
15898     return;
15899
15900   PlaySoundLoop(sound);
15901 }
15902
15903 void StopSound_MM(int sound_mm)
15904 {
15905   int sound = map_sound_MM_to_RND(sound_mm);
15906
15907   if (sound == SND_UNDEFINED)
15908     return;
15909
15910   StopSound(sound);
15911 }
15912
15913 void RaiseScore(int value)
15914 {
15915   game.score += value;
15916
15917   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15918
15919   DisplayGameControlValues();
15920 }
15921
15922 void RaiseScoreElement(int element)
15923 {
15924   switch (element)
15925   {
15926     case EL_EMERALD:
15927     case EL_BD_DIAMOND:
15928     case EL_EMERALD_YELLOW:
15929     case EL_EMERALD_RED:
15930     case EL_EMERALD_PURPLE:
15931     case EL_SP_INFOTRON:
15932       RaiseScore(level.score[SC_EMERALD]);
15933       break;
15934     case EL_DIAMOND:
15935       RaiseScore(level.score[SC_DIAMOND]);
15936       break;
15937     case EL_CRYSTAL:
15938       RaiseScore(level.score[SC_CRYSTAL]);
15939       break;
15940     case EL_PEARL:
15941       RaiseScore(level.score[SC_PEARL]);
15942       break;
15943     case EL_BUG:
15944     case EL_BD_BUTTERFLY:
15945     case EL_SP_ELECTRON:
15946       RaiseScore(level.score[SC_BUG]);
15947       break;
15948     case EL_SPACESHIP:
15949     case EL_BD_FIREFLY:
15950     case EL_SP_SNIKSNAK:
15951       RaiseScore(level.score[SC_SPACESHIP]);
15952       break;
15953     case EL_YAMYAM:
15954     case EL_DARK_YAMYAM:
15955       RaiseScore(level.score[SC_YAMYAM]);
15956       break;
15957     case EL_ROBOT:
15958       RaiseScore(level.score[SC_ROBOT]);
15959       break;
15960     case EL_PACMAN:
15961       RaiseScore(level.score[SC_PACMAN]);
15962       break;
15963     case EL_NUT:
15964       RaiseScore(level.score[SC_NUT]);
15965       break;
15966     case EL_DYNAMITE:
15967     case EL_EM_DYNAMITE:
15968     case EL_SP_DISK_RED:
15969     case EL_DYNABOMB_INCREASE_NUMBER:
15970     case EL_DYNABOMB_INCREASE_SIZE:
15971     case EL_DYNABOMB_INCREASE_POWER:
15972       RaiseScore(level.score[SC_DYNAMITE]);
15973       break;
15974     case EL_SHIELD_NORMAL:
15975     case EL_SHIELD_DEADLY:
15976       RaiseScore(level.score[SC_SHIELD]);
15977       break;
15978     case EL_EXTRA_TIME:
15979       RaiseScore(level.extra_time_score);
15980       break;
15981     case EL_KEY_1:
15982     case EL_KEY_2:
15983     case EL_KEY_3:
15984     case EL_KEY_4:
15985     case EL_EM_KEY_1:
15986     case EL_EM_KEY_2:
15987     case EL_EM_KEY_3:
15988     case EL_EM_KEY_4:
15989     case EL_EMC_KEY_5:
15990     case EL_EMC_KEY_6:
15991     case EL_EMC_KEY_7:
15992     case EL_EMC_KEY_8:
15993     case EL_DC_KEY_WHITE:
15994       RaiseScore(level.score[SC_KEY]);
15995       break;
15996     default:
15997       RaiseScore(element_info[element].collect_score);
15998       break;
15999   }
16000 }
16001
16002 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16003 {
16004   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16005   {
16006     if (!quick_quit)
16007     {
16008       // prevent short reactivation of overlay buttons while closing door
16009       SetOverlayActive(FALSE);
16010       UnmapGameButtons();
16011
16012       // door may still be open due to skipped or envelope style request
16013       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16014     }
16015
16016     if (network.enabled)
16017     {
16018       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16019     }
16020     else
16021     {
16022       // when using BD game engine, cover screen before fading out
16023       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16024         game_bd.cover_screen = TRUE;
16025
16026       if (quick_quit)
16027         FadeSkipNextFadeIn();
16028
16029       SetGameStatus(GAME_MODE_MAIN);
16030
16031       DrawMainMenu();
16032     }
16033   }
16034   else          // continue playing the game
16035   {
16036     if (tape.playing && tape.deactivate_display)
16037       TapeDeactivateDisplayOff(TRUE);
16038
16039     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16040
16041     if (tape.playing && tape.deactivate_display)
16042       TapeDeactivateDisplayOn();
16043   }
16044 }
16045
16046 void RequestQuitGame(boolean escape_key_pressed)
16047 {
16048   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16049   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16050                         level_editor_test_game);
16051   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16052                           quick_quit || score_info_tape_play);
16053
16054   RequestQuitGameExt(skip_request, quick_quit,
16055                      "Do you really want to quit the game?");
16056 }
16057
16058 static char *getRestartGameMessage(void)
16059 {
16060   boolean play_again = hasStartedNetworkGame();
16061   static char message[MAX_OUTPUT_LINESIZE];
16062   char *game_over_text = "Game over!";
16063   char *play_again_text = " Play it again?";
16064
16065   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16066       game_mm.game_over_message != NULL)
16067     game_over_text = game_mm.game_over_message;
16068
16069   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16070            (play_again ? play_again_text : ""));
16071
16072   return message;
16073 }
16074
16075 static void RequestRestartGame(void)
16076 {
16077   char *message = getRestartGameMessage();
16078   boolean has_started_game = hasStartedNetworkGame();
16079   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16080   int door_state = DOOR_CLOSE_1;
16081
16082   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
16083   {
16084     CloseDoor(door_state);
16085
16086     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16087   }
16088   else
16089   {
16090     // if game was invoked from level editor, also close tape recorder door
16091     if (level_editor_test_game)
16092       door_state = DOOR_CLOSE_ALL;
16093
16094     CloseDoor(door_state);
16095
16096     SetGameStatus(GAME_MODE_MAIN);
16097
16098     DrawMainMenu();
16099   }
16100 }
16101
16102 boolean CheckRestartGame(void)
16103 {
16104   static int game_over_delay = 0;
16105   int game_over_delay_value = 50;
16106   boolean game_over = checkGameFailed();
16107
16108   if (!game_over)
16109   {
16110     game_over_delay = game_over_delay_value;
16111
16112     return FALSE;
16113   }
16114
16115   if (game_over_delay > 0)
16116   {
16117     if (game_over_delay == game_over_delay_value / 2)
16118       PlaySound(SND_GAME_LOSING);
16119
16120     game_over_delay--;
16121
16122     return FALSE;
16123   }
16124
16125   // do not ask to play again if request dialog is already active
16126   if (game.request_active)
16127     return FALSE;
16128
16129   // do not ask to play again if request dialog already handled
16130   if (game.RestartGameRequested)
16131     return FALSE;
16132
16133   // do not ask to play again if game was never actually played
16134   if (!game.GamePlayed)
16135     return FALSE;
16136
16137   // do not ask to play again if this was disabled in setup menu
16138   if (!setup.ask_on_game_over)
16139     return FALSE;
16140
16141   game.RestartGameRequested = TRUE;
16142
16143   RequestRestartGame();
16144
16145   return TRUE;
16146 }
16147
16148 boolean checkGameRunning(void)
16149 {
16150   if (game_status != GAME_MODE_PLAYING)
16151     return FALSE;
16152
16153   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16154     return FALSE;
16155
16156   return TRUE;
16157 }
16158
16159 boolean checkGameSolved(void)
16160 {
16161   // set for all game engines if level was solved
16162   return game.LevelSolved_GameEnd;
16163 }
16164
16165 boolean checkGameFailed(void)
16166 {
16167   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16168     return (game_bd.game_over && !game_bd.level_solved);
16169   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16170     return (game_em.game_over && !game_em.level_solved);
16171   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16172     return (game_sp.game_over && !game_sp.level_solved);
16173   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16174     return (game_mm.game_over && !game_mm.level_solved);
16175   else                          // GAME_ENGINE_TYPE_RND
16176     return (game.GameOver && !game.LevelSolved);
16177 }
16178
16179 boolean checkGameEnded(void)
16180 {
16181   return (checkGameSolved() || checkGameFailed());
16182 }
16183
16184
16185 // ----------------------------------------------------------------------------
16186 // random generator functions
16187 // ----------------------------------------------------------------------------
16188
16189 unsigned int InitEngineRandom_RND(int seed)
16190 {
16191   game.num_random_calls = 0;
16192
16193   return InitEngineRandom(seed);
16194 }
16195
16196 unsigned int RND(int max)
16197 {
16198   if (max > 0)
16199   {
16200     game.num_random_calls++;
16201
16202     return GetEngineRandom(max);
16203   }
16204
16205   return 0;
16206 }
16207
16208
16209 // ----------------------------------------------------------------------------
16210 // game engine snapshot handling functions
16211 // ----------------------------------------------------------------------------
16212
16213 struct EngineSnapshotInfo
16214 {
16215   // runtime values for custom element collect score
16216   int collect_score[NUM_CUSTOM_ELEMENTS];
16217
16218   // runtime values for group element choice position
16219   int choice_pos[NUM_GROUP_ELEMENTS];
16220
16221   // runtime values for belt position animations
16222   int belt_graphic[4][NUM_BELT_PARTS];
16223   int belt_anim_mode[4][NUM_BELT_PARTS];
16224 };
16225
16226 static struct EngineSnapshotInfo engine_snapshot_rnd;
16227 static char *snapshot_level_identifier = NULL;
16228 static int snapshot_level_nr = -1;
16229
16230 static void SaveEngineSnapshotValues_RND(void)
16231 {
16232   static int belt_base_active_element[4] =
16233   {
16234     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16235     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16236     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16237     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16238   };
16239   int i, j;
16240
16241   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16242   {
16243     int element = EL_CUSTOM_START + i;
16244
16245     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16246   }
16247
16248   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16249   {
16250     int element = EL_GROUP_START + i;
16251
16252     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16253   }
16254
16255   for (i = 0; i < 4; i++)
16256   {
16257     for (j = 0; j < NUM_BELT_PARTS; j++)
16258     {
16259       int element = belt_base_active_element[i] + j;
16260       int graphic = el2img(element);
16261       int anim_mode = graphic_info[graphic].anim_mode;
16262
16263       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16264       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16265     }
16266   }
16267 }
16268
16269 static void LoadEngineSnapshotValues_RND(void)
16270 {
16271   unsigned int num_random_calls = game.num_random_calls;
16272   int i, j;
16273
16274   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16275   {
16276     int element = EL_CUSTOM_START + i;
16277
16278     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16279   }
16280
16281   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16282   {
16283     int element = EL_GROUP_START + i;
16284
16285     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16286   }
16287
16288   for (i = 0; i < 4; i++)
16289   {
16290     for (j = 0; j < NUM_BELT_PARTS; j++)
16291     {
16292       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16293       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16294
16295       graphic_info[graphic].anim_mode = anim_mode;
16296     }
16297   }
16298
16299   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16300   {
16301     InitRND(tape.random_seed);
16302     for (i = 0; i < num_random_calls; i++)
16303       RND(1);
16304   }
16305
16306   if (game.num_random_calls != num_random_calls)
16307   {
16308     Error("number of random calls out of sync");
16309     Error("number of random calls should be %d", num_random_calls);
16310     Error("number of random calls is %d", game.num_random_calls);
16311
16312     Fail("this should not happen -- please debug");
16313   }
16314 }
16315
16316 void FreeEngineSnapshotSingle(void)
16317 {
16318   FreeSnapshotSingle();
16319
16320   setString(&snapshot_level_identifier, NULL);
16321   snapshot_level_nr = -1;
16322 }
16323
16324 void FreeEngineSnapshotList(void)
16325 {
16326   FreeSnapshotList();
16327 }
16328
16329 static ListNode *SaveEngineSnapshotBuffers(void)
16330 {
16331   ListNode *buffers = NULL;
16332
16333   // copy some special values to a structure better suited for the snapshot
16334
16335   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16336     SaveEngineSnapshotValues_RND();
16337   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16338     SaveEngineSnapshotValues_EM();
16339   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16340     SaveEngineSnapshotValues_SP(&buffers);
16341   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16342     SaveEngineSnapshotValues_MM();
16343
16344   // save values stored in special snapshot structure
16345
16346   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16347     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16348   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16349     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16350   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16351     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16352   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16353     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16354
16355   // save further RND engine values
16356
16357   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16358   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16359   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16360
16361   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16362   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16363   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16364   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16365   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16366   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16367
16368   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16369   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16370   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16371
16372   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16373
16374   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16375   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16376
16377   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16378   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16379   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16380   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16381   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16382   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16383   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16384   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16385   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16386   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16387   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16388   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16389   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16390   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16391   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16392   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16393   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16394   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16395
16396   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16397   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16398
16399   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16400   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16401   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16402
16403   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16404   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16405
16406   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16407   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16408   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16409   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16410   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16411   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16412
16413   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16414   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16415
16416 #if 0
16417   ListNode *node = engine_snapshot_list_rnd;
16418   int num_bytes = 0;
16419
16420   while (node != NULL)
16421   {
16422     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16423
16424     node = node->next;
16425   }
16426
16427   Debug("game:playing:SaveEngineSnapshotBuffers",
16428         "size of engine snapshot: %d bytes", num_bytes);
16429 #endif
16430
16431   return buffers;
16432 }
16433
16434 void SaveEngineSnapshotSingle(void)
16435 {
16436   ListNode *buffers = SaveEngineSnapshotBuffers();
16437
16438   // finally save all snapshot buffers to single snapshot
16439   SaveSnapshotSingle(buffers);
16440
16441   // save level identification information
16442   setString(&snapshot_level_identifier, leveldir_current->identifier);
16443   snapshot_level_nr = level_nr;
16444 }
16445
16446 boolean CheckSaveEngineSnapshotToList(void)
16447 {
16448   boolean save_snapshot =
16449     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16450      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16451       game.snapshot.changed_action) ||
16452      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16453       game.snapshot.collected_item));
16454
16455   game.snapshot.changed_action = FALSE;
16456   game.snapshot.collected_item = FALSE;
16457   game.snapshot.save_snapshot = save_snapshot;
16458
16459   return save_snapshot;
16460 }
16461
16462 void SaveEngineSnapshotToList(void)
16463 {
16464   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16465       tape.quick_resume)
16466     return;
16467
16468   ListNode *buffers = SaveEngineSnapshotBuffers();
16469
16470   // finally save all snapshot buffers to snapshot list
16471   SaveSnapshotToList(buffers);
16472 }
16473
16474 void SaveEngineSnapshotToListInitial(void)
16475 {
16476   FreeEngineSnapshotList();
16477
16478   SaveEngineSnapshotToList();
16479 }
16480
16481 static void LoadEngineSnapshotValues(void)
16482 {
16483   // restore special values from snapshot structure
16484
16485   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16486     LoadEngineSnapshotValues_RND();
16487   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16488     LoadEngineSnapshotValues_EM();
16489   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16490     LoadEngineSnapshotValues_SP();
16491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16492     LoadEngineSnapshotValues_MM();
16493 }
16494
16495 void LoadEngineSnapshotSingle(void)
16496 {
16497   LoadSnapshotSingle();
16498
16499   LoadEngineSnapshotValues();
16500 }
16501
16502 static void LoadEngineSnapshot_Undo(int steps)
16503 {
16504   LoadSnapshotFromList_Older(steps);
16505
16506   LoadEngineSnapshotValues();
16507 }
16508
16509 static void LoadEngineSnapshot_Redo(int steps)
16510 {
16511   LoadSnapshotFromList_Newer(steps);
16512
16513   LoadEngineSnapshotValues();
16514 }
16515
16516 boolean CheckEngineSnapshotSingle(void)
16517 {
16518   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16519           snapshot_level_nr == level_nr);
16520 }
16521
16522 boolean CheckEngineSnapshotList(void)
16523 {
16524   return CheckSnapshotList();
16525 }
16526
16527
16528 // ---------- new game button stuff -------------------------------------------
16529
16530 static struct
16531 {
16532   int graphic;
16533   struct XY *pos;
16534   int gadget_id;
16535   boolean *setup_value;
16536   boolean allowed_on_tape;
16537   boolean is_touch_button;
16538   char *infotext;
16539 } gamebutton_info[NUM_GAME_BUTTONS] =
16540 {
16541   {
16542     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16543     GAME_CTRL_ID_STOP,                          NULL,
16544     TRUE, FALSE,                                "stop game"
16545   },
16546   {
16547     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16548     GAME_CTRL_ID_PAUSE,                         NULL,
16549     TRUE, FALSE,                                "pause game"
16550   },
16551   {
16552     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16553     GAME_CTRL_ID_PLAY,                          NULL,
16554     TRUE, FALSE,                                "play game"
16555   },
16556   {
16557     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16558     GAME_CTRL_ID_UNDO,                          NULL,
16559     TRUE, FALSE,                                "undo step"
16560   },
16561   {
16562     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16563     GAME_CTRL_ID_REDO,                          NULL,
16564     TRUE, FALSE,                                "redo step"
16565   },
16566   {
16567     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16568     GAME_CTRL_ID_SAVE,                          NULL,
16569     TRUE, FALSE,                                "save game"
16570   },
16571   {
16572     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16573     GAME_CTRL_ID_PAUSE2,                        NULL,
16574     TRUE, FALSE,                                "pause game"
16575   },
16576   {
16577     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16578     GAME_CTRL_ID_LOAD,                          NULL,
16579     TRUE, FALSE,                                "load game"
16580   },
16581   {
16582     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16583     GAME_CTRL_ID_RESTART,                       NULL,
16584     TRUE, FALSE,                                "restart game"
16585   },
16586   {
16587     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16588     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16589     FALSE, FALSE,                               "stop game"
16590   },
16591   {
16592     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16593     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16594     FALSE, FALSE,                               "pause game"
16595   },
16596   {
16597     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16598     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16599     FALSE, FALSE,                               "play game"
16600   },
16601   {
16602     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16603     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16604     FALSE, FALSE,                               "restart game"
16605   },
16606   {
16607     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16608     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16609     FALSE, TRUE,                                "stop game"
16610   },
16611   {
16612     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16613     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16614     FALSE, TRUE,                                "pause game"
16615   },
16616   {
16617     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16618     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16619     FALSE, TRUE,                                "restart game"
16620   },
16621   {
16622     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16623     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16624     TRUE, FALSE,                                "background music on/off"
16625   },
16626   {
16627     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16628     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16629     TRUE, FALSE,                                "sound loops on/off"
16630   },
16631   {
16632     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16633     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16634     TRUE, FALSE,                                "normal sounds on/off"
16635   },
16636   {
16637     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16638     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16639     FALSE, FALSE,                               "background music on/off"
16640   },
16641   {
16642     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16643     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16644     FALSE, FALSE,                               "sound loops on/off"
16645   },
16646   {
16647     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16648     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16649     FALSE, FALSE,                               "normal sounds on/off"
16650   }
16651 };
16652
16653 void CreateGameButtons(void)
16654 {
16655   int i;
16656
16657   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16658   {
16659     int graphic = gamebutton_info[i].graphic;
16660     struct GraphicInfo *gfx = &graphic_info[graphic];
16661     struct XY *pos = gamebutton_info[i].pos;
16662     struct GadgetInfo *gi;
16663     int button_type;
16664     boolean checked;
16665     unsigned int event_mask;
16666     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16667     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16668     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16669     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16670     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16671     int gd_x   = gfx->src_x;
16672     int gd_y   = gfx->src_y;
16673     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16674     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16675     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16676     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16677     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16678     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16679     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16680     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16681     int id = i;
16682
16683     // do not use touch buttons if overlay touch buttons are disabled
16684     if (is_touch_button && !setup.touch.overlay_buttons)
16685       continue;
16686
16687     if (gfx->bitmap == NULL)
16688     {
16689       game_gadget[id] = NULL;
16690
16691       continue;
16692     }
16693
16694     if (id == GAME_CTRL_ID_STOP ||
16695         id == GAME_CTRL_ID_PANEL_STOP ||
16696         id == GAME_CTRL_ID_TOUCH_STOP ||
16697         id == GAME_CTRL_ID_PLAY ||
16698         id == GAME_CTRL_ID_PANEL_PLAY ||
16699         id == GAME_CTRL_ID_SAVE ||
16700         id == GAME_CTRL_ID_LOAD ||
16701         id == GAME_CTRL_ID_RESTART ||
16702         id == GAME_CTRL_ID_PANEL_RESTART ||
16703         id == GAME_CTRL_ID_TOUCH_RESTART)
16704     {
16705       button_type = GD_TYPE_NORMAL_BUTTON;
16706       checked = FALSE;
16707       event_mask = GD_EVENT_RELEASED;
16708     }
16709     else if (id == GAME_CTRL_ID_UNDO ||
16710              id == GAME_CTRL_ID_REDO)
16711     {
16712       button_type = GD_TYPE_NORMAL_BUTTON;
16713       checked = FALSE;
16714       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16715     }
16716     else
16717     {
16718       button_type = GD_TYPE_CHECK_BUTTON;
16719       checked = (gamebutton_info[i].setup_value != NULL ?
16720                  *gamebutton_info[i].setup_value : FALSE);
16721       event_mask = GD_EVENT_PRESSED;
16722     }
16723
16724     gi = CreateGadget(GDI_CUSTOM_ID, id,
16725                       GDI_IMAGE_ID, graphic,
16726                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16727                       GDI_X, base_x + x,
16728                       GDI_Y, base_y + y,
16729                       GDI_WIDTH, gfx->width,
16730                       GDI_HEIGHT, gfx->height,
16731                       GDI_TYPE, button_type,
16732                       GDI_STATE, GD_BUTTON_UNPRESSED,
16733                       GDI_CHECKED, checked,
16734                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16735                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16736                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16737                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16738                       GDI_DIRECT_DRAW, FALSE,
16739                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16740                       GDI_EVENT_MASK, event_mask,
16741                       GDI_CALLBACK_ACTION, HandleGameButtons,
16742                       GDI_END);
16743
16744     if (gi == NULL)
16745       Fail("cannot create gadget");
16746
16747     game_gadget[id] = gi;
16748   }
16749 }
16750
16751 void FreeGameButtons(void)
16752 {
16753   int i;
16754
16755   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16756     FreeGadget(game_gadget[i]);
16757 }
16758
16759 static void UnmapGameButtonsAtSamePosition(int id)
16760 {
16761   int i;
16762
16763   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16764     if (i != id &&
16765         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16766         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16767       UnmapGadget(game_gadget[i]);
16768 }
16769
16770 static void UnmapGameButtonsAtSamePosition_All(void)
16771 {
16772   if (setup.show_load_save_buttons)
16773   {
16774     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16775     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16776     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16777   }
16778   else if (setup.show_undo_redo_buttons)
16779   {
16780     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16781     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16782     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16783   }
16784   else
16785   {
16786     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16787     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16788     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16789
16790     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16791     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16792     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16793   }
16794 }
16795
16796 void MapLoadSaveButtons(void)
16797 {
16798   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16799   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16800
16801   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16802   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16803 }
16804
16805 void MapUndoRedoButtons(void)
16806 {
16807   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16808   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16809
16810   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16811   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16812 }
16813
16814 void ModifyPauseButtons(void)
16815 {
16816   static int ids[] =
16817   {
16818     GAME_CTRL_ID_PAUSE,
16819     GAME_CTRL_ID_PAUSE2,
16820     GAME_CTRL_ID_PANEL_PAUSE,
16821     GAME_CTRL_ID_TOUCH_PAUSE,
16822     -1
16823   };
16824   int i;
16825
16826   // do not redraw pause button on closed door (may happen when restarting game)
16827   if (!(GetDoorState() & DOOR_OPEN_1))
16828     return;
16829
16830   for (i = 0; ids[i] > -1; i++)
16831     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16832 }
16833
16834 static void MapGameButtonsExt(boolean on_tape)
16835 {
16836   int i;
16837
16838   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16839   {
16840     if ((i == GAME_CTRL_ID_UNDO ||
16841          i == GAME_CTRL_ID_REDO) &&
16842         game_status != GAME_MODE_PLAYING)
16843       continue;
16844
16845     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16846       MapGadget(game_gadget[i]);
16847   }
16848
16849   UnmapGameButtonsAtSamePosition_All();
16850
16851   RedrawGameButtons();
16852 }
16853
16854 static void UnmapGameButtonsExt(boolean on_tape)
16855 {
16856   int i;
16857
16858   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16859     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16860       UnmapGadget(game_gadget[i]);
16861 }
16862
16863 static void RedrawGameButtonsExt(boolean on_tape)
16864 {
16865   int i;
16866
16867   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16868     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16869       RedrawGadget(game_gadget[i]);
16870 }
16871
16872 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16873 {
16874   if (gi == NULL)
16875     return;
16876
16877   gi->checked = state;
16878 }
16879
16880 static void RedrawSoundButtonGadget(int id)
16881 {
16882   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16883              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16884              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16885              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16886              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16887              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16888              id);
16889
16890   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16891   RedrawGadget(game_gadget[id2]);
16892 }
16893
16894 void MapGameButtons(void)
16895 {
16896   MapGameButtonsExt(FALSE);
16897 }
16898
16899 void UnmapGameButtons(void)
16900 {
16901   UnmapGameButtonsExt(FALSE);
16902 }
16903
16904 void RedrawGameButtons(void)
16905 {
16906   RedrawGameButtonsExt(FALSE);
16907 }
16908
16909 void MapGameButtonsOnTape(void)
16910 {
16911   MapGameButtonsExt(TRUE);
16912 }
16913
16914 void UnmapGameButtonsOnTape(void)
16915 {
16916   UnmapGameButtonsExt(TRUE);
16917 }
16918
16919 void RedrawGameButtonsOnTape(void)
16920 {
16921   RedrawGameButtonsExt(TRUE);
16922 }
16923
16924 static void GameUndoRedoExt(void)
16925 {
16926   ClearPlayerAction();
16927
16928   tape.pausing = TRUE;
16929
16930   RedrawPlayfield();
16931   UpdateAndDisplayGameControlValues();
16932
16933   DrawCompleteVideoDisplay();
16934   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16935   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16936   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16937
16938   ModifyPauseButtons();
16939
16940   BackToFront();
16941 }
16942
16943 static void GameUndo(int steps)
16944 {
16945   if (!CheckEngineSnapshotList())
16946     return;
16947
16948   int tape_property_bits = tape.property_bits;
16949
16950   LoadEngineSnapshot_Undo(steps);
16951
16952   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16953
16954   GameUndoRedoExt();
16955 }
16956
16957 static void GameRedo(int steps)
16958 {
16959   if (!CheckEngineSnapshotList())
16960     return;
16961
16962   int tape_property_bits = tape.property_bits;
16963
16964   LoadEngineSnapshot_Redo(steps);
16965
16966   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16967
16968   GameUndoRedoExt();
16969 }
16970
16971 static void HandleGameButtonsExt(int id, int button)
16972 {
16973   static boolean game_undo_executed = FALSE;
16974   int steps = BUTTON_STEPSIZE(button);
16975   boolean handle_game_buttons =
16976     (game_status == GAME_MODE_PLAYING ||
16977      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16978
16979   if (!handle_game_buttons)
16980     return;
16981
16982   switch (id)
16983   {
16984     case GAME_CTRL_ID_STOP:
16985     case GAME_CTRL_ID_PANEL_STOP:
16986     case GAME_CTRL_ID_TOUCH_STOP:
16987       TapeStopGame();
16988
16989       break;
16990
16991     case GAME_CTRL_ID_PAUSE:
16992     case GAME_CTRL_ID_PAUSE2:
16993     case GAME_CTRL_ID_PANEL_PAUSE:
16994     case GAME_CTRL_ID_TOUCH_PAUSE:
16995       if (network.enabled && game_status == GAME_MODE_PLAYING)
16996       {
16997         if (tape.pausing)
16998           SendToServer_ContinuePlaying();
16999         else
17000           SendToServer_PausePlaying();
17001       }
17002       else
17003         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17004
17005       game_undo_executed = FALSE;
17006
17007       break;
17008
17009     case GAME_CTRL_ID_PLAY:
17010     case GAME_CTRL_ID_PANEL_PLAY:
17011       if (game_status == GAME_MODE_MAIN)
17012       {
17013         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17014       }
17015       else if (tape.pausing)
17016       {
17017         if (network.enabled)
17018           SendToServer_ContinuePlaying();
17019         else
17020           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17021       }
17022       break;
17023
17024     case GAME_CTRL_ID_UNDO:
17025       // Important: When using "save snapshot when collecting an item" mode,
17026       // load last (current) snapshot for first "undo" after pressing "pause"
17027       // (else the last-but-one snapshot would be loaded, because the snapshot
17028       // pointer already points to the last snapshot when pressing "pause",
17029       // which is fine for "every step/move" mode, but not for "every collect")
17030       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17031           !game_undo_executed)
17032         steps--;
17033
17034       game_undo_executed = TRUE;
17035
17036       GameUndo(steps);
17037       break;
17038
17039     case GAME_CTRL_ID_REDO:
17040       GameRedo(steps);
17041       break;
17042
17043     case GAME_CTRL_ID_SAVE:
17044       TapeQuickSave();
17045       break;
17046
17047     case GAME_CTRL_ID_LOAD:
17048       TapeQuickLoad();
17049       break;
17050
17051     case GAME_CTRL_ID_RESTART:
17052     case GAME_CTRL_ID_PANEL_RESTART:
17053     case GAME_CTRL_ID_TOUCH_RESTART:
17054       TapeRestartGame();
17055
17056       break;
17057
17058     case SOUND_CTRL_ID_MUSIC:
17059     case SOUND_CTRL_ID_PANEL_MUSIC:
17060       if (setup.sound_music)
17061       { 
17062         setup.sound_music = FALSE;
17063
17064         FadeMusic();
17065       }
17066       else if (audio.music_available)
17067       { 
17068         setup.sound = setup.sound_music = TRUE;
17069
17070         SetAudioMode(setup.sound);
17071
17072         if (game_status == GAME_MODE_PLAYING)
17073           PlayLevelMusic();
17074       }
17075
17076       RedrawSoundButtonGadget(id);
17077
17078       break;
17079
17080     case SOUND_CTRL_ID_LOOPS:
17081     case SOUND_CTRL_ID_PANEL_LOOPS:
17082       if (setup.sound_loops)
17083         setup.sound_loops = FALSE;
17084       else if (audio.loops_available)
17085       {
17086         setup.sound = setup.sound_loops = TRUE;
17087
17088         SetAudioMode(setup.sound);
17089       }
17090
17091       RedrawSoundButtonGadget(id);
17092
17093       break;
17094
17095     case SOUND_CTRL_ID_SIMPLE:
17096     case SOUND_CTRL_ID_PANEL_SIMPLE:
17097       if (setup.sound_simple)
17098         setup.sound_simple = FALSE;
17099       else if (audio.sound_available)
17100       {
17101         setup.sound = setup.sound_simple = TRUE;
17102
17103         SetAudioMode(setup.sound);
17104       }
17105
17106       RedrawSoundButtonGadget(id);
17107
17108       break;
17109
17110     default:
17111       break;
17112   }
17113 }
17114
17115 static void HandleGameButtons(struct GadgetInfo *gi)
17116 {
17117   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17118 }
17119
17120 void HandleSoundButtonKeys(Key key)
17121 {
17122   if (key == setup.shortcut.sound_simple)
17123     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17124   else if (key == setup.shortcut.sound_loops)
17125     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17126   else if (key == setup.shortcut.sound_music)
17127     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17128 }