1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
63 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
65 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
67 #if USE_DELAYED_GFX_REDRAW
68 #define TEST_DrawLevelField(x, y) \
69 GfxRedraw[x][y] |= GFX_REDRAW_TILE
70 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
71 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
72 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
73 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
74 #define TEST_DrawTwinkleOnField(x, y) \
75 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
77 #define TEST_DrawLevelField(x, y) \
79 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
80 DrawLevelFieldCrumbledSand(x, y)
81 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
82 DrawLevelFieldCrumbledSandNeighbours(x, y)
83 #define TEST_DrawTwinkleOnField(x, y) \
84 DrawTwinkleOnField(x, y)
93 /* for MovePlayer() */
94 #define MP_NO_ACTION 0
97 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
99 /* for ScrollPlayer() */
100 #define SCROLL_INIT 0
101 #define SCROLL_GO_ON 1
103 /* for Bang()/Explode() */
104 #define EX_PHASE_START 0
105 #define EX_TYPE_NONE 0
106 #define EX_TYPE_NORMAL (1 << 0)
107 #define EX_TYPE_CENTER (1 << 1)
108 #define EX_TYPE_BORDER (1 << 2)
109 #define EX_TYPE_CROSS (1 << 3)
110 #define EX_TYPE_DYNA (1 << 4)
111 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
113 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
114 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
115 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
116 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
118 /* special positions in the game control window (relative to control window) */
119 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
120 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
121 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
122 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
123 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
124 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
125 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
126 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
127 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
128 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
129 #define XX_SCORE (PANEL_XPOS(game.panel.score))
130 #define YY_SCORE (PANEL_YPOS(game.panel.score))
131 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
132 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
133 #define XX_TIME (PANEL_XPOS(game.panel.time))
134 #define YY_TIME (PANEL_YPOS(game.panel.time))
136 /* special positions in the game control window (relative to main window) */
137 #define DX_LEVEL1 (DX + XX_LEVEL1)
138 #define DX_LEVEL2 (DX + XX_LEVEL2)
139 #define DX_LEVEL (DX + XX_LEVEL)
140 #define DY_LEVEL (DY + YY_LEVEL)
141 #define DX_EMERALDS (DX + XX_EMERALDS)
142 #define DY_EMERALDS (DY + YY_EMERALDS)
143 #define DX_DYNAMITE (DX + XX_DYNAMITE)
144 #define DY_DYNAMITE (DY + YY_DYNAMITE)
145 #define DX_KEYS (DX + XX_KEYS)
146 #define DY_KEYS (DY + YY_KEYS)
147 #define DX_SCORE (DX + XX_SCORE)
148 #define DY_SCORE (DY + YY_SCORE)
149 #define DX_TIME1 (DX + XX_TIME1)
150 #define DX_TIME2 (DX + XX_TIME2)
151 #define DX_TIME (DX + XX_TIME)
152 #define DY_TIME (DY + YY_TIME)
155 /* game panel display and control definitions */
157 #define GAME_PANEL_LEVEL_NUMBER 0
158 #define GAME_PANEL_GEMS 1
159 #define GAME_PANEL_INVENTORY_COUNT 2
160 #define GAME_PANEL_INVENTORY_FIRST_1 3
161 #define GAME_PANEL_INVENTORY_FIRST_2 4
162 #define GAME_PANEL_INVENTORY_FIRST_3 5
163 #define GAME_PANEL_INVENTORY_FIRST_4 6
164 #define GAME_PANEL_INVENTORY_FIRST_5 7
165 #define GAME_PANEL_INVENTORY_FIRST_6 8
166 #define GAME_PANEL_INVENTORY_FIRST_7 9
167 #define GAME_PANEL_INVENTORY_FIRST_8 10
168 #define GAME_PANEL_INVENTORY_LAST_1 11
169 #define GAME_PANEL_INVENTORY_LAST_2 12
170 #define GAME_PANEL_INVENTORY_LAST_3 13
171 #define GAME_PANEL_INVENTORY_LAST_4 14
172 #define GAME_PANEL_INVENTORY_LAST_5 15
173 #define GAME_PANEL_INVENTORY_LAST_6 16
174 #define GAME_PANEL_INVENTORY_LAST_7 17
175 #define GAME_PANEL_INVENTORY_LAST_8 18
176 #define GAME_PANEL_KEY_1 19
177 #define GAME_PANEL_KEY_2 20
178 #define GAME_PANEL_KEY_3 21
179 #define GAME_PANEL_KEY_4 22
180 #define GAME_PANEL_KEY_5 23
181 #define GAME_PANEL_KEY_6 24
182 #define GAME_PANEL_KEY_7 25
183 #define GAME_PANEL_KEY_8 26
184 #define GAME_PANEL_KEY_WHITE 27
185 #define GAME_PANEL_KEY_WHITE_COUNT 28
186 #define GAME_PANEL_SCORE 29
187 #define GAME_PANEL_HIGHSCORE 30
188 #define GAME_PANEL_TIME 31
189 #define GAME_PANEL_TIME_HH 32
190 #define GAME_PANEL_TIME_MM 33
191 #define GAME_PANEL_TIME_SS 34
192 #define GAME_PANEL_SHIELD_NORMAL 35
193 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
194 #define GAME_PANEL_SHIELD_DEADLY 37
195 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
196 #define GAME_PANEL_EXIT 39
197 #define GAME_PANEL_EMC_MAGIC_BALL 40
198 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
199 #define GAME_PANEL_LIGHT_SWITCH 42
200 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
201 #define GAME_PANEL_TIMEGATE_SWITCH 44
202 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
203 #define GAME_PANEL_SWITCHGATE_SWITCH 46
204 #define GAME_PANEL_EMC_LENSES 47
205 #define GAME_PANEL_EMC_LENSES_TIME 48
206 #define GAME_PANEL_EMC_MAGNIFIER 49
207 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
208 #define GAME_PANEL_BALLOON_SWITCH 51
209 #define GAME_PANEL_DYNABOMB_NUMBER 52
210 #define GAME_PANEL_DYNABOMB_SIZE 53
211 #define GAME_PANEL_DYNABOMB_POWER 54
212 #define GAME_PANEL_PENGUINS 55
213 #define GAME_PANEL_SOKOBAN_OBJECTS 56
214 #define GAME_PANEL_SOKOBAN_FIELDS 57
215 #define GAME_PANEL_ROBOT_WHEEL 58
216 #define GAME_PANEL_CONVEYOR_BELT_1 59
217 #define GAME_PANEL_CONVEYOR_BELT_2 60
218 #define GAME_PANEL_CONVEYOR_BELT_3 61
219 #define GAME_PANEL_CONVEYOR_BELT_4 62
220 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
221 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
222 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
223 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
224 #define GAME_PANEL_MAGIC_WALL 67
225 #define GAME_PANEL_MAGIC_WALL_TIME 68
226 #define GAME_PANEL_GRAVITY_STATE 69
227 #define GAME_PANEL_GRAPHIC_1 70
228 #define GAME_PANEL_GRAPHIC_2 71
229 #define GAME_PANEL_GRAPHIC_3 72
230 #define GAME_PANEL_GRAPHIC_4 73
231 #define GAME_PANEL_GRAPHIC_5 74
232 #define GAME_PANEL_GRAPHIC_6 75
233 #define GAME_PANEL_GRAPHIC_7 76
234 #define GAME_PANEL_GRAPHIC_8 77
235 #define GAME_PANEL_ELEMENT_1 78
236 #define GAME_PANEL_ELEMENT_2 79
237 #define GAME_PANEL_ELEMENT_3 80
238 #define GAME_PANEL_ELEMENT_4 81
239 #define GAME_PANEL_ELEMENT_5 82
240 #define GAME_PANEL_ELEMENT_6 83
241 #define GAME_PANEL_ELEMENT_7 84
242 #define GAME_PANEL_ELEMENT_8 85
243 #define GAME_PANEL_ELEMENT_COUNT_1 86
244 #define GAME_PANEL_ELEMENT_COUNT_2 87
245 #define GAME_PANEL_ELEMENT_COUNT_3 88
246 #define GAME_PANEL_ELEMENT_COUNT_4 89
247 #define GAME_PANEL_ELEMENT_COUNT_5 90
248 #define GAME_PANEL_ELEMENT_COUNT_6 91
249 #define GAME_PANEL_ELEMENT_COUNT_7 92
250 #define GAME_PANEL_ELEMENT_COUNT_8 93
251 #define GAME_PANEL_CE_SCORE_1 94
252 #define GAME_PANEL_CE_SCORE_2 95
253 #define GAME_PANEL_CE_SCORE_3 96
254 #define GAME_PANEL_CE_SCORE_4 97
255 #define GAME_PANEL_CE_SCORE_5 98
256 #define GAME_PANEL_CE_SCORE_6 99
257 #define GAME_PANEL_CE_SCORE_7 100
258 #define GAME_PANEL_CE_SCORE_8 101
259 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
260 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
261 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
262 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
263 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
264 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
265 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
266 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
267 #define GAME_PANEL_PLAYER_NAME 110
268 #define GAME_PANEL_LEVEL_NAME 111
269 #define GAME_PANEL_LEVEL_AUTHOR 112
271 #define NUM_GAME_PANEL_CONTROLS 113
273 struct GamePanelOrderInfo
279 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
281 struct GamePanelControlInfo
285 struct TextPosInfo *pos;
288 int value, last_value;
289 int frame, last_frame;
294 static struct GamePanelControlInfo game_panel_controls[] =
297 GAME_PANEL_LEVEL_NUMBER,
298 &game.panel.level_number,
307 GAME_PANEL_INVENTORY_COUNT,
308 &game.panel.inventory_count,
312 GAME_PANEL_INVENTORY_FIRST_1,
313 &game.panel.inventory_first[0],
317 GAME_PANEL_INVENTORY_FIRST_2,
318 &game.panel.inventory_first[1],
322 GAME_PANEL_INVENTORY_FIRST_3,
323 &game.panel.inventory_first[2],
327 GAME_PANEL_INVENTORY_FIRST_4,
328 &game.panel.inventory_first[3],
332 GAME_PANEL_INVENTORY_FIRST_5,
333 &game.panel.inventory_first[4],
337 GAME_PANEL_INVENTORY_FIRST_6,
338 &game.panel.inventory_first[5],
342 GAME_PANEL_INVENTORY_FIRST_7,
343 &game.panel.inventory_first[6],
347 GAME_PANEL_INVENTORY_FIRST_8,
348 &game.panel.inventory_first[7],
352 GAME_PANEL_INVENTORY_LAST_1,
353 &game.panel.inventory_last[0],
357 GAME_PANEL_INVENTORY_LAST_2,
358 &game.panel.inventory_last[1],
362 GAME_PANEL_INVENTORY_LAST_3,
363 &game.panel.inventory_last[2],
367 GAME_PANEL_INVENTORY_LAST_4,
368 &game.panel.inventory_last[3],
372 GAME_PANEL_INVENTORY_LAST_5,
373 &game.panel.inventory_last[4],
377 GAME_PANEL_INVENTORY_LAST_6,
378 &game.panel.inventory_last[5],
382 GAME_PANEL_INVENTORY_LAST_7,
383 &game.panel.inventory_last[6],
387 GAME_PANEL_INVENTORY_LAST_8,
388 &game.panel.inventory_last[7],
432 GAME_PANEL_KEY_WHITE,
433 &game.panel.key_white,
437 GAME_PANEL_KEY_WHITE_COUNT,
438 &game.panel.key_white_count,
447 GAME_PANEL_HIGHSCORE,
448 &game.panel.highscore,
472 GAME_PANEL_SHIELD_NORMAL,
473 &game.panel.shield_normal,
477 GAME_PANEL_SHIELD_NORMAL_TIME,
478 &game.panel.shield_normal_time,
482 GAME_PANEL_SHIELD_DEADLY,
483 &game.panel.shield_deadly,
487 GAME_PANEL_SHIELD_DEADLY_TIME,
488 &game.panel.shield_deadly_time,
497 GAME_PANEL_EMC_MAGIC_BALL,
498 &game.panel.emc_magic_ball,
502 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
503 &game.panel.emc_magic_ball_switch,
507 GAME_PANEL_LIGHT_SWITCH,
508 &game.panel.light_switch,
512 GAME_PANEL_LIGHT_SWITCH_TIME,
513 &game.panel.light_switch_time,
517 GAME_PANEL_TIMEGATE_SWITCH,
518 &game.panel.timegate_switch,
522 GAME_PANEL_TIMEGATE_SWITCH_TIME,
523 &game.panel.timegate_switch_time,
527 GAME_PANEL_SWITCHGATE_SWITCH,
528 &game.panel.switchgate_switch,
532 GAME_PANEL_EMC_LENSES,
533 &game.panel.emc_lenses,
537 GAME_PANEL_EMC_LENSES_TIME,
538 &game.panel.emc_lenses_time,
542 GAME_PANEL_EMC_MAGNIFIER,
543 &game.panel.emc_magnifier,
547 GAME_PANEL_EMC_MAGNIFIER_TIME,
548 &game.panel.emc_magnifier_time,
552 GAME_PANEL_BALLOON_SWITCH,
553 &game.panel.balloon_switch,
557 GAME_PANEL_DYNABOMB_NUMBER,
558 &game.panel.dynabomb_number,
562 GAME_PANEL_DYNABOMB_SIZE,
563 &game.panel.dynabomb_size,
567 GAME_PANEL_DYNABOMB_POWER,
568 &game.panel.dynabomb_power,
573 &game.panel.penguins,
577 GAME_PANEL_SOKOBAN_OBJECTS,
578 &game.panel.sokoban_objects,
582 GAME_PANEL_SOKOBAN_FIELDS,
583 &game.panel.sokoban_fields,
587 GAME_PANEL_ROBOT_WHEEL,
588 &game.panel.robot_wheel,
592 GAME_PANEL_CONVEYOR_BELT_1,
593 &game.panel.conveyor_belt[0],
597 GAME_PANEL_CONVEYOR_BELT_2,
598 &game.panel.conveyor_belt[1],
602 GAME_PANEL_CONVEYOR_BELT_3,
603 &game.panel.conveyor_belt[2],
607 GAME_PANEL_CONVEYOR_BELT_4,
608 &game.panel.conveyor_belt[3],
612 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
613 &game.panel.conveyor_belt_switch[0],
617 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
618 &game.panel.conveyor_belt_switch[1],
622 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
623 &game.panel.conveyor_belt_switch[2],
627 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
628 &game.panel.conveyor_belt_switch[3],
632 GAME_PANEL_MAGIC_WALL,
633 &game.panel.magic_wall,
637 GAME_PANEL_MAGIC_WALL_TIME,
638 &game.panel.magic_wall_time,
642 GAME_PANEL_GRAVITY_STATE,
643 &game.panel.gravity_state,
647 GAME_PANEL_GRAPHIC_1,
648 &game.panel.graphic[0],
652 GAME_PANEL_GRAPHIC_2,
653 &game.panel.graphic[1],
657 GAME_PANEL_GRAPHIC_3,
658 &game.panel.graphic[2],
662 GAME_PANEL_GRAPHIC_4,
663 &game.panel.graphic[3],
667 GAME_PANEL_GRAPHIC_5,
668 &game.panel.graphic[4],
672 GAME_PANEL_GRAPHIC_6,
673 &game.panel.graphic[5],
677 GAME_PANEL_GRAPHIC_7,
678 &game.panel.graphic[6],
682 GAME_PANEL_GRAPHIC_8,
683 &game.panel.graphic[7],
687 GAME_PANEL_ELEMENT_1,
688 &game.panel.element[0],
692 GAME_PANEL_ELEMENT_2,
693 &game.panel.element[1],
697 GAME_PANEL_ELEMENT_3,
698 &game.panel.element[2],
702 GAME_PANEL_ELEMENT_4,
703 &game.panel.element[3],
707 GAME_PANEL_ELEMENT_5,
708 &game.panel.element[4],
712 GAME_PANEL_ELEMENT_6,
713 &game.panel.element[5],
717 GAME_PANEL_ELEMENT_7,
718 &game.panel.element[6],
722 GAME_PANEL_ELEMENT_8,
723 &game.panel.element[7],
727 GAME_PANEL_ELEMENT_COUNT_1,
728 &game.panel.element_count[0],
732 GAME_PANEL_ELEMENT_COUNT_2,
733 &game.panel.element_count[1],
737 GAME_PANEL_ELEMENT_COUNT_3,
738 &game.panel.element_count[2],
742 GAME_PANEL_ELEMENT_COUNT_4,
743 &game.panel.element_count[3],
747 GAME_PANEL_ELEMENT_COUNT_5,
748 &game.panel.element_count[4],
752 GAME_PANEL_ELEMENT_COUNT_6,
753 &game.panel.element_count[5],
757 GAME_PANEL_ELEMENT_COUNT_7,
758 &game.panel.element_count[6],
762 GAME_PANEL_ELEMENT_COUNT_8,
763 &game.panel.element_count[7],
767 GAME_PANEL_CE_SCORE_1,
768 &game.panel.ce_score[0],
772 GAME_PANEL_CE_SCORE_2,
773 &game.panel.ce_score[1],
777 GAME_PANEL_CE_SCORE_3,
778 &game.panel.ce_score[2],
782 GAME_PANEL_CE_SCORE_4,
783 &game.panel.ce_score[3],
787 GAME_PANEL_CE_SCORE_5,
788 &game.panel.ce_score[4],
792 GAME_PANEL_CE_SCORE_6,
793 &game.panel.ce_score[5],
797 GAME_PANEL_CE_SCORE_7,
798 &game.panel.ce_score[6],
802 GAME_PANEL_CE_SCORE_8,
803 &game.panel.ce_score[7],
807 GAME_PANEL_CE_SCORE_1_ELEMENT,
808 &game.panel.ce_score_element[0],
812 GAME_PANEL_CE_SCORE_2_ELEMENT,
813 &game.panel.ce_score_element[1],
817 GAME_PANEL_CE_SCORE_3_ELEMENT,
818 &game.panel.ce_score_element[2],
822 GAME_PANEL_CE_SCORE_4_ELEMENT,
823 &game.panel.ce_score_element[3],
827 GAME_PANEL_CE_SCORE_5_ELEMENT,
828 &game.panel.ce_score_element[4],
832 GAME_PANEL_CE_SCORE_6_ELEMENT,
833 &game.panel.ce_score_element[5],
837 GAME_PANEL_CE_SCORE_7_ELEMENT,
838 &game.panel.ce_score_element[6],
842 GAME_PANEL_CE_SCORE_8_ELEMENT,
843 &game.panel.ce_score_element[7],
847 GAME_PANEL_PLAYER_NAME,
848 &game.panel.player_name,
852 GAME_PANEL_LEVEL_NAME,
853 &game.panel.level_name,
857 GAME_PANEL_LEVEL_AUTHOR,
858 &game.panel.level_author,
871 /* values for delayed check of falling and moving elements and for collision */
872 #define CHECK_DELAY_MOVING 3
873 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
874 #define CHECK_DELAY_COLLISION 2
875 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
877 /* values for initial player move delay (initial delay counter value) */
878 #define INITIAL_MOVE_DELAY_OFF -1
879 #define INITIAL_MOVE_DELAY_ON 0
881 /* values for player movement speed (which is in fact a delay value) */
882 #define MOVE_DELAY_MIN_SPEED 32
883 #define MOVE_DELAY_NORMAL_SPEED 8
884 #define MOVE_DELAY_HIGH_SPEED 4
885 #define MOVE_DELAY_MAX_SPEED 1
887 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
888 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
890 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
891 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
893 /* values for other actions */
894 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
895 #define MOVE_STEPSIZE_MIN (1)
896 #define MOVE_STEPSIZE_MAX (TILEX)
898 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
899 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
901 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
903 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
904 RND(element_info[e].push_delay_random))
905 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
906 RND(element_info[e].drop_delay_random))
907 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
908 RND(element_info[e].move_delay_random))
909 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
910 (element_info[e].move_delay_random))
911 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
912 RND(element_info[e].ce_value_random_initial))
913 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
914 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
915 RND((c)->delay_random * (c)->delay_frames))
916 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
917 RND((c)->delay_random))
920 #define GET_VALID_RUNTIME_ELEMENT(e) \
921 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
923 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
924 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
925 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
926 (be) + (e) - EL_SELF)
928 #define GET_PLAYER_FROM_BITS(p) \
929 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
931 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
932 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
933 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
934 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
935 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
936 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
937 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
938 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
939 RESOLVED_REFERENCE_ELEMENT(be, e) : \
942 #define CAN_GROW_INTO(e) \
943 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
945 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
946 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
949 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
950 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
951 (CAN_MOVE_INTO_ACID(e) && \
952 Feld[x][y] == EL_ACID) || \
955 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
956 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
957 (CAN_MOVE_INTO_ACID(e) && \
958 Feld[x][y] == EL_ACID) || \
961 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
962 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
964 (CAN_MOVE_INTO_ACID(e) && \
965 Feld[x][y] == EL_ACID) || \
966 (DONT_COLLIDE_WITH(e) && \
968 !PLAYER_ENEMY_PROTECTED(x, y))))
970 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
971 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
973 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
974 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
976 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
977 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
979 #define ANDROID_CAN_CLONE_FIELD(x, y) \
980 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
981 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
983 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
984 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
986 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
987 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
989 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
990 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
992 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
993 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
995 #define PIG_CAN_ENTER_FIELD(e, x, y) \
996 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
998 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
999 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1000 Feld[x][y] == EL_EM_EXIT_OPEN || \
1001 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1002 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1003 IS_FOOD_PENGUIN(Feld[x][y])))
1004 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1005 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1007 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1008 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1010 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1011 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1013 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1014 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1015 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1017 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1019 #define CE_ENTER_FIELD_COND(e, x, y) \
1020 (!IS_PLAYER(x, y) && \
1021 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1023 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1024 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1026 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1027 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1029 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1030 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1031 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1032 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1034 /* game button identifiers */
1035 #define GAME_CTRL_ID_STOP 0
1036 #define GAME_CTRL_ID_PAUSE 1
1037 #define GAME_CTRL_ID_PLAY 2
1038 #define SOUND_CTRL_ID_MUSIC 3
1039 #define SOUND_CTRL_ID_LOOPS 4
1040 #define SOUND_CTRL_ID_SIMPLE 5
1042 #define NUM_GAME_BUTTONS 6
1045 /* forward declaration for internal use */
1047 static void CreateField(int, int, int);
1049 static void ResetGfxAnimation(int, int);
1051 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1052 static void AdvanceFrameAndPlayerCounters(int);
1054 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1055 static boolean MovePlayer(struct PlayerInfo *, int, int);
1056 static void ScrollPlayer(struct PlayerInfo *, int);
1057 static void ScrollScreen(struct PlayerInfo *, int);
1059 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1060 static boolean DigFieldByCE(int, int, int);
1061 static boolean SnapField(struct PlayerInfo *, int, int);
1062 static boolean DropElement(struct PlayerInfo *);
1064 static void InitBeltMovement(void);
1065 static void CloseAllOpenTimegates(void);
1066 static void CheckGravityMovement(struct PlayerInfo *);
1067 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1068 static void KillPlayerUnlessEnemyProtected(int, int);
1069 static void KillPlayerUnlessExplosionProtected(int, int);
1071 static void TestIfPlayerTouchesCustomElement(int, int);
1072 static void TestIfElementTouchesCustomElement(int, int);
1073 static void TestIfElementHitsCustomElement(int, int, int);
1075 static void TestIfElementSmashesCustomElement(int, int, int);
1078 static void HandleElementChange(int, int, int);
1079 static void ExecuteCustomElementAction(int, int, int, int);
1080 static boolean ChangeElement(int, int, int, int);
1082 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1083 #define CheckTriggeredElementChange(x, y, e, ev) \
1084 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1085 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1086 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1087 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1088 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1089 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1090 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1092 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1093 #define CheckElementChange(x, y, e, te, ev) \
1094 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1095 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1096 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1097 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1098 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1100 static void PlayLevelSound(int, int, int);
1101 static void PlayLevelSoundNearest(int, int, int);
1102 static void PlayLevelSoundAction(int, int, int);
1103 static void PlayLevelSoundElementAction(int, int, int, int);
1104 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1105 static void PlayLevelSoundActionIfLoop(int, int, int);
1106 static void StopLevelSoundActionIfLoop(int, int, int);
1107 static void PlayLevelMusic();
1109 static void MapGameButtons();
1110 static void HandleGameButtons(struct GadgetInfo *);
1112 int AmoebeNachbarNr(int, int);
1113 void AmoebeUmwandeln(int, int);
1114 void ContinueMoving(int, int);
1115 void Bang(int, int);
1116 void InitMovDir(int, int);
1117 void InitAmoebaNr(int, int);
1118 int NewHiScore(void);
1120 void TestIfGoodThingHitsBadThing(int, int, int);
1121 void TestIfBadThingHitsGoodThing(int, int, int);
1122 void TestIfPlayerTouchesBadThing(int, int);
1123 void TestIfPlayerRunsIntoBadThing(int, int, int);
1124 void TestIfBadThingTouchesPlayer(int, int);
1125 void TestIfBadThingRunsIntoPlayer(int, int, int);
1126 void TestIfFriendTouchesBadThing(int, int);
1127 void TestIfBadThingTouchesFriend(int, int);
1128 void TestIfBadThingTouchesOtherBadThing(int, int);
1129 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1131 void KillPlayer(struct PlayerInfo *);
1132 void BuryPlayer(struct PlayerInfo *);
1133 void RemovePlayer(struct PlayerInfo *);
1135 static int getInvisibleActiveFromInvisibleElement(int);
1136 static int getInvisibleFromInvisibleActiveElement(int);
1138 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1140 /* for detection of endless loops, caused by custom element programming */
1141 /* (using maximal playfield width x 10 is just a rough approximation) */
1142 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1144 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1146 if (recursion_loop_detected) \
1149 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1151 recursion_loop_detected = TRUE; \
1152 recursion_loop_element = (e); \
1155 recursion_loop_depth++; \
1158 #define RECURSION_LOOP_DETECTION_END() \
1160 recursion_loop_depth--; \
1163 static int recursion_loop_depth;
1164 static boolean recursion_loop_detected;
1165 static boolean recursion_loop_element;
1168 /* ------------------------------------------------------------------------- */
1169 /* definition of elements that automatically change to other elements after */
1170 /* a specified time, eventually calling a function when changing */
1171 /* ------------------------------------------------------------------------- */
1173 /* forward declaration for changer functions */
1174 static void InitBuggyBase(int, int);
1175 static void WarnBuggyBase(int, int);
1177 static void InitTrap(int, int);
1178 static void ActivateTrap(int, int);
1179 static void ChangeActiveTrap(int, int);
1181 static void InitRobotWheel(int, int);
1182 static void RunRobotWheel(int, int);
1183 static void StopRobotWheel(int, int);
1185 static void InitTimegateWheel(int, int);
1186 static void RunTimegateWheel(int, int);
1188 static void InitMagicBallDelay(int, int);
1189 static void ActivateMagicBall(int, int);
1191 struct ChangingElementInfo
1196 void (*pre_change_function)(int x, int y);
1197 void (*change_function)(int x, int y);
1198 void (*post_change_function)(int x, int y);
1201 static struct ChangingElementInfo change_delay_list[] =
1236 EL_STEEL_EXIT_OPENING,
1244 EL_STEEL_EXIT_CLOSING,
1245 EL_STEEL_EXIT_CLOSED,
1272 EL_EM_STEEL_EXIT_OPENING,
1273 EL_EM_STEEL_EXIT_OPEN,
1280 EL_EM_STEEL_EXIT_CLOSING,
1284 EL_EM_STEEL_EXIT_CLOSED,
1308 EL_SWITCHGATE_OPENING,
1316 EL_SWITCHGATE_CLOSING,
1317 EL_SWITCHGATE_CLOSED,
1324 EL_TIMEGATE_OPENING,
1332 EL_TIMEGATE_CLOSING,
1341 EL_ACID_SPLASH_LEFT,
1349 EL_ACID_SPLASH_RIGHT,
1358 EL_SP_BUGGY_BASE_ACTIVATING,
1365 EL_SP_BUGGY_BASE_ACTIVATING,
1366 EL_SP_BUGGY_BASE_ACTIVE,
1373 EL_SP_BUGGY_BASE_ACTIVE,
1397 EL_ROBOT_WHEEL_ACTIVE,
1405 EL_TIMEGATE_SWITCH_ACTIVE,
1413 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1414 EL_DC_TIMEGATE_SWITCH,
1421 EL_EMC_MAGIC_BALL_ACTIVE,
1422 EL_EMC_MAGIC_BALL_ACTIVE,
1429 EL_EMC_SPRING_BUMPER_ACTIVE,
1430 EL_EMC_SPRING_BUMPER,
1437 EL_DIAGONAL_SHRINKING,
1445 EL_DIAGONAL_GROWING,
1466 int push_delay_fixed, push_delay_random;
1470 { EL_SPRING, 0, 0 },
1471 { EL_BALLOON, 0, 0 },
1473 { EL_SOKOBAN_OBJECT, 2, 0 },
1474 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1475 { EL_SATELLITE, 2, 0 },
1476 { EL_SP_DISK_YELLOW, 2, 0 },
1478 { EL_UNDEFINED, 0, 0 },
1486 move_stepsize_list[] =
1488 { EL_AMOEBA_DROP, 2 },
1489 { EL_AMOEBA_DROPPING, 2 },
1490 { EL_QUICKSAND_FILLING, 1 },
1491 { EL_QUICKSAND_EMPTYING, 1 },
1492 { EL_QUICKSAND_FAST_FILLING, 2 },
1493 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1494 { EL_MAGIC_WALL_FILLING, 2 },
1495 { EL_MAGIC_WALL_EMPTYING, 2 },
1496 { EL_BD_MAGIC_WALL_FILLING, 2 },
1497 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1498 { EL_DC_MAGIC_WALL_FILLING, 2 },
1499 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1501 { EL_UNDEFINED, 0 },
1509 collect_count_list[] =
1512 { EL_BD_DIAMOND, 1 },
1513 { EL_EMERALD_YELLOW, 1 },
1514 { EL_EMERALD_RED, 1 },
1515 { EL_EMERALD_PURPLE, 1 },
1517 { EL_SP_INFOTRON, 1 },
1521 { EL_UNDEFINED, 0 },
1529 access_direction_list[] =
1531 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1532 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1533 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1534 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1535 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1536 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1537 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1538 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1539 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1540 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1541 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1543 { EL_SP_PORT_LEFT, MV_RIGHT },
1544 { EL_SP_PORT_RIGHT, MV_LEFT },
1545 { EL_SP_PORT_UP, MV_DOWN },
1546 { EL_SP_PORT_DOWN, MV_UP },
1547 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1548 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1549 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1550 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1551 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1552 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1553 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1554 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1555 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1556 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1557 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1558 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1559 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1560 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1561 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1563 { EL_UNDEFINED, MV_NONE }
1566 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1568 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1569 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1570 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1571 IS_JUST_CHANGING(x, y))
1573 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1575 /* static variables for playfield scan mode (scanning forward or backward) */
1576 static int playfield_scan_start_x = 0;
1577 static int playfield_scan_start_y = 0;
1578 static int playfield_scan_delta_x = 1;
1579 static int playfield_scan_delta_y = 1;
1581 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1582 (y) >= 0 && (y) <= lev_fieldy - 1; \
1583 (y) += playfield_scan_delta_y) \
1584 for ((x) = playfield_scan_start_x; \
1585 (x) >= 0 && (x) <= lev_fieldx - 1; \
1586 (x) += playfield_scan_delta_x)
1589 void DEBUG_SetMaximumDynamite()
1593 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1594 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1595 local_player->inventory_element[local_player->inventory_size++] =
1600 static void InitPlayfieldScanModeVars()
1602 if (game.use_reverse_scan_direction)
1604 playfield_scan_start_x = lev_fieldx - 1;
1605 playfield_scan_start_y = lev_fieldy - 1;
1607 playfield_scan_delta_x = -1;
1608 playfield_scan_delta_y = -1;
1612 playfield_scan_start_x = 0;
1613 playfield_scan_start_y = 0;
1615 playfield_scan_delta_x = 1;
1616 playfield_scan_delta_y = 1;
1620 static void InitPlayfieldScanMode(int mode)
1622 game.use_reverse_scan_direction =
1623 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1625 InitPlayfieldScanModeVars();
1628 static int get_move_delay_from_stepsize(int move_stepsize)
1631 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1633 /* make sure that stepsize value is always a power of 2 */
1634 move_stepsize = (1 << log_2(move_stepsize));
1636 return TILEX / move_stepsize;
1639 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1642 int player_nr = player->index_nr;
1643 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1644 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1646 /* do no immediately change move delay -- the player might just be moving */
1647 player->move_delay_value_next = move_delay;
1649 /* information if player can move must be set separately */
1650 player->cannot_move = cannot_move;
1654 player->move_delay = game.initial_move_delay[player_nr];
1655 player->move_delay_value = game.initial_move_delay_value[player_nr];
1657 player->move_delay_value_next = -1;
1659 player->move_delay_reset_counter = 0;
1663 void GetPlayerConfig()
1665 GameFrameDelay = setup.game_frame_delay;
1667 if (!audio.sound_available)
1668 setup.sound_simple = FALSE;
1670 if (!audio.loops_available)
1671 setup.sound_loops = FALSE;
1673 if (!audio.music_available)
1674 setup.sound_music = FALSE;
1676 if (!video.fullscreen_available)
1677 setup.fullscreen = FALSE;
1679 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1681 SetAudioMode(setup.sound);
1685 int GetElementFromGroupElement(int element)
1687 if (IS_GROUP_ELEMENT(element))
1689 struct ElementGroupInfo *group = element_info[element].group;
1690 int last_anim_random_frame = gfx.anim_random_frame;
1693 if (group->choice_mode == ANIM_RANDOM)
1694 gfx.anim_random_frame = RND(group->num_elements_resolved);
1696 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1697 group->choice_mode, 0,
1700 if (group->choice_mode == ANIM_RANDOM)
1701 gfx.anim_random_frame = last_anim_random_frame;
1703 group->choice_pos++;
1705 element = group->element_resolved[element_pos];
1711 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 if (element == EL_SP_MURPHY)
1717 if (stored_player[0].present)
1719 Feld[x][y] = EL_SP_MURPHY_CLONE;
1725 stored_player[0].initial_element = element;
1726 stored_player[0].use_murphy = TRUE;
1728 if (!level.use_artwork_element[0])
1729 stored_player[0].artwork_element = EL_SP_MURPHY;
1732 Feld[x][y] = EL_PLAYER_1;
1738 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1739 int jx = player->jx, jy = player->jy;
1741 player->present = TRUE;
1743 player->block_last_field = (element == EL_SP_MURPHY ?
1744 level.sp_block_last_field :
1745 level.block_last_field);
1747 /* ---------- initialize player's last field block delay --------------- */
1749 /* always start with reliable default value (no adjustment needed) */
1750 player->block_delay_adjustment = 0;
1752 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1753 if (player->block_last_field && element == EL_SP_MURPHY)
1754 player->block_delay_adjustment = 1;
1756 /* special case 2: in game engines before 3.1.1, blocking was different */
1757 if (game.use_block_last_field_bug)
1758 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760 if (!options.network || player->connected)
1762 player->active = TRUE;
1764 /* remove potentially duplicate players */
1765 if (StorePlayer[jx][jy] == Feld[x][y])
1766 StorePlayer[jx][jy] = 0;
1768 StorePlayer[x][y] = Feld[x][y];
1772 printf("Player %d activated.\n", player->element_nr);
1773 printf("[Local player is %d and currently %s.]\n",
1774 local_player->element_nr,
1775 local_player->active ? "active" : "not active");
1779 Feld[x][y] = EL_EMPTY;
1781 player->jx = player->last_jx = x;
1782 player->jy = player->last_jy = y;
1786 static void InitField(int x, int y, boolean init_game)
1788 int element = Feld[x][y];
1797 InitPlayerField(x, y, element, init_game);
1800 case EL_SOKOBAN_FIELD_PLAYER:
1801 element = Feld[x][y] = EL_PLAYER_1;
1802 InitField(x, y, init_game);
1804 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1805 InitField(x, y, init_game);
1808 case EL_SOKOBAN_FIELD_EMPTY:
1809 local_player->sokobanfields_still_needed++;
1813 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1814 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1815 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1816 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1817 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1818 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1819 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1820 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1821 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1822 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1831 case EL_SPACESHIP_RIGHT:
1832 case EL_SPACESHIP_UP:
1833 case EL_SPACESHIP_LEFT:
1834 case EL_SPACESHIP_DOWN:
1835 case EL_BD_BUTTERFLY:
1836 case EL_BD_BUTTERFLY_RIGHT:
1837 case EL_BD_BUTTERFLY_UP:
1838 case EL_BD_BUTTERFLY_LEFT:
1839 case EL_BD_BUTTERFLY_DOWN:
1841 case EL_BD_FIREFLY_RIGHT:
1842 case EL_BD_FIREFLY_UP:
1843 case EL_BD_FIREFLY_LEFT:
1844 case EL_BD_FIREFLY_DOWN:
1845 case EL_PACMAN_RIGHT:
1847 case EL_PACMAN_LEFT:
1848 case EL_PACMAN_DOWN:
1850 case EL_YAMYAM_LEFT:
1851 case EL_YAMYAM_RIGHT:
1853 case EL_YAMYAM_DOWN:
1854 case EL_DARK_YAMYAM:
1857 case EL_SP_SNIKSNAK:
1858 case EL_SP_ELECTRON:
1867 case EL_AMOEBA_FULL:
1872 case EL_AMOEBA_DROP:
1873 if (y == lev_fieldy - 1)
1875 Feld[x][y] = EL_AMOEBA_GROWING;
1876 Store[x][y] = EL_AMOEBA_WET;
1880 case EL_DYNAMITE_ACTIVE:
1881 case EL_SP_DISK_RED_ACTIVE:
1882 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1883 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1884 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1885 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1886 MovDelay[x][y] = 96;
1889 case EL_EM_DYNAMITE_ACTIVE:
1890 MovDelay[x][y] = 32;
1894 local_player->lights_still_needed++;
1898 local_player->friends_still_needed++;
1903 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1906 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1907 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1908 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1909 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1910 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1911 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1912 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1913 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1914 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1915 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1916 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1917 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1920 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1921 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1922 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1924 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1926 game.belt_dir[belt_nr] = belt_dir;
1927 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1929 else /* more than one switch -- set it like the first switch */
1931 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1936 #if !USE_BOTH_SWITCHGATE_SWITCHES
1937 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1939 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1942 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1944 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1948 case EL_LIGHT_SWITCH_ACTIVE:
1950 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953 case EL_INVISIBLE_STEELWALL:
1954 case EL_INVISIBLE_WALL:
1955 case EL_INVISIBLE_SAND:
1956 if (game.light_time_left > 0 ||
1957 game.lenses_time_left > 0)
1958 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961 case EL_EMC_MAGIC_BALL:
1962 if (game.ball_state)
1963 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966 case EL_EMC_MAGIC_BALL_SWITCH:
1967 if (game.ball_state)
1968 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971 case EL_TRIGGER_PLAYER:
1972 case EL_TRIGGER_ELEMENT:
1973 case EL_TRIGGER_CE_VALUE:
1974 case EL_TRIGGER_CE_SCORE:
1976 case EL_ANY_ELEMENT:
1977 case EL_CURRENT_CE_VALUE:
1978 case EL_CURRENT_CE_SCORE:
1995 /* reference elements should not be used on the playfield */
1996 Feld[x][y] = EL_EMPTY;
2000 if (IS_CUSTOM_ELEMENT(element))
2002 if (CAN_MOVE(element))
2005 #if USE_NEW_CUSTOM_VALUE
2006 if (!element_info[element].use_last_ce_value || init_game)
2007 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2010 else if (IS_GROUP_ELEMENT(element))
2012 Feld[x][y] = GetElementFromGroupElement(element);
2014 InitField(x, y, init_game);
2021 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2026 InitField(x, y, init_game);
2028 /* not needed to call InitMovDir() -- already done by InitField()! */
2029 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030 CAN_MOVE(Feld[x][y]))
2034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2036 int old_element = Feld[x][y];
2038 InitField(x, y, init_game);
2040 /* not needed to call InitMovDir() -- already done by InitField()! */
2041 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042 CAN_MOVE(old_element) &&
2043 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046 /* this case is in fact a combination of not less than three bugs:
2047 first, it calls InitMovDir() for elements that can move, although this is
2048 already done by InitField(); then, it checks the element that was at this
2049 field _before_ the call to InitField() (which can change it); lastly, it
2050 was not called for "mole with direction" elements, which were treated as
2051 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2057 static int get_key_element_from_nr(int key_nr)
2059 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061 EL_EM_KEY_1 : EL_KEY_1);
2063 return key_base_element + key_nr;
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2068 return (player->inventory_size > 0 ?
2069 player->inventory_element[player->inventory_size - 1] :
2070 player->inventory_infinite_element != EL_UNDEFINED ?
2071 player->inventory_infinite_element :
2072 player->dynabombs_left > 0 ?
2073 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2079 /* pos >= 0: get element from bottom of the stack;
2080 pos < 0: get element from top of the stack */
2084 int min_inventory_size = -pos;
2085 int inventory_pos = player->inventory_size - min_inventory_size;
2086 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2088 return (player->inventory_size >= min_inventory_size ?
2089 player->inventory_element[inventory_pos] :
2090 player->inventory_infinite_element != EL_UNDEFINED ?
2091 player->inventory_infinite_element :
2092 player->dynabombs_left >= min_dynabombs_left ?
2093 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2098 int min_dynabombs_left = pos + 1;
2099 int min_inventory_size = pos + 1 - player->dynabombs_left;
2100 int inventory_pos = pos - player->dynabombs_left;
2102 return (player->inventory_infinite_element != EL_UNDEFINED ?
2103 player->inventory_infinite_element :
2104 player->dynabombs_left >= min_dynabombs_left ?
2105 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106 player->inventory_size >= min_inventory_size ?
2107 player->inventory_element[inventory_pos] :
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2114 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2118 if (gpo1->sort_priority != gpo2->sort_priority)
2119 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2121 compare_result = gpo1->nr - gpo2->nr;
2123 return compare_result;
2126 void InitGameControlValues()
2130 for (i = 0; game_panel_controls[i].nr != -1; i++)
2132 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2133 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2134 struct TextPosInfo *pos = gpc->pos;
2136 int type = gpc->type;
2140 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2141 Error(ERR_EXIT, "this should not happen -- please debug");
2144 /* force update of game controls after initialization */
2145 gpc->value = gpc->last_value = -1;
2146 gpc->frame = gpc->last_frame = -1;
2147 gpc->gfx_frame = -1;
2149 /* determine panel value width for later calculation of alignment */
2150 if (type == TYPE_INTEGER || type == TYPE_STRING)
2152 pos->width = pos->size * getFontWidth(pos->font);
2153 pos->height = getFontHeight(pos->font);
2155 else if (type == TYPE_ELEMENT)
2157 pos->width = pos->size;
2158 pos->height = pos->size;
2161 /* fill structure for game panel draw order */
2163 gpo->sort_priority = pos->sort_priority;
2166 /* sort game panel controls according to sort_priority and control number */
2167 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2168 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2171 void UpdatePlayfieldElementCount()
2173 boolean use_element_count = FALSE;
2176 /* first check if it is needed at all to calculate playfield element count */
2177 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2178 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2179 use_element_count = TRUE;
2181 if (!use_element_count)
2184 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2185 element_info[i].element_count = 0;
2187 SCAN_PLAYFIELD(x, y)
2189 element_info[Feld[x][y]].element_count++;
2192 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2193 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2194 if (IS_IN_GROUP(j, i))
2195 element_info[EL_GROUP_START + i].element_count +=
2196 element_info[j].element_count;
2199 void UpdateGameControlValues()
2202 int time = (local_player->LevelSolved ?
2203 local_player->LevelSolved_CountingTime :
2204 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2205 level.native_em_level->lev->time :
2206 level.time == 0 ? TimePlayed : TimeLeft);
2207 int score = (local_player->LevelSolved ?
2208 local_player->LevelSolved_CountingScore :
2209 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210 level.native_em_level->lev->score :
2211 local_player->score);
2212 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213 level.native_em_level->lev->required :
2214 local_player->gems_still_needed);
2215 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2216 level.native_em_level->lev->required > 0 :
2217 local_player->gems_still_needed > 0 ||
2218 local_player->sokobanfields_still_needed > 0 ||
2219 local_player->lights_still_needed > 0);
2221 UpdatePlayfieldElementCount();
2223 /* update game panel control values */
2225 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2226 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2229 for (i = 0; i < MAX_NUM_KEYS; i++)
2230 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2231 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234 if (game.centered_player_nr == -1)
2236 for (i = 0; i < MAX_PLAYERS; i++)
2238 for (k = 0; k < MAX_NUM_KEYS; k++)
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2242 if (level.native_em_level->ply[i]->keys & (1 << k))
2243 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2244 get_key_element_from_nr(k);
2246 else if (stored_player[i].key[k])
2247 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2248 get_key_element_from_nr(k);
2251 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2252 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253 level.native_em_level->ply[i]->dynamite;
2255 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256 stored_player[i].inventory_size;
2258 if (stored_player[i].num_white_keys > 0)
2259 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2262 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2263 stored_player[i].num_white_keys;
2268 int player_nr = game.centered_player_nr;
2270 for (k = 0; k < MAX_NUM_KEYS; k++)
2272 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2275 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2276 get_key_element_from_nr(k);
2278 else if (stored_player[player_nr].key[k])
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2283 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 level.native_em_level->ply[player_nr]->dynamite;
2287 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288 stored_player[player_nr].inventory_size;
2290 if (stored_player[player_nr].num_white_keys > 0)
2291 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2293 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294 stored_player[player_nr].num_white_keys;
2297 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2299 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300 get_inventory_element_from_pos(local_player, i);
2301 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302 get_inventory_element_from_pos(local_player, -i - 1);
2305 game_panel_controls[GAME_PANEL_SCORE].value = score;
2306 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2308 game_panel_controls[GAME_PANEL_TIME].value = time;
2310 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2314 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2315 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2317 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2318 local_player->shield_normal_time_left;
2319 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2320 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2322 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2323 local_player->shield_deadly_time_left;
2325 game_panel_controls[GAME_PANEL_EXIT].value =
2326 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2328 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2329 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2330 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2331 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2332 EL_EMC_MAGIC_BALL_SWITCH);
2334 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2335 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2336 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2337 game.light_time_left;
2339 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2340 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2341 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2342 game.timegate_time_left;
2344 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2345 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2347 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2348 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2349 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2350 game.lenses_time_left;
2352 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2353 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2354 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2355 game.magnify_time_left;
2357 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2358 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2359 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2360 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2361 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2362 EL_BALLOON_SWITCH_NONE);
2364 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2365 local_player->dynabomb_count;
2366 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2367 local_player->dynabomb_size;
2368 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2369 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2371 game_panel_controls[GAME_PANEL_PENGUINS].value =
2372 local_player->friends_still_needed;
2374 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2375 local_player->sokobanfields_still_needed;
2376 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2377 local_player->sokobanfields_still_needed;
2379 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2380 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2382 for (i = 0; i < NUM_BELTS; i++)
2384 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2385 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2386 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2387 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2388 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2391 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2392 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2393 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2394 game.magic_wall_time_left;
2396 #if USE_PLAYER_GRAVITY
2397 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398 local_player->gravity;
2400 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2403 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2404 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2406 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2407 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2408 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2409 game.panel.element[i].id : EL_UNDEFINED);
2411 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2413 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2414 element_info[game.panel.element_count[i].id].element_count : 0);
2416 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2417 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2418 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2419 element_info[game.panel.ce_score[i].id].collect_score : 0);
2421 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2423 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2424 element_info[game.panel.ce_score_element[i].id].collect_score :
2427 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2428 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2429 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2431 /* update game panel control frames */
2433 for (i = 0; game_panel_controls[i].nr != -1; i++)
2435 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2437 if (gpc->type == TYPE_ELEMENT)
2439 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2441 int last_anim_random_frame = gfx.anim_random_frame;
2442 int element = gpc->value;
2443 int graphic = el2panelimg(element);
2445 if (gpc->value != gpc->last_value)
2448 gpc->gfx_random = INIT_GFX_RANDOM();
2454 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2455 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2456 gpc->gfx_random = INIT_GFX_RANDOM();
2459 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2460 gfx.anim_random_frame = gpc->gfx_random;
2462 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2463 gpc->gfx_frame = element_info[element].collect_score;
2465 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2468 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2469 gfx.anim_random_frame = last_anim_random_frame;
2475 void DisplayGameControlValues()
2477 boolean redraw_panel = FALSE;
2480 for (i = 0; game_panel_controls[i].nr != -1; i++)
2482 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2484 if (PANEL_DEACTIVATED(gpc->pos))
2487 if (gpc->value == gpc->last_value &&
2488 gpc->frame == gpc->last_frame)
2491 redraw_panel = TRUE;
2497 /* copy default game door content to main double buffer */
2498 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2499 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2501 /* redraw game control buttons */
2503 RedrawGameButtons();
2509 game_status = GAME_MODE_PSEUDO_PANEL;
2512 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2514 for (i = 0; game_panel_controls[i].nr != -1; i++)
2518 int nr = game_panel_order[i].nr;
2519 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2521 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2524 struct TextPosInfo *pos = gpc->pos;
2525 int type = gpc->type;
2526 int value = gpc->value;
2527 int frame = gpc->frame;
2529 int last_value = gpc->last_value;
2530 int last_frame = gpc->last_frame;
2532 int size = pos->size;
2533 int font = pos->font;
2534 boolean draw_masked = pos->draw_masked;
2535 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2537 if (PANEL_DEACTIVATED(pos))
2541 if (value == last_value && frame == last_frame)
2545 gpc->last_value = value;
2546 gpc->last_frame = frame;
2549 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2552 if (type == TYPE_INTEGER)
2554 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2555 nr == GAME_PANEL_TIME)
2557 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2559 if (use_dynamic_size) /* use dynamic number of digits */
2561 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2562 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2563 int size2 = size1 + 1;
2564 int font1 = pos->font;
2565 int font2 = pos->font_alt;
2567 size = (value < value_change ? size1 : size2);
2568 font = (value < value_change ? font1 : font2);
2571 /* clear background if value just changed its size (dynamic digits) */
2572 if ((last_value < value_change) != (value < value_change))
2574 int width1 = size1 * getFontWidth(font1);
2575 int width2 = size2 * getFontWidth(font2);
2576 int max_width = MAX(width1, width2);
2577 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2579 pos->width = max_width;
2581 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2582 max_width, max_height);
2589 /* correct text size if "digits" is zero or less */
2591 size = strlen(int2str(value, size));
2593 /* dynamically correct text alignment */
2594 pos->width = size * getFontWidth(font);
2597 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598 int2str(value, size), font, mask_mode);
2600 else if (type == TYPE_ELEMENT)
2602 int element, graphic;
2606 int dst_x = PANEL_XPOS(pos);
2607 int dst_y = PANEL_YPOS(pos);
2610 if (value != EL_UNDEFINED && value != EL_EMPTY)
2613 graphic = el2panelimg(value);
2615 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2618 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2622 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2625 width = graphic_info[graphic].width * size / TILESIZE;
2626 height = graphic_info[graphic].height * size / TILESIZE;
2630 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2631 dst_x - src_x, dst_y - src_y);
2632 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2637 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2642 if (value == EL_UNDEFINED || value == EL_EMPTY)
2644 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2645 graphic = el2panelimg(element);
2647 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2648 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2649 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2654 graphic = el2panelimg(value);
2656 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2659 width = graphic_info[graphic].width * size / TILESIZE;
2660 height = graphic_info[graphic].height * size / TILESIZE;
2662 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2665 else if (type == TYPE_STRING)
2667 boolean active = (value != 0);
2668 char *state_normal = "off";
2669 char *state_active = "on";
2670 char *state = (active ? state_active : state_normal);
2671 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2672 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2673 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2674 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2676 if (nr == GAME_PANEL_GRAVITY_STATE)
2678 int font1 = pos->font; /* (used for normal state) */
2679 int font2 = pos->font_alt; /* (used for active state) */
2681 int size1 = strlen(state_normal);
2682 int size2 = strlen(state_active);
2683 int width1 = size1 * getFontWidth(font1);
2684 int width2 = size2 * getFontWidth(font2);
2685 int max_width = MAX(width1, width2);
2686 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2688 pos->width = max_width;
2690 /* clear background for values that may have changed its size */
2691 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2692 max_width, max_height);
2695 font = (active ? font2 : font1);
2705 /* don't truncate output if "chars" is zero or less */
2708 /* dynamically correct text alignment */
2709 pos->width = size * getFontWidth(font);
2713 s_cut = getStringCopyN(s, size);
2715 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2716 s_cut, font, mask_mode);
2722 redraw_mask |= REDRAW_DOOR_1;
2725 game_status = GAME_MODE_PLAYING;
2728 void UpdateAndDisplayGameControlValues()
2730 if (tape.warp_forward)
2733 UpdateGameControlValues();
2734 DisplayGameControlValues();
2737 void DrawGameValue_Emeralds(int value)
2739 struct TextPosInfo *pos = &game.panel.gems;
2741 int font_nr = pos->font;
2743 int font_nr = FONT_TEXT_2;
2745 int font_width = getFontWidth(font_nr);
2746 int chars = pos->size;
2749 return; /* !!! USE NEW STUFF !!! */
2752 if (PANEL_DEACTIVATED(pos))
2755 pos->width = chars * font_width;
2757 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2760 void DrawGameValue_Dynamite(int value)
2762 struct TextPosInfo *pos = &game.panel.inventory_count;
2764 int font_nr = pos->font;
2766 int font_nr = FONT_TEXT_2;
2768 int font_width = getFontWidth(font_nr);
2769 int chars = pos->size;
2772 return; /* !!! USE NEW STUFF !!! */
2775 if (PANEL_DEACTIVATED(pos))
2778 pos->width = chars * font_width;
2780 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2783 void DrawGameValue_Score(int value)
2785 struct TextPosInfo *pos = &game.panel.score;
2787 int font_nr = pos->font;
2789 int font_nr = FONT_TEXT_2;
2791 int font_width = getFontWidth(font_nr);
2792 int chars = pos->size;
2795 return; /* !!! USE NEW STUFF !!! */
2798 if (PANEL_DEACTIVATED(pos))
2801 pos->width = chars * font_width;
2803 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2806 void DrawGameValue_Time(int value)
2808 struct TextPosInfo *pos = &game.panel.time;
2809 static int last_value = -1;
2812 int chars = pos->size;
2814 int font1_nr = pos->font;
2815 int font2_nr = pos->font_alt;
2817 int font1_nr = FONT_TEXT_2;
2818 int font2_nr = FONT_TEXT_1;
2820 int font_nr = font1_nr;
2821 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2824 return; /* !!! USE NEW STUFF !!! */
2827 if (PANEL_DEACTIVATED(pos))
2830 if (use_dynamic_chars) /* use dynamic number of chars */
2832 chars = (value < 1000 ? chars1 : chars2);
2833 font_nr = (value < 1000 ? font1_nr : font2_nr);
2836 /* clear background if value just changed its size (dynamic chars only) */
2837 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2839 int width1 = chars1 * getFontWidth(font1_nr);
2840 int width2 = chars2 * getFontWidth(font2_nr);
2841 int max_width = MAX(width1, width2);
2842 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2844 pos->width = max_width;
2846 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847 max_width, max_height);
2850 pos->width = chars * getFontWidth(font_nr);
2852 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Level(int value)
2859 struct TextPosInfo *pos = &game.panel.level_number;
2862 int chars = pos->size;
2864 int font1_nr = pos->font;
2865 int font2_nr = pos->font_alt;
2867 int font1_nr = FONT_TEXT_2;
2868 int font2_nr = FONT_TEXT_1;
2870 int font_nr = font1_nr;
2871 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2874 return; /* !!! USE NEW STUFF !!! */
2877 if (PANEL_DEACTIVATED(pos))
2880 if (use_dynamic_chars) /* use dynamic number of chars */
2882 chars = (level_nr < 100 ? chars1 : chars2);
2883 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2886 pos->width = chars * getFontWidth(font_nr);
2888 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2891 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2894 struct TextPosInfo *pos = &game.panel.keys;
2897 int base_key_graphic = EL_KEY_1;
2902 return; /* !!! USE NEW STUFF !!! */
2906 if (PANEL_DEACTIVATED(pos))
2911 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2912 base_key_graphic = EL_EM_KEY_1;
2916 pos->width = 4 * MINI_TILEX;
2920 for (i = 0; i < MAX_NUM_KEYS; i++)
2922 /* currently only 4 of 8 possible keys are displayed */
2923 for (i = 0; i < STD_NUM_KEYS; i++)
2927 struct TextPosInfo *pos = &game.panel.key[i];
2929 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2930 int src_y = DOOR_GFX_PAGEY1 + 123;
2932 int dst_x = PANEL_XPOS(pos);
2933 int dst_y = PANEL_YPOS(pos);
2935 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2936 int dst_y = PANEL_YPOS(pos);
2940 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2941 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2943 int graphic = el2edimg(element);
2947 if (PANEL_DEACTIVATED(pos))
2952 /* masked blit with tiles from half-size scaled bitmap does not work yet
2953 (no mask bitmap created for these sizes after loading and scaling) --
2954 solution: load without creating mask, scale, then create final mask */
2956 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2957 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2962 int graphic = el2edimg(base_key_graphic + i);
2967 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2969 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2970 dst_x - src_x, dst_y - src_y);
2971 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2977 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2979 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2980 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2983 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2985 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2986 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2994 void DrawGameValue_Emeralds(int value)
2996 int font_nr = FONT_TEXT_2;
2997 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2999 if (PANEL_DEACTIVATED(game.panel.gems))
3002 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3005 void DrawGameValue_Dynamite(int value)
3007 int font_nr = FONT_TEXT_2;
3008 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3010 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3013 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3016 void DrawGameValue_Score(int value)
3018 int font_nr = FONT_TEXT_2;
3019 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3021 if (PANEL_DEACTIVATED(game.panel.score))
3024 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3027 void DrawGameValue_Time(int value)
3029 int font1_nr = FONT_TEXT_2;
3031 int font2_nr = FONT_TEXT_1;
3033 int font2_nr = FONT_LEVEL_NUMBER;
3035 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3036 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3038 if (PANEL_DEACTIVATED(game.panel.time))
3041 /* clear background if value just changed its size */
3042 if (value == 999 || value == 1000)
3043 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3046 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3048 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3051 void DrawGameValue_Level(int value)
3053 int font1_nr = FONT_TEXT_2;
3055 int font2_nr = FONT_TEXT_1;
3057 int font2_nr = FONT_LEVEL_NUMBER;
3060 if (PANEL_DEACTIVATED(game.panel.level))
3064 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3066 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3069 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3071 int base_key_graphic = EL_KEY_1;
3074 if (PANEL_DEACTIVATED(game.panel.keys))
3077 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3078 base_key_graphic = EL_EM_KEY_1;
3080 /* currently only 4 of 8 possible keys are displayed */
3081 for (i = 0; i < STD_NUM_KEYS; i++)
3083 int x = XX_KEYS + i * MINI_TILEX;
3087 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3089 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3090 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3096 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3099 int key[MAX_NUM_KEYS];
3102 /* prevent EM engine from updating time/score values parallel to GameWon() */
3103 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3104 local_player->LevelSolved)
3107 for (i = 0; i < MAX_NUM_KEYS; i++)
3108 key[i] = key_bits & (1 << i);
3110 DrawGameValue_Level(level_nr);
3112 DrawGameValue_Emeralds(emeralds);
3113 DrawGameValue_Dynamite(dynamite);
3114 DrawGameValue_Score(score);
3115 DrawGameValue_Time(time);
3117 DrawGameValue_Keys(key);
3120 void UpdateGameDoorValues()
3122 UpdateGameControlValues();
3125 void DrawGameDoorValues()
3127 DisplayGameControlValues();
3130 void DrawGameDoorValues_OLD()
3132 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3133 int dynamite_value = 0;
3134 int score_value = (local_player->LevelSolved ? local_player->score_final :
3135 local_player->score);
3136 int gems_value = local_player->gems_still_needed;
3140 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3142 DrawGameDoorValues_EM();
3147 if (game.centered_player_nr == -1)
3149 for (i = 0; i < MAX_PLAYERS; i++)
3151 for (j = 0; j < MAX_NUM_KEYS; j++)
3152 if (stored_player[i].key[j])
3153 key_bits |= (1 << j);
3155 dynamite_value += stored_player[i].inventory_size;
3160 int player_nr = game.centered_player_nr;
3162 for (i = 0; i < MAX_NUM_KEYS; i++)
3163 if (stored_player[player_nr].key[i])
3164 key_bits |= (1 << i);
3166 dynamite_value = stored_player[player_nr].inventory_size;
3169 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3175 =============================================================================
3177 -----------------------------------------------------------------------------
3178 initialize game engine due to level / tape version number
3179 =============================================================================
3182 static void InitGameEngine()
3184 int i, j, k, l, x, y;
3186 /* set game engine from tape file when re-playing, else from level file */
3187 game.engine_version = (tape.playing ? tape.engine_version :
3188 level.game_version);
3190 /* ---------------------------------------------------------------------- */
3191 /* set flags for bugs and changes according to active game engine version */
3192 /* ---------------------------------------------------------------------- */
3195 Summary of bugfix/change:
3196 Fixed handling for custom elements that change when pushed by the player.
3198 Fixed/changed in version:
3202 Before 3.1.0, custom elements that "change when pushing" changed directly
3203 after the player started pushing them (until then handled in "DigField()").
3204 Since 3.1.0, these custom elements are not changed until the "pushing"
3205 move of the element is finished (now handled in "ContinueMoving()").
3207 Affected levels/tapes:
3208 The first condition is generally needed for all levels/tapes before version
3209 3.1.0, which might use the old behaviour before it was changed; known tapes
3210 that are affected are some tapes from the level set "Walpurgis Gardens" by
3212 The second condition is an exception from the above case and is needed for
3213 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3214 above (including some development versions of 3.1.0), but before it was
3215 known that this change would break tapes like the above and was fixed in
3216 3.1.1, so that the changed behaviour was active although the engine version
3217 while recording maybe was before 3.1.0. There is at least one tape that is
3218 affected by this exception, which is the tape for the one-level set "Bug
3219 Machine" by Juergen Bonhagen.
3222 game.use_change_when_pushing_bug =
3223 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3225 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3226 tape.game_version < VERSION_IDENT(3,1,1,0)));
3229 Summary of bugfix/change:
3230 Fixed handling for blocking the field the player leaves when moving.
3232 Fixed/changed in version:
3236 Before 3.1.1, when "block last field when moving" was enabled, the field
3237 the player is leaving when moving was blocked for the time of the move,
3238 and was directly unblocked afterwards. This resulted in the last field
3239 being blocked for exactly one less than the number of frames of one player
3240 move. Additionally, even when blocking was disabled, the last field was
3241 blocked for exactly one frame.
3242 Since 3.1.1, due to changes in player movement handling, the last field
3243 is not blocked at all when blocking is disabled. When blocking is enabled,
3244 the last field is blocked for exactly the number of frames of one player
3245 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3246 last field is blocked for exactly one more than the number of frames of
3249 Affected levels/tapes:
3250 (!!! yet to be determined -- probably many !!!)
3253 game.use_block_last_field_bug =
3254 (game.engine_version < VERSION_IDENT(3,1,1,0));
3257 Summary of bugfix/change:
3258 Changed behaviour of CE changes with multiple changes per single frame.
3260 Fixed/changed in version:
3264 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3265 This resulted in race conditions where CEs seem to behave strange in some
3266 situations (where triggered CE changes were just skipped because there was
3267 already a CE change on that tile in the playfield in that engine frame).
3268 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3269 (The number of changes per frame must be limited in any case, because else
3270 it is easily possible to define CE changes that would result in an infinite
3271 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3272 should be set large enough so that it would only be reached in cases where
3273 the corresponding CE change conditions run into a loop. Therefore, it seems
3274 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3275 maximal number of change pages for custom elements.)
3277 Affected levels/tapes:
3281 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3282 game.max_num_changes_per_frame = 1;
3284 game.max_num_changes_per_frame =
3285 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3288 /* ---------------------------------------------------------------------- */
3290 /* default scan direction: scan playfield from top/left to bottom/right */
3291 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3293 /* dynamically adjust element properties according to game engine version */
3294 InitElementPropertiesEngine(game.engine_version);
3297 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3298 printf(" tape version == %06d [%s] [file: %06d]\n",
3299 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3301 printf(" => game.engine_version == %06d\n", game.engine_version);
3304 /* ---------- initialize player's initial move delay --------------------- */
3306 /* dynamically adjust player properties according to level information */
3307 for (i = 0; i < MAX_PLAYERS; i++)
3308 game.initial_move_delay_value[i] =
3309 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3311 /* dynamically adjust player properties according to game engine version */
3312 for (i = 0; i < MAX_PLAYERS; i++)
3313 game.initial_move_delay[i] =
3314 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3315 game.initial_move_delay_value[i] : 0);
3317 /* ---------- initialize player's initial push delay --------------------- */
3319 /* dynamically adjust player properties according to game engine version */
3320 game.initial_push_delay_value =
3321 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3323 /* ---------- initialize changing elements ------------------------------- */
3325 /* initialize changing elements information */
3326 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3328 struct ElementInfo *ei = &element_info[i];
3330 /* this pointer might have been changed in the level editor */
3331 ei->change = &ei->change_page[0];
3333 if (!IS_CUSTOM_ELEMENT(i))
3335 ei->change->target_element = EL_EMPTY_SPACE;
3336 ei->change->delay_fixed = 0;
3337 ei->change->delay_random = 0;
3338 ei->change->delay_frames = 1;
3341 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3343 ei->has_change_event[j] = FALSE;
3345 ei->event_page_nr[j] = 0;
3346 ei->event_page[j] = &ei->change_page[0];
3350 /* add changing elements from pre-defined list */
3351 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3353 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3354 struct ElementInfo *ei = &element_info[ch_delay->element];
3356 ei->change->target_element = ch_delay->target_element;
3357 ei->change->delay_fixed = ch_delay->change_delay;
3359 ei->change->pre_change_function = ch_delay->pre_change_function;
3360 ei->change->change_function = ch_delay->change_function;
3361 ei->change->post_change_function = ch_delay->post_change_function;
3363 ei->change->can_change = TRUE;
3364 ei->change->can_change_or_has_action = TRUE;
3366 ei->has_change_event[CE_DELAY] = TRUE;
3368 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3369 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3372 /* ---------- initialize internal run-time variables --------------------- */
3374 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3376 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3378 for (j = 0; j < ei->num_change_pages; j++)
3380 ei->change_page[j].can_change_or_has_action =
3381 (ei->change_page[j].can_change |
3382 ei->change_page[j].has_action);
3386 /* add change events from custom element configuration */
3387 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3389 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3391 for (j = 0; j < ei->num_change_pages; j++)
3393 if (!ei->change_page[j].can_change_or_has_action)
3396 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3398 /* only add event page for the first page found with this event */
3399 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3401 ei->has_change_event[k] = TRUE;
3403 ei->event_page_nr[k] = j;
3404 ei->event_page[k] = &ei->change_page[j];
3411 /* ---------- initialize reference elements in change conditions --------- */
3413 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3415 int element = EL_CUSTOM_START + i;
3416 struct ElementInfo *ei = &element_info[element];
3418 for (j = 0; j < ei->num_change_pages; j++)
3420 int trigger_element = ei->change_page[j].initial_trigger_element;
3422 if (trigger_element >= EL_PREV_CE_8 &&
3423 trigger_element <= EL_NEXT_CE_8)
3424 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3426 ei->change_page[j].trigger_element = trigger_element;
3431 /* ---------- initialize run-time trigger player and element ------------- */
3433 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3435 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3437 for (j = 0; j < ei->num_change_pages; j++)
3439 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3440 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3441 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3442 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3443 ei->change_page[j].actual_trigger_ce_value = 0;
3444 ei->change_page[j].actual_trigger_ce_score = 0;
3448 /* ---------- initialize trigger events ---------------------------------- */
3450 /* initialize trigger events information */
3451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3453 trigger_events[i][j] = FALSE;
3455 /* add trigger events from element change event properties */
3456 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3458 struct ElementInfo *ei = &element_info[i];
3460 for (j = 0; j < ei->num_change_pages; j++)
3462 if (!ei->change_page[j].can_change_or_has_action)
3465 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3467 int trigger_element = ei->change_page[j].trigger_element;
3469 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3471 if (ei->change_page[j].has_event[k])
3473 if (IS_GROUP_ELEMENT(trigger_element))
3475 struct ElementGroupInfo *group =
3476 element_info[trigger_element].group;
3478 for (l = 0; l < group->num_elements_resolved; l++)
3479 trigger_events[group->element_resolved[l]][k] = TRUE;
3481 else if (trigger_element == EL_ANY_ELEMENT)
3482 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3483 trigger_events[l][k] = TRUE;
3485 trigger_events[trigger_element][k] = TRUE;
3492 /* ---------- initialize push delay -------------------------------------- */
3494 /* initialize push delay values to default */
3495 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3497 if (!IS_CUSTOM_ELEMENT(i))
3499 /* set default push delay values (corrected since version 3.0.7-1) */
3500 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3502 element_info[i].push_delay_fixed = 2;
3503 element_info[i].push_delay_random = 8;
3507 element_info[i].push_delay_fixed = 8;
3508 element_info[i].push_delay_random = 8;
3513 /* set push delay value for certain elements from pre-defined list */
3514 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3516 int e = push_delay_list[i].element;
3518 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3519 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3522 /* set push delay value for Supaplex elements for newer engine versions */
3523 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3525 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3527 if (IS_SP_ELEMENT(i))
3529 /* set SP push delay to just enough to push under a falling zonk */
3530 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3532 element_info[i].push_delay_fixed = delay;
3533 element_info[i].push_delay_random = 0;
3538 /* ---------- initialize move stepsize ----------------------------------- */
3540 /* initialize move stepsize values to default */
3541 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3542 if (!IS_CUSTOM_ELEMENT(i))
3543 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3545 /* set move stepsize value for certain elements from pre-defined list */
3546 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3548 int e = move_stepsize_list[i].element;
3550 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3553 /* ---------- initialize collect score ----------------------------------- */
3555 /* initialize collect score values for custom elements from initial value */
3556 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3557 if (IS_CUSTOM_ELEMENT(i))
3558 element_info[i].collect_score = element_info[i].collect_score_initial;
3560 /* ---------- initialize collect count ----------------------------------- */
3562 /* initialize collect count values for non-custom elements */
3563 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3564 if (!IS_CUSTOM_ELEMENT(i))
3565 element_info[i].collect_count_initial = 0;
3567 /* add collect count values for all elements from pre-defined list */
3568 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3569 element_info[collect_count_list[i].element].collect_count_initial =
3570 collect_count_list[i].count;
3572 /* ---------- initialize access direction -------------------------------- */
3574 /* initialize access direction values to default (access from every side) */
3575 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3576 if (!IS_CUSTOM_ELEMENT(i))
3577 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3579 /* set access direction value for certain elements from pre-defined list */
3580 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3581 element_info[access_direction_list[i].element].access_direction =
3582 access_direction_list[i].direction;
3584 /* ---------- initialize explosion content ------------------------------- */
3585 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3587 if (IS_CUSTOM_ELEMENT(i))
3590 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3592 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3594 element_info[i].content.e[x][y] =
3595 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3596 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3597 i == EL_PLAYER_3 ? EL_EMERALD :
3598 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3599 i == EL_MOLE ? EL_EMERALD_RED :
3600 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3601 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3602 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3603 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3604 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3605 i == EL_WALL_EMERALD ? EL_EMERALD :
3606 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3607 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3608 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3609 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3610 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3611 i == EL_WALL_PEARL ? EL_PEARL :
3612 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3617 /* ---------- initialize recursion detection ------------------------------ */
3618 recursion_loop_depth = 0;
3619 recursion_loop_detected = FALSE;
3620 recursion_loop_element = EL_UNDEFINED;
3622 /* ---------- initialize graphics engine ---------------------------------- */
3623 game.scroll_delay_value =
3624 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3625 setup.scroll_delay ? setup.scroll_delay_value : 0);
3626 game.scroll_delay_value =
3627 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3630 int get_num_special_action(int element, int action_first, int action_last)
3632 int num_special_action = 0;
3635 for (i = action_first; i <= action_last; i++)
3637 boolean found = FALSE;
3639 for (j = 0; j < NUM_DIRECTIONS; j++)
3640 if (el_act_dir2img(element, i, j) !=
3641 el_act_dir2img(element, ACTION_DEFAULT, j))
3645 num_special_action++;
3650 return num_special_action;
3655 =============================================================================
3657 -----------------------------------------------------------------------------
3658 initialize and start new game
3659 =============================================================================
3664 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3665 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3666 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3668 boolean do_fading = (game_status == GAME_MODE_MAIN);
3672 game_status = GAME_MODE_PLAYING;
3675 InitGameControlValues();
3677 /* don't play tapes over network */
3678 network_playing = (options.network && !tape.playing);
3680 for (i = 0; i < MAX_PLAYERS; i++)
3682 struct PlayerInfo *player = &stored_player[i];
3684 player->index_nr = i;
3685 player->index_bit = (1 << i);
3686 player->element_nr = EL_PLAYER_1 + i;
3688 player->present = FALSE;
3689 player->active = FALSE;
3690 player->killed = FALSE;
3693 player->effective_action = 0;
3694 player->programmed_action = 0;
3697 player->score_final = 0;
3699 player->gems_still_needed = level.gems_needed;
3700 player->sokobanfields_still_needed = 0;
3701 player->lights_still_needed = 0;
3702 player->friends_still_needed = 0;
3704 for (j = 0; j < MAX_NUM_KEYS; j++)
3705 player->key[j] = FALSE;
3707 player->num_white_keys = 0;
3709 player->dynabomb_count = 0;
3710 player->dynabomb_size = 1;
3711 player->dynabombs_left = 0;
3712 player->dynabomb_xl = FALSE;
3714 player->MovDir = MV_NONE;
3717 player->GfxDir = MV_NONE;
3718 player->GfxAction = ACTION_DEFAULT;
3720 player->StepFrame = 0;
3722 player->initial_element = player->element_nr;
3723 player->artwork_element =
3724 (level.use_artwork_element[i] ? level.artwork_element[i] :
3725 player->element_nr);
3726 player->use_murphy = FALSE;
3728 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3729 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3731 player->gravity = level.initial_player_gravity[i];
3733 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3735 player->actual_frame_counter = 0;
3737 player->step_counter = 0;
3739 player->last_move_dir = MV_NONE;
3741 player->is_active = FALSE;
3743 player->is_waiting = FALSE;
3744 player->is_moving = FALSE;
3745 player->is_auto_moving = FALSE;
3746 player->is_digging = FALSE;
3747 player->is_snapping = FALSE;
3748 player->is_collecting = FALSE;
3749 player->is_pushing = FALSE;
3750 player->is_switching = FALSE;
3751 player->is_dropping = FALSE;
3752 player->is_dropping_pressed = FALSE;
3754 player->is_bored = FALSE;
3755 player->is_sleeping = FALSE;
3757 player->frame_counter_bored = -1;
3758 player->frame_counter_sleeping = -1;
3760 player->anim_delay_counter = 0;
3761 player->post_delay_counter = 0;
3763 player->dir_waiting = MV_NONE;
3764 player->action_waiting = ACTION_DEFAULT;
3765 player->last_action_waiting = ACTION_DEFAULT;
3766 player->special_action_bored = ACTION_DEFAULT;
3767 player->special_action_sleeping = ACTION_DEFAULT;
3769 player->switch_x = -1;
3770 player->switch_y = -1;
3772 player->drop_x = -1;
3773 player->drop_y = -1;
3775 player->show_envelope = 0;
3777 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3779 player->push_delay = -1; /* initialized when pushing starts */
3780 player->push_delay_value = game.initial_push_delay_value;
3782 player->drop_delay = 0;
3783 player->drop_pressed_delay = 0;
3785 player->last_jx = -1;
3786 player->last_jy = -1;
3790 player->shield_normal_time_left = 0;
3791 player->shield_deadly_time_left = 0;
3793 player->inventory_infinite_element = EL_UNDEFINED;
3794 player->inventory_size = 0;
3796 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3797 SnapField(player, 0, 0);
3799 player->LevelSolved = FALSE;
3800 player->GameOver = FALSE;
3802 player->LevelSolved_GameWon = FALSE;
3803 player->LevelSolved_GameEnd = FALSE;
3804 player->LevelSolved_PanelOff = FALSE;
3805 player->LevelSolved_SaveTape = FALSE;
3806 player->LevelSolved_SaveScore = FALSE;
3807 player->LevelSolved_CountingTime = 0;
3808 player->LevelSolved_CountingScore = 0;
3811 network_player_action_received = FALSE;
3813 #if defined(NETWORK_AVALIABLE)
3814 /* initial null action */
3815 if (network_playing)
3816 SendToServer_MovePlayer(MV_NONE);
3825 TimeLeft = level.time;
3828 ScreenMovDir = MV_NONE;
3832 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3834 AllPlayersGone = FALSE;
3836 game.yamyam_content_nr = 0;
3837 game.robot_wheel_active = FALSE;
3838 game.magic_wall_active = FALSE;
3839 game.magic_wall_time_left = 0;
3840 game.light_time_left = 0;
3841 game.timegate_time_left = 0;
3842 game.switchgate_pos = 0;
3843 game.wind_direction = level.wind_direction_initial;
3845 #if !USE_PLAYER_GRAVITY
3846 game.gravity = FALSE;
3847 game.explosions_delayed = TRUE;
3850 game.lenses_time_left = 0;
3851 game.magnify_time_left = 0;
3853 game.ball_state = level.ball_state_initial;
3854 game.ball_content_nr = 0;
3856 game.envelope_active = FALSE;
3858 /* set focus to local player for network games, else to all players */
3859 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3860 game.centered_player_nr_next = game.centered_player_nr;
3861 game.set_centered_player = FALSE;
3863 if (network_playing && tape.recording)
3865 /* store client dependent player focus when recording network games */
3866 tape.centered_player_nr_next = game.centered_player_nr_next;
3867 tape.set_centered_player = TRUE;
3870 for (i = 0; i < NUM_BELTS; i++)
3872 game.belt_dir[i] = MV_NONE;
3873 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3876 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3877 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3879 SCAN_PLAYFIELD(x, y)
3881 Feld[x][y] = level.field[x][y];
3882 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3883 ChangeDelay[x][y] = 0;
3884 ChangePage[x][y] = -1;
3885 #if USE_NEW_CUSTOM_VALUE
3886 CustomValue[x][y] = 0; /* initialized in InitField() */
3888 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3890 WasJustMoving[x][y] = 0;
3891 WasJustFalling[x][y] = 0;
3892 CheckCollision[x][y] = 0;
3893 CheckImpact[x][y] = 0;
3895 Pushed[x][y] = FALSE;
3897 ChangeCount[x][y] = 0;
3898 ChangeEvent[x][y] = -1;
3900 ExplodePhase[x][y] = 0;
3901 ExplodeDelay[x][y] = 0;
3902 ExplodeField[x][y] = EX_TYPE_NONE;
3904 RunnerVisit[x][y] = 0;
3905 PlayerVisit[x][y] = 0;
3908 GfxRandom[x][y] = INIT_GFX_RANDOM();
3909 GfxElement[x][y] = EL_UNDEFINED;
3910 GfxAction[x][y] = ACTION_DEFAULT;
3911 GfxDir[x][y] = MV_NONE;
3912 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3915 SCAN_PLAYFIELD(x, y)
3917 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3919 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3921 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3924 InitField(x, y, TRUE);
3926 ResetGfxAnimation(x, y);
3931 for (i = 0; i < MAX_PLAYERS; i++)
3933 struct PlayerInfo *player = &stored_player[i];
3935 /* set number of special actions for bored and sleeping animation */
3936 player->num_special_action_bored =
3937 get_num_special_action(player->artwork_element,
3938 ACTION_BORING_1, ACTION_BORING_LAST);
3939 player->num_special_action_sleeping =
3940 get_num_special_action(player->artwork_element,
3941 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3944 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3945 emulate_sb ? EMU_SOKOBAN :
3946 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3948 #if USE_NEW_ALL_SLIPPERY
3949 /* initialize type of slippery elements */
3950 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3952 if (!IS_CUSTOM_ELEMENT(i))
3954 /* default: elements slip down either to the left or right randomly */
3955 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3957 /* SP style elements prefer to slip down on the left side */
3958 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3959 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3961 /* BD style elements prefer to slip down on the left side */
3962 if (game.emulation == EMU_BOULDERDASH)
3963 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3968 /* initialize explosion and ignition delay */
3969 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3971 if (!IS_CUSTOM_ELEMENT(i))
3974 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977 int last_phase = (num_phase + 1) * delay;
3978 int half_phase = (num_phase / 2) * delay;
3980 element_info[i].explosion_delay = last_phase - 1;
3981 element_info[i].ignition_delay = half_phase;
3983 if (i == EL_BLACK_ORB)
3984 element_info[i].ignition_delay = 1;
3988 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3989 element_info[i].explosion_delay = 1;
3991 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3992 element_info[i].ignition_delay = 1;
3996 /* correct non-moving belts to start moving left */
3997 for (i = 0; i < NUM_BELTS; i++)
3998 if (game.belt_dir[i] == MV_NONE)
3999 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4001 /* check if any connected player was not found in playfield */
4002 for (i = 0; i < MAX_PLAYERS; i++)
4004 struct PlayerInfo *player = &stored_player[i];
4006 if (player->connected && !player->present)
4008 for (j = 0; j < MAX_PLAYERS; j++)
4010 struct PlayerInfo *some_player = &stored_player[j];
4011 int jx = some_player->jx, jy = some_player->jy;
4013 /* assign first free player found that is present in the playfield */
4014 if (some_player->present && !some_player->connected)
4016 player->present = TRUE;
4017 player->active = TRUE;
4019 some_player->present = FALSE;
4020 some_player->active = FALSE;
4022 player->initial_element = some_player->initial_element;
4023 player->artwork_element = some_player->artwork_element;
4025 player->block_last_field = some_player->block_last_field;
4026 player->block_delay_adjustment = some_player->block_delay_adjustment;
4028 StorePlayer[jx][jy] = player->element_nr;
4029 player->jx = player->last_jx = jx;
4030 player->jy = player->last_jy = jy;
4040 /* when playing a tape, eliminate all players who do not participate */
4042 for (i = 0; i < MAX_PLAYERS; i++)
4044 if (stored_player[i].active && !tape.player_participates[i])
4046 struct PlayerInfo *player = &stored_player[i];
4047 int jx = player->jx, jy = player->jy;
4049 player->active = FALSE;
4050 StorePlayer[jx][jy] = 0;
4051 Feld[jx][jy] = EL_EMPTY;
4055 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4057 /* when in single player mode, eliminate all but the first active player */
4059 for (i = 0; i < MAX_PLAYERS; i++)
4061 if (stored_player[i].active)
4063 for (j = i + 1; j < MAX_PLAYERS; j++)
4065 if (stored_player[j].active)
4067 struct PlayerInfo *player = &stored_player[j];
4068 int jx = player->jx, jy = player->jy;
4070 player->active = FALSE;
4071 player->present = FALSE;
4073 StorePlayer[jx][jy] = 0;
4074 Feld[jx][jy] = EL_EMPTY;
4081 /* when recording the game, store which players take part in the game */
4084 for (i = 0; i < MAX_PLAYERS; i++)
4085 if (stored_player[i].active)
4086 tape.player_participates[i] = TRUE;
4091 for (i = 0; i < MAX_PLAYERS; i++)
4093 struct PlayerInfo *player = &stored_player[i];
4095 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4100 if (local_player == player)
4101 printf("Player %d is local player.\n", i+1);
4105 if (BorderElement == EL_EMPTY)
4108 SBX_Right = lev_fieldx - SCR_FIELDX;
4110 SBY_Lower = lev_fieldy - SCR_FIELDY;
4115 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4117 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4120 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4121 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4123 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4124 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4126 /* if local player not found, look for custom element that might create
4127 the player (make some assumptions about the right custom element) */
4128 if (!local_player->present)
4130 int start_x = 0, start_y = 0;
4131 int found_rating = 0;
4132 int found_element = EL_UNDEFINED;
4133 int player_nr = local_player->index_nr;
4135 SCAN_PLAYFIELD(x, y)
4137 int element = Feld[x][y];
4142 if (level.use_start_element[player_nr] &&
4143 level.start_element[player_nr] == element &&
4150 found_element = element;
4153 if (!IS_CUSTOM_ELEMENT(element))
4156 if (CAN_CHANGE(element))
4158 for (i = 0; i < element_info[element].num_change_pages; i++)
4160 /* check for player created from custom element as single target */
4161 content = element_info[element].change_page[i].target_element;
4162 is_player = ELEM_IS_PLAYER(content);
4164 if (is_player && (found_rating < 3 ||
4165 (found_rating == 3 && element < found_element)))
4171 found_element = element;
4176 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4178 /* check for player created from custom element as explosion content */
4179 content = element_info[element].content.e[xx][yy];
4180 is_player = ELEM_IS_PLAYER(content);
4182 if (is_player && (found_rating < 2 ||
4183 (found_rating == 2 && element < found_element)))
4185 start_x = x + xx - 1;
4186 start_y = y + yy - 1;
4189 found_element = element;
4192 if (!CAN_CHANGE(element))
4195 for (i = 0; i < element_info[element].num_change_pages; i++)
4197 /* check for player created from custom element as extended target */
4199 element_info[element].change_page[i].target_content.e[xx][yy];
4201 is_player = ELEM_IS_PLAYER(content);
4203 if (is_player && (found_rating < 1 ||
4204 (found_rating == 1 && element < found_element)))
4206 start_x = x + xx - 1;
4207 start_y = y + yy - 1;
4210 found_element = element;
4216 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4217 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4220 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4221 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4226 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4227 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4228 local_player->jx - MIDPOSX);
4230 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4231 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4232 local_player->jy - MIDPOSY);
4236 /* do not use PLAYING mask for fading out from main screen */
4237 game_status = GAME_MODE_MAIN;
4242 if (!game.restart_level)
4243 CloseDoor(DOOR_CLOSE_1);
4246 if (level_editor_test_game)
4247 FadeSkipNextFadeIn();
4249 FadeSetEnterScreen();
4251 if (level_editor_test_game)
4252 fading = fading_none;
4254 fading = menu.destination;
4258 FadeOut(REDRAW_FIELD);
4261 FadeOut(REDRAW_FIELD);
4265 game_status = GAME_MODE_PLAYING;
4268 /* !!! FIX THIS (START) !!! */
4269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4271 InitGameEngine_EM();
4273 /* blit playfield from scroll buffer to normal back buffer for fading in */
4274 BlitScreenToBitmap_EM(backbuffer);
4281 /* after drawing the level, correct some elements */
4282 if (game.timegate_time_left == 0)
4283 CloseAllOpenTimegates();
4285 /* blit playfield from scroll buffer to normal back buffer for fading in */
4286 if (setup.soft_scrolling)
4287 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4289 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4291 /* !!! FIX THIS (END) !!! */
4294 FadeIn(REDRAW_FIELD);
4297 FadeIn(REDRAW_FIELD);
4302 if (!game.restart_level)
4304 /* copy default game door content to main double buffer */
4305 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4306 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4309 SetPanelBackground();
4310 SetDrawBackgroundMask(REDRAW_DOOR_1);
4313 UpdateAndDisplayGameControlValues();
4315 UpdateGameDoorValues();
4316 DrawGameDoorValues();
4319 if (!game.restart_level)
4323 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4324 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4325 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4329 /* copy actual game door content to door double buffer for OpenDoor() */
4330 BlitBitmap(drawto, bitmap_db_door,
4331 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4333 OpenDoor(DOOR_OPEN_ALL);
4335 PlaySound(SND_GAME_STARTING);
4337 if (setup.sound_music)
4340 KeyboardAutoRepeatOffUnlessAutoplay();
4344 for (i = 0; i < MAX_PLAYERS; i++)
4345 printf("Player %d %sactive.\n",
4346 i + 1, (stored_player[i].active ? "" : "not "));
4357 game.restart_level = FALSE;
4360 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4362 /* this is used for non-R'n'D game engines to update certain engine values */
4364 /* needed to determine if sounds are played within the visible screen area */
4365 scroll_x = actual_scroll_x;
4366 scroll_y = actual_scroll_y;
4369 void InitMovDir(int x, int y)
4371 int i, element = Feld[x][y];
4372 static int xy[4][2] =
4379 static int direction[3][4] =
4381 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4382 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4383 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4392 Feld[x][y] = EL_BUG;
4393 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4396 case EL_SPACESHIP_RIGHT:
4397 case EL_SPACESHIP_UP:
4398 case EL_SPACESHIP_LEFT:
4399 case EL_SPACESHIP_DOWN:
4400 Feld[x][y] = EL_SPACESHIP;
4401 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4404 case EL_BD_BUTTERFLY_RIGHT:
4405 case EL_BD_BUTTERFLY_UP:
4406 case EL_BD_BUTTERFLY_LEFT:
4407 case EL_BD_BUTTERFLY_DOWN:
4408 Feld[x][y] = EL_BD_BUTTERFLY;
4409 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4412 case EL_BD_FIREFLY_RIGHT:
4413 case EL_BD_FIREFLY_UP:
4414 case EL_BD_FIREFLY_LEFT:
4415 case EL_BD_FIREFLY_DOWN:
4416 Feld[x][y] = EL_BD_FIREFLY;
4417 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4420 case EL_PACMAN_RIGHT:
4422 case EL_PACMAN_LEFT:
4423 case EL_PACMAN_DOWN:
4424 Feld[x][y] = EL_PACMAN;
4425 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4428 case EL_YAMYAM_LEFT:
4429 case EL_YAMYAM_RIGHT:
4431 case EL_YAMYAM_DOWN:
4432 Feld[x][y] = EL_YAMYAM;
4433 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4436 case EL_SP_SNIKSNAK:
4437 MovDir[x][y] = MV_UP;
4440 case EL_SP_ELECTRON:
4441 MovDir[x][y] = MV_LEFT;
4448 Feld[x][y] = EL_MOLE;
4449 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4453 if (IS_CUSTOM_ELEMENT(element))
4455 struct ElementInfo *ei = &element_info[element];
4456 int move_direction_initial = ei->move_direction_initial;
4457 int move_pattern = ei->move_pattern;
4459 if (move_direction_initial == MV_START_PREVIOUS)
4461 if (MovDir[x][y] != MV_NONE)
4464 move_direction_initial = MV_START_AUTOMATIC;
4467 if (move_direction_initial == MV_START_RANDOM)
4468 MovDir[x][y] = 1 << RND(4);
4469 else if (move_direction_initial & MV_ANY_DIRECTION)
4470 MovDir[x][y] = move_direction_initial;
4471 else if (move_pattern == MV_ALL_DIRECTIONS ||
4472 move_pattern == MV_TURNING_LEFT ||
4473 move_pattern == MV_TURNING_RIGHT ||
4474 move_pattern == MV_TURNING_LEFT_RIGHT ||
4475 move_pattern == MV_TURNING_RIGHT_LEFT ||
4476 move_pattern == MV_TURNING_RANDOM)
4477 MovDir[x][y] = 1 << RND(4);
4478 else if (move_pattern == MV_HORIZONTAL)
4479 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4480 else if (move_pattern == MV_VERTICAL)
4481 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4482 else if (move_pattern & MV_ANY_DIRECTION)
4483 MovDir[x][y] = element_info[element].move_pattern;
4484 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4485 move_pattern == MV_ALONG_RIGHT_SIDE)
4487 /* use random direction as default start direction */
4488 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4489 MovDir[x][y] = 1 << RND(4);
4491 for (i = 0; i < NUM_DIRECTIONS; i++)
4493 int x1 = x + xy[i][0];
4494 int y1 = y + xy[i][1];
4496 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4498 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4499 MovDir[x][y] = direction[0][i];
4501 MovDir[x][y] = direction[1][i];
4510 MovDir[x][y] = 1 << RND(4);
4512 if (element != EL_BUG &&
4513 element != EL_SPACESHIP &&
4514 element != EL_BD_BUTTERFLY &&
4515 element != EL_BD_FIREFLY)
4518 for (i = 0; i < NUM_DIRECTIONS; i++)
4520 int x1 = x + xy[i][0];
4521 int y1 = y + xy[i][1];
4523 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4525 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4527 MovDir[x][y] = direction[0][i];
4530 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4531 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4533 MovDir[x][y] = direction[1][i];
4542 GfxDir[x][y] = MovDir[x][y];
4545 void InitAmoebaNr(int x, int y)
4548 int group_nr = AmoebeNachbarNr(x, y);
4552 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4554 if (AmoebaCnt[i] == 0)
4562 AmoebaNr[x][y] = group_nr;
4563 AmoebaCnt[group_nr]++;
4564 AmoebaCnt2[group_nr]++;
4567 static void PlayerWins(struct PlayerInfo *player)
4569 player->LevelSolved = TRUE;
4570 player->GameOver = TRUE;
4572 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4573 level.native_em_level->lev->score : player->score);
4575 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4576 player->LevelSolved_CountingScore = player->score_final;
4581 static int time, time_final;
4582 static int score, score_final;
4583 static int game_over_delay_1 = 0;
4584 static int game_over_delay_2 = 0;
4585 int game_over_delay_value_1 = 50;
4586 int game_over_delay_value_2 = 50;
4588 if (!local_player->LevelSolved_GameWon)
4592 /* do not start end game actions before the player stops moving (to exit) */
4593 if (local_player->MovPos)
4596 local_player->LevelSolved_GameWon = TRUE;
4597 local_player->LevelSolved_SaveTape = tape.recording;
4598 local_player->LevelSolved_SaveScore = !tape.playing;
4600 if (tape.auto_play) /* tape might already be stopped here */
4601 tape.auto_play_level_solved = TRUE;
4607 game_over_delay_1 = game_over_delay_value_1;
4608 game_over_delay_2 = game_over_delay_value_2;
4610 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4611 score = score_final = local_player->score_final;
4616 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4618 else if (level.time == 0 && TimePlayed < 999)
4621 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4624 local_player->score_final = score_final;
4626 if (level_editor_test_game)
4629 score = score_final;
4632 local_player->LevelSolved_CountingTime = time;
4633 local_player->LevelSolved_CountingScore = score;
4635 game_panel_controls[GAME_PANEL_TIME].value = time;
4636 game_panel_controls[GAME_PANEL_SCORE].value = score;
4638 DisplayGameControlValues();
4640 DrawGameValue_Time(time);
4641 DrawGameValue_Score(score);
4645 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4647 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4649 /* close exit door after last player */
4650 if ((AllPlayersGone &&
4651 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4652 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4653 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4654 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4655 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4657 int element = Feld[ExitX][ExitY];
4660 if (element == EL_EM_EXIT_OPEN ||
4661 element == EL_EM_STEEL_EXIT_OPEN)
4668 Feld[ExitX][ExitY] =
4669 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4670 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4671 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4672 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4673 EL_EM_STEEL_EXIT_CLOSING);
4675 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4679 /* player disappears */
4680 DrawLevelField(ExitX, ExitY);
4683 for (i = 0; i < MAX_PLAYERS; i++)
4685 struct PlayerInfo *player = &stored_player[i];
4687 if (player->present)
4689 RemovePlayer(player);
4691 /* player disappears */
4692 DrawLevelField(player->jx, player->jy);
4697 PlaySound(SND_GAME_WINNING);
4700 if (game_over_delay_1 > 0)
4702 game_over_delay_1--;
4707 if (time != time_final)
4709 int time_to_go = ABS(time_final - time);
4710 int time_count_dir = (time < time_final ? +1 : -1);
4711 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4713 time += time_count_steps * time_count_dir;
4714 score += time_count_steps * level.score[SC_TIME_BONUS];
4717 local_player->LevelSolved_CountingTime = time;
4718 local_player->LevelSolved_CountingScore = score;
4720 game_panel_controls[GAME_PANEL_TIME].value = time;
4721 game_panel_controls[GAME_PANEL_SCORE].value = score;
4723 DisplayGameControlValues();
4725 DrawGameValue_Time(time);
4726 DrawGameValue_Score(score);
4729 if (time == time_final)
4730 StopSound(SND_GAME_LEVELTIME_BONUS);
4731 else if (setup.sound_loops)
4732 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4734 PlaySound(SND_GAME_LEVELTIME_BONUS);
4739 local_player->LevelSolved_PanelOff = TRUE;
4741 if (game_over_delay_2 > 0)
4743 game_over_delay_2--;
4756 boolean raise_level = FALSE;
4758 local_player->LevelSolved_GameEnd = TRUE;
4760 CloseDoor(DOOR_CLOSE_1);
4762 if (local_player->LevelSolved_SaveTape)
4769 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4771 SaveTape(tape.level_nr); /* ask to save tape */
4775 if (level_editor_test_game)
4777 game_status = GAME_MODE_MAIN;
4780 DrawAndFadeInMainMenu(REDRAW_FIELD);
4788 if (!local_player->LevelSolved_SaveScore)
4791 FadeOut(REDRAW_FIELD);
4794 game_status = GAME_MODE_MAIN;
4796 DrawAndFadeInMainMenu(REDRAW_FIELD);
4801 if (level_nr == leveldir_current->handicap_level)
4803 leveldir_current->handicap_level++;
4804 SaveLevelSetup_SeriesInfo();
4807 if (level_nr < leveldir_current->last_level)
4808 raise_level = TRUE; /* advance to next level */
4810 if ((hi_pos = NewHiScore()) >= 0)
4812 game_status = GAME_MODE_SCORES;
4814 DrawHallOfFame(hi_pos);
4825 FadeOut(REDRAW_FIELD);
4828 game_status = GAME_MODE_MAIN;
4836 DrawAndFadeInMainMenu(REDRAW_FIELD);
4845 LoadScore(level_nr);
4847 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4848 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4851 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4853 if (local_player->score_final > highscore[k].Score)
4855 /* player has made it to the hall of fame */
4857 if (k < MAX_SCORE_ENTRIES - 1)
4859 int m = MAX_SCORE_ENTRIES - 1;
4862 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4863 if (strEqual(setup.player_name, highscore[l].Name))
4865 if (m == k) /* player's new highscore overwrites his old one */
4869 for (l = m; l > k; l--)
4871 strcpy(highscore[l].Name, highscore[l - 1].Name);
4872 highscore[l].Score = highscore[l - 1].Score;
4879 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4880 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4881 highscore[k].Score = local_player->score_final;
4887 else if (!strncmp(setup.player_name, highscore[k].Name,
4888 MAX_PLAYER_NAME_LEN))
4889 break; /* player already there with a higher score */
4895 SaveScore(level_nr);
4900 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4902 int element = Feld[x][y];
4903 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4904 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4905 int horiz_move = (dx != 0);
4906 int sign = (horiz_move ? dx : dy);
4907 int step = sign * element_info[element].move_stepsize;
4909 /* special values for move stepsize for spring and things on conveyor belt */
4912 if (CAN_FALL(element) &&
4913 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4914 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4915 else if (element == EL_SPRING)
4916 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4922 inline static int getElementMoveStepsize(int x, int y)
4924 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4927 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4929 if (player->GfxAction != action || player->GfxDir != dir)
4932 printf("Player frame reset! (%d => %d, %d => %d)\n",
4933 player->GfxAction, action, player->GfxDir, dir);
4936 player->GfxAction = action;
4937 player->GfxDir = dir;
4939 player->StepFrame = 0;
4943 #if USE_GFX_RESET_GFX_ANIMATION
4944 static void ResetGfxFrame(int x, int y, boolean redraw)
4946 int element = Feld[x][y];
4947 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4948 int last_gfx_frame = GfxFrame[x][y];
4950 if (graphic_info[graphic].anim_global_sync)
4951 GfxFrame[x][y] = FrameCounter;
4952 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4953 GfxFrame[x][y] = CustomValue[x][y];
4954 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4955 GfxFrame[x][y] = element_info[element].collect_score;
4956 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4957 GfxFrame[x][y] = ChangeDelay[x][y];
4959 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4960 DrawLevelGraphicAnimation(x, y, graphic);
4964 static void ResetGfxAnimation(int x, int y)
4966 GfxAction[x][y] = ACTION_DEFAULT;
4967 GfxDir[x][y] = MovDir[x][y];
4970 #if USE_GFX_RESET_GFX_ANIMATION
4971 ResetGfxFrame(x, y, FALSE);
4975 static void ResetRandomAnimationValue(int x, int y)
4977 GfxRandom[x][y] = INIT_GFX_RANDOM();
4980 void InitMovingField(int x, int y, int direction)
4982 int element = Feld[x][y];
4983 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4984 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4987 boolean is_moving_before, is_moving_after;
4989 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4992 /* check if element was/is moving or being moved before/after mode change */
4995 is_moving_before = (WasJustMoving[x][y] != 0);
4997 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4998 is_moving_before = WasJustMoving[x][y];
5001 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5003 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5005 /* reset animation only for moving elements which change direction of moving
5006 or which just started or stopped moving
5007 (else CEs with property "can move" / "not moving" are reset each frame) */
5008 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5010 if (is_moving_before != is_moving_after ||
5011 direction != MovDir[x][y])
5012 ResetGfxAnimation(x, y);
5014 if ((is_moving_before || is_moving_after) && !continues_moving)
5015 ResetGfxAnimation(x, y);
5018 if (!continues_moving)
5019 ResetGfxAnimation(x, y);
5022 MovDir[x][y] = direction;
5023 GfxDir[x][y] = direction;
5025 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5026 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5027 direction == MV_DOWN && CAN_FALL(element) ?
5028 ACTION_FALLING : ACTION_MOVING);
5030 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5031 ACTION_FALLING : ACTION_MOVING);
5034 /* this is needed for CEs with property "can move" / "not moving" */
5036 if (is_moving_after)
5038 if (Feld[newx][newy] == EL_EMPTY)
5039 Feld[newx][newy] = EL_BLOCKED;
5041 MovDir[newx][newy] = MovDir[x][y];
5043 #if USE_NEW_CUSTOM_VALUE
5044 CustomValue[newx][newy] = CustomValue[x][y];
5047 GfxFrame[newx][newy] = GfxFrame[x][y];
5048 GfxRandom[newx][newy] = GfxRandom[x][y];
5049 GfxAction[newx][newy] = GfxAction[x][y];
5050 GfxDir[newx][newy] = GfxDir[x][y];
5054 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5056 int direction = MovDir[x][y];
5057 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5058 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5064 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5066 int oldx = x, oldy = y;
5067 int direction = MovDir[x][y];
5069 if (direction == MV_LEFT)
5071 else if (direction == MV_RIGHT)
5073 else if (direction == MV_UP)
5075 else if (direction == MV_DOWN)
5078 *comes_from_x = oldx;
5079 *comes_from_y = oldy;
5082 int MovingOrBlocked2Element(int x, int y)
5084 int element = Feld[x][y];
5086 if (element == EL_BLOCKED)
5090 Blocked2Moving(x, y, &oldx, &oldy);
5091 return Feld[oldx][oldy];
5097 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5099 /* like MovingOrBlocked2Element(), but if element is moving
5100 and (x,y) is the field the moving element is just leaving,
5101 return EL_BLOCKED instead of the element value */
5102 int element = Feld[x][y];
5104 if (IS_MOVING(x, y))
5106 if (element == EL_BLOCKED)
5110 Blocked2Moving(x, y, &oldx, &oldy);
5111 return Feld[oldx][oldy];
5120 static void RemoveField(int x, int y)
5122 Feld[x][y] = EL_EMPTY;
5128 #if USE_NEW_CUSTOM_VALUE
5129 CustomValue[x][y] = 0;
5133 ChangeDelay[x][y] = 0;
5134 ChangePage[x][y] = -1;
5135 Pushed[x][y] = FALSE;
5138 ExplodeField[x][y] = EX_TYPE_NONE;
5141 GfxElement[x][y] = EL_UNDEFINED;
5142 GfxAction[x][y] = ACTION_DEFAULT;
5143 GfxDir[x][y] = MV_NONE;
5145 /* !!! this would prevent the removed tile from being redrawn !!! */
5146 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5150 void RemoveMovingField(int x, int y)
5152 int oldx = x, oldy = y, newx = x, newy = y;
5153 int element = Feld[x][y];
5154 int next_element = EL_UNDEFINED;
5156 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5159 if (IS_MOVING(x, y))
5161 Moving2Blocked(x, y, &newx, &newy);
5163 if (Feld[newx][newy] != EL_BLOCKED)
5165 /* element is moving, but target field is not free (blocked), but
5166 already occupied by something different (example: acid pool);
5167 in this case, only remove the moving field, but not the target */
5169 RemoveField(oldx, oldy);
5171 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5173 TEST_DrawLevelField(oldx, oldy);
5178 else if (element == EL_BLOCKED)
5180 Blocked2Moving(x, y, &oldx, &oldy);
5181 if (!IS_MOVING(oldx, oldy))
5185 if (element == EL_BLOCKED &&
5186 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5187 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5188 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5189 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5190 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5191 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5192 next_element = get_next_element(Feld[oldx][oldy]);
5194 RemoveField(oldx, oldy);
5195 RemoveField(newx, newy);
5197 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5199 if (next_element != EL_UNDEFINED)
5200 Feld[oldx][oldy] = next_element;
5202 TEST_DrawLevelField(oldx, oldy);
5203 TEST_DrawLevelField(newx, newy);
5206 void DrawDynamite(int x, int y)
5208 int sx = SCREENX(x), sy = SCREENY(y);
5209 int graphic = el2img(Feld[x][y]);
5212 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5215 if (IS_WALKABLE_INSIDE(Back[x][y]))
5219 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5220 else if (Store[x][y])
5221 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5223 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5225 if (Back[x][y] || Store[x][y])
5226 DrawGraphicThruMask(sx, sy, graphic, frame);
5228 DrawGraphic(sx, sy, graphic, frame);
5231 void CheckDynamite(int x, int y)
5233 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5237 if (MovDelay[x][y] != 0)
5240 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5246 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5251 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5253 boolean num_checked_players = 0;
5256 for (i = 0; i < MAX_PLAYERS; i++)
5258 if (stored_player[i].active)
5260 int sx = stored_player[i].jx;
5261 int sy = stored_player[i].jy;
5263 if (num_checked_players == 0)
5270 *sx1 = MIN(*sx1, sx);
5271 *sy1 = MIN(*sy1, sy);
5272 *sx2 = MAX(*sx2, sx);
5273 *sy2 = MAX(*sy2, sy);
5276 num_checked_players++;
5281 static boolean checkIfAllPlayersFitToScreen_RND()
5283 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5285 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5287 return (sx2 - sx1 < SCR_FIELDX &&
5288 sy2 - sy1 < SCR_FIELDY);
5291 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5293 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5295 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5297 *sx = (sx1 + sx2) / 2;
5298 *sy = (sy1 + sy2) / 2;
5301 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5302 boolean center_screen, boolean quick_relocation)
5304 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5305 boolean no_delay = (tape.warp_forward);
5306 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5307 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5309 if (quick_relocation)
5311 int offset = game.scroll_delay_value;
5313 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5315 if (!level.shifted_relocation || center_screen)
5317 /* quick relocation (without scrolling), with centering of screen */
5319 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5320 x > SBX_Right + MIDPOSX ? SBX_Right :
5323 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5324 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5329 /* quick relocation (without scrolling), but do not center screen */
5331 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5332 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5335 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5336 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5339 int offset_x = x + (scroll_x - center_scroll_x);
5340 int offset_y = y + (scroll_y - center_scroll_y);
5342 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5343 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5344 offset_x - MIDPOSX);
5346 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5347 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5348 offset_y - MIDPOSY);
5353 /* quick relocation (without scrolling), inside visible screen area */
5355 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5356 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5357 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5359 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5360 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5361 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5363 /* don't scroll over playfield boundaries */
5364 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5365 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5367 /* don't scroll over playfield boundaries */
5368 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5369 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5372 RedrawPlayfield(TRUE, 0,0,0,0);
5377 int scroll_xx, scroll_yy;
5379 if (!level.shifted_relocation || center_screen)
5381 /* visible relocation (with scrolling), with centering of screen */
5383 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5384 x > SBX_Right + MIDPOSX ? SBX_Right :
5387 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5388 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5393 /* visible relocation (with scrolling), but do not center screen */
5395 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5396 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5399 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5400 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5403 int offset_x = x + (scroll_x - center_scroll_x);
5404 int offset_y = y + (scroll_y - center_scroll_y);
5406 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5407 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5408 offset_x - MIDPOSX);
5410 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5411 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5412 offset_y - MIDPOSY);
5417 /* visible relocation (with scrolling), with centering of screen */
5419 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5420 x > SBX_Right + MIDPOSX ? SBX_Right :
5423 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5424 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5428 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5430 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5433 int fx = FX, fy = FY;
5435 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5436 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5438 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5444 fx += dx * TILEX / 2;
5445 fy += dy * TILEY / 2;
5447 ScrollLevel(dx, dy);
5450 /* scroll in two steps of half tile size to make things smoother */
5451 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5453 Delay(wait_delay_value);
5455 /* scroll second step to align at full tile size */
5457 Delay(wait_delay_value);
5462 Delay(wait_delay_value);
5466 void RelocatePlayer(int jx, int jy, int el_player_raw)
5468 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5469 int player_nr = GET_PLAYER_NR(el_player);
5470 struct PlayerInfo *player = &stored_player[player_nr];
5471 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5472 boolean no_delay = (tape.warp_forward);
5473 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5474 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5475 int old_jx = player->jx;
5476 int old_jy = player->jy;
5477 int old_element = Feld[old_jx][old_jy];
5478 int element = Feld[jx][jy];
5479 boolean player_relocated = (old_jx != jx || old_jy != jy);
5481 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5482 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5483 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5484 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5485 int leave_side_horiz = move_dir_horiz;
5486 int leave_side_vert = move_dir_vert;
5487 int enter_side = enter_side_horiz | enter_side_vert;
5488 int leave_side = leave_side_horiz | leave_side_vert;
5490 if (player->GameOver) /* do not reanimate dead player */
5493 if (!player_relocated) /* no need to relocate the player */
5496 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5498 RemoveField(jx, jy); /* temporarily remove newly placed player */
5499 DrawLevelField(jx, jy);
5502 if (player->present)
5504 while (player->MovPos)
5506 ScrollPlayer(player, SCROLL_GO_ON);
5507 ScrollScreen(NULL, SCROLL_GO_ON);
5509 AdvanceFrameAndPlayerCounters(player->index_nr);
5514 Delay(wait_delay_value);
5517 DrawPlayer(player); /* needed here only to cleanup last field */
5518 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5520 player->is_moving = FALSE;
5523 if (IS_CUSTOM_ELEMENT(old_element))
5524 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5526 player->index_bit, leave_side);
5528 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5530 player->index_bit, leave_side);
5532 Feld[jx][jy] = el_player;
5533 InitPlayerField(jx, jy, el_player, TRUE);
5535 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5537 Feld[jx][jy] = element;
5538 InitField(jx, jy, FALSE);
5541 /* only visually relocate centered player */
5542 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5543 FALSE, level.instant_relocation);
5545 TestIfPlayerTouchesBadThing(jx, jy);
5546 TestIfPlayerTouchesCustomElement(jx, jy);
5548 if (IS_CUSTOM_ELEMENT(element))
5549 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5550 player->index_bit, enter_side);
5552 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5553 player->index_bit, enter_side);
5556 void Explode(int ex, int ey, int phase, int mode)
5562 /* !!! eliminate this variable !!! */
5563 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5565 if (game.explosions_delayed)
5567 ExplodeField[ex][ey] = mode;
5571 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5573 int center_element = Feld[ex][ey];
5574 int artwork_element, explosion_element; /* set these values later */
5577 /* --- This is only really needed (and now handled) in "Impact()". --- */
5578 /* do not explode moving elements that left the explode field in time */
5579 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5580 center_element == EL_EMPTY &&
5581 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5586 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5587 if (mode == EX_TYPE_NORMAL ||
5588 mode == EX_TYPE_CENTER ||
5589 mode == EX_TYPE_CROSS)
5590 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5593 /* remove things displayed in background while burning dynamite */
5594 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5597 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5599 /* put moving element to center field (and let it explode there) */
5600 center_element = MovingOrBlocked2Element(ex, ey);
5601 RemoveMovingField(ex, ey);
5602 Feld[ex][ey] = center_element;
5605 /* now "center_element" is finally determined -- set related values now */
5606 artwork_element = center_element; /* for custom player artwork */
5607 explosion_element = center_element; /* for custom player artwork */
5609 if (IS_PLAYER(ex, ey))
5611 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5613 artwork_element = stored_player[player_nr].artwork_element;
5615 if (level.use_explosion_element[player_nr])
5617 explosion_element = level.explosion_element[player_nr];
5618 artwork_element = explosion_element;
5623 if (mode == EX_TYPE_NORMAL ||
5624 mode == EX_TYPE_CENTER ||
5625 mode == EX_TYPE_CROSS)
5626 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5629 last_phase = element_info[explosion_element].explosion_delay + 1;
5631 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5633 int xx = x - ex + 1;
5634 int yy = y - ey + 1;
5637 if (!IN_LEV_FIELD(x, y) ||
5638 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5639 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5642 element = Feld[x][y];
5644 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5646 element = MovingOrBlocked2Element(x, y);
5648 if (!IS_EXPLOSION_PROOF(element))
5649 RemoveMovingField(x, y);
5652 /* indestructible elements can only explode in center (but not flames) */
5653 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5654 mode == EX_TYPE_BORDER)) ||
5655 element == EL_FLAMES)
5658 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5659 behaviour, for example when touching a yamyam that explodes to rocks
5660 with active deadly shield, a rock is created under the player !!! */
5661 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5663 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5664 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5665 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5667 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5670 if (IS_ACTIVE_BOMB(element))
5672 /* re-activate things under the bomb like gate or penguin */
5673 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5680 /* save walkable background elements while explosion on same tile */
5681 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5682 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5683 Back[x][y] = element;
5685 /* ignite explodable elements reached by other explosion */
5686 if (element == EL_EXPLOSION)
5687 element = Store2[x][y];
5689 if (AmoebaNr[x][y] &&
5690 (element == EL_AMOEBA_FULL ||
5691 element == EL_BD_AMOEBA ||
5692 element == EL_AMOEBA_GROWING))
5694 AmoebaCnt[AmoebaNr[x][y]]--;
5695 AmoebaCnt2[AmoebaNr[x][y]]--;
5700 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5702 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5704 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5706 if (PLAYERINFO(ex, ey)->use_murphy)
5707 Store[x][y] = EL_EMPTY;
5710 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5711 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5712 else if (ELEM_IS_PLAYER(center_element))
5713 Store[x][y] = EL_EMPTY;
5714 else if (center_element == EL_YAMYAM)
5715 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5716 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5717 Store[x][y] = element_info[center_element].content.e[xx][yy];
5719 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5720 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5721 otherwise) -- FIX THIS !!! */
5722 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5723 Store[x][y] = element_info[element].content.e[1][1];
5725 else if (!CAN_EXPLODE(element))
5726 Store[x][y] = element_info[element].content.e[1][1];
5729 Store[x][y] = EL_EMPTY;
5731 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5732 center_element == EL_AMOEBA_TO_DIAMOND)
5733 Store2[x][y] = element;
5735 Feld[x][y] = EL_EXPLOSION;
5736 GfxElement[x][y] = artwork_element;
5738 ExplodePhase[x][y] = 1;
5739 ExplodeDelay[x][y] = last_phase;
5744 if (center_element == EL_YAMYAM)
5745 game.yamyam_content_nr =
5746 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5758 GfxFrame[x][y] = 0; /* restart explosion animation */
5760 last_phase = ExplodeDelay[x][y];
5762 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5766 /* activate this even in non-DEBUG version until cause for crash in
5767 getGraphicAnimationFrame() (see below) is found and eliminated */
5773 /* this can happen if the player leaves an explosion just in time */
5774 if (GfxElement[x][y] == EL_UNDEFINED)
5775 GfxElement[x][y] = EL_EMPTY;
5777 if (GfxElement[x][y] == EL_UNDEFINED)
5780 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5781 printf("Explode(): This should never happen!\n");
5784 GfxElement[x][y] = EL_EMPTY;
5790 border_element = Store2[x][y];
5791 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5792 border_element = StorePlayer[x][y];
5794 if (phase == element_info[border_element].ignition_delay ||
5795 phase == last_phase)
5797 boolean border_explosion = FALSE;
5799 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5800 !PLAYER_EXPLOSION_PROTECTED(x, y))
5802 KillPlayerUnlessExplosionProtected(x, y);
5803 border_explosion = TRUE;
5805 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5807 Feld[x][y] = Store2[x][y];
5810 border_explosion = TRUE;
5812 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5814 AmoebeUmwandeln(x, y);
5816 border_explosion = TRUE;
5819 /* if an element just explodes due to another explosion (chain-reaction),
5820 do not immediately end the new explosion when it was the last frame of
5821 the explosion (as it would be done in the following "if"-statement!) */
5822 if (border_explosion && phase == last_phase)
5826 if (phase == last_phase)
5830 element = Feld[x][y] = Store[x][y];
5831 Store[x][y] = Store2[x][y] = 0;
5832 GfxElement[x][y] = EL_UNDEFINED;
5834 /* player can escape from explosions and might therefore be still alive */
5835 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5836 element <= EL_PLAYER_IS_EXPLODING_4)
5838 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5839 int explosion_element = EL_PLAYER_1 + player_nr;
5840 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5841 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5843 if (level.use_explosion_element[player_nr])
5844 explosion_element = level.explosion_element[player_nr];
5846 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5847 element_info[explosion_element].content.e[xx][yy]);
5850 /* restore probably existing indestructible background element */
5851 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5852 element = Feld[x][y] = Back[x][y];
5855 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5856 GfxDir[x][y] = MV_NONE;
5857 ChangeDelay[x][y] = 0;
5858 ChangePage[x][y] = -1;
5860 #if USE_NEW_CUSTOM_VALUE
5861 CustomValue[x][y] = 0;
5864 InitField_WithBug2(x, y, FALSE);
5866 TEST_DrawLevelField(x, y);
5868 TestIfElementTouchesCustomElement(x, y);
5870 if (GFX_CRUMBLED(element))
5871 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5873 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5874 StorePlayer[x][y] = 0;
5876 if (ELEM_IS_PLAYER(element))
5877 RelocatePlayer(x, y, element);
5879 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5881 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5882 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5885 TEST_DrawLevelFieldCrumbledSand(x, y);
5887 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5889 DrawLevelElement(x, y, Back[x][y]);
5890 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5892 else if (IS_WALKABLE_UNDER(Back[x][y]))
5894 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5895 DrawLevelElementThruMask(x, y, Back[x][y]);
5897 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5898 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5902 void DynaExplode(int ex, int ey)
5905 int dynabomb_element = Feld[ex][ey];
5906 int dynabomb_size = 1;
5907 boolean dynabomb_xl = FALSE;
5908 struct PlayerInfo *player;
5909 static int xy[4][2] =
5917 if (IS_ACTIVE_BOMB(dynabomb_element))
5919 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5920 dynabomb_size = player->dynabomb_size;
5921 dynabomb_xl = player->dynabomb_xl;
5922 player->dynabombs_left++;
5925 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5927 for (i = 0; i < NUM_DIRECTIONS; i++)
5929 for (j = 1; j <= dynabomb_size; j++)
5931 int x = ex + j * xy[i][0];
5932 int y = ey + j * xy[i][1];
5935 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5938 element = Feld[x][y];
5940 /* do not restart explosions of fields with active bombs */
5941 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5944 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5946 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5947 !IS_DIGGABLE(element) && !dynabomb_xl)
5953 void Bang(int x, int y)
5955 int element = MovingOrBlocked2Element(x, y);
5956 int explosion_type = EX_TYPE_NORMAL;
5958 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5960 struct PlayerInfo *player = PLAYERINFO(x, y);
5962 #if USE_FIX_CE_ACTION_WITH_PLAYER
5963 element = Feld[x][y] = player->initial_element;
5965 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5966 player->element_nr);
5969 if (level.use_explosion_element[player->index_nr])
5971 int explosion_element = level.explosion_element[player->index_nr];
5973 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5974 explosion_type = EX_TYPE_CROSS;
5975 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5976 explosion_type = EX_TYPE_CENTER;
5984 case EL_BD_BUTTERFLY:
5987 case EL_DARK_YAMYAM:
5991 RaiseScoreElement(element);
5994 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5995 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5996 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5997 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5998 case EL_DYNABOMB_INCREASE_NUMBER:
5999 case EL_DYNABOMB_INCREASE_SIZE:
6000 case EL_DYNABOMB_INCREASE_POWER:
6001 explosion_type = EX_TYPE_DYNA;
6004 case EL_DC_LANDMINE:
6006 case EL_EM_EXIT_OPEN:
6007 case EL_EM_STEEL_EXIT_OPEN:
6009 explosion_type = EX_TYPE_CENTER;
6014 case EL_LAMP_ACTIVE:
6015 case EL_AMOEBA_TO_DIAMOND:
6016 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6017 explosion_type = EX_TYPE_CENTER;
6021 if (element_info[element].explosion_type == EXPLODES_CROSS)
6022 explosion_type = EX_TYPE_CROSS;
6023 else if (element_info[element].explosion_type == EXPLODES_1X1)
6024 explosion_type = EX_TYPE_CENTER;
6028 if (explosion_type == EX_TYPE_DYNA)
6031 Explode(x, y, EX_PHASE_START, explosion_type);
6033 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6036 void SplashAcid(int x, int y)
6038 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6039 (!IN_LEV_FIELD(x - 1, y - 2) ||
6040 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6041 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6043 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6044 (!IN_LEV_FIELD(x + 1, y - 2) ||
6045 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6046 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6048 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6051 static void InitBeltMovement()
6053 static int belt_base_element[4] =
6055 EL_CONVEYOR_BELT_1_LEFT,
6056 EL_CONVEYOR_BELT_2_LEFT,
6057 EL_CONVEYOR_BELT_3_LEFT,
6058 EL_CONVEYOR_BELT_4_LEFT
6060 static int belt_base_active_element[4] =
6062 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6063 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6064 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6065 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6070 /* set frame order for belt animation graphic according to belt direction */
6071 for (i = 0; i < NUM_BELTS; i++)
6075 for (j = 0; j < NUM_BELT_PARTS; j++)
6077 int element = belt_base_active_element[belt_nr] + j;
6078 int graphic_1 = el2img(element);
6079 int graphic_2 = el2panelimg(element);
6081 if (game.belt_dir[i] == MV_LEFT)
6083 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6084 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6088 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6089 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6094 SCAN_PLAYFIELD(x, y)
6096 int element = Feld[x][y];
6098 for (i = 0; i < NUM_BELTS; i++)
6100 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6102 int e_belt_nr = getBeltNrFromBeltElement(element);
6105 if (e_belt_nr == belt_nr)
6107 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6109 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6116 static void ToggleBeltSwitch(int x, int y)
6118 static int belt_base_element[4] =
6120 EL_CONVEYOR_BELT_1_LEFT,
6121 EL_CONVEYOR_BELT_2_LEFT,
6122 EL_CONVEYOR_BELT_3_LEFT,
6123 EL_CONVEYOR_BELT_4_LEFT
6125 static int belt_base_active_element[4] =
6127 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6128 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6129 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6130 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6132 static int belt_base_switch_element[4] =
6134 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6135 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6136 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6137 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6139 static int belt_move_dir[4] =
6147 int element = Feld[x][y];
6148 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6149 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6150 int belt_dir = belt_move_dir[belt_dir_nr];
6153 if (!IS_BELT_SWITCH(element))
6156 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6157 game.belt_dir[belt_nr] = belt_dir;
6159 if (belt_dir_nr == 3)
6162 /* set frame order for belt animation graphic according to belt direction */
6163 for (i = 0; i < NUM_BELT_PARTS; i++)
6165 int element = belt_base_active_element[belt_nr] + i;
6166 int graphic_1 = el2img(element);
6167 int graphic_2 = el2panelimg(element);
6169 if (belt_dir == MV_LEFT)
6171 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6172 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6176 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6177 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6181 SCAN_PLAYFIELD(xx, yy)
6183 int element = Feld[xx][yy];
6185 if (IS_BELT_SWITCH(element))
6187 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6189 if (e_belt_nr == belt_nr)
6191 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6192 TEST_DrawLevelField(xx, yy);
6195 else if (IS_BELT(element) && belt_dir != MV_NONE)
6197 int e_belt_nr = getBeltNrFromBeltElement(element);
6199 if (e_belt_nr == belt_nr)
6201 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6203 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6204 TEST_DrawLevelField(xx, yy);
6207 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6209 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6211 if (e_belt_nr == belt_nr)
6213 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6215 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6216 TEST_DrawLevelField(xx, yy);
6222 static void ToggleSwitchgateSwitch(int x, int y)
6226 game.switchgate_pos = !game.switchgate_pos;
6228 SCAN_PLAYFIELD(xx, yy)
6230 int element = Feld[xx][yy];
6232 #if !USE_BOTH_SWITCHGATE_SWITCHES
6233 if (element == EL_SWITCHGATE_SWITCH_UP ||
6234 element == EL_SWITCHGATE_SWITCH_DOWN)
6236 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6237 TEST_DrawLevelField(xx, yy);
6239 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6240 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6242 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6243 TEST_DrawLevelField(xx, yy);
6246 if (element == EL_SWITCHGATE_SWITCH_UP)
6248 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6249 TEST_DrawLevelField(xx, yy);
6251 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6253 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6254 TEST_DrawLevelField(xx, yy);
6256 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6258 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6259 TEST_DrawLevelField(xx, yy);
6261 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6263 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6264 TEST_DrawLevelField(xx, yy);
6267 else if (element == EL_SWITCHGATE_OPEN ||
6268 element == EL_SWITCHGATE_OPENING)
6270 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6272 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6274 else if (element == EL_SWITCHGATE_CLOSED ||
6275 element == EL_SWITCHGATE_CLOSING)
6277 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6279 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6284 static int getInvisibleActiveFromInvisibleElement(int element)
6286 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6287 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6288 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6292 static int getInvisibleFromInvisibleActiveElement(int element)
6294 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6295 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6296 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6300 static void RedrawAllLightSwitchesAndInvisibleElements()
6304 SCAN_PLAYFIELD(x, y)
6306 int element = Feld[x][y];
6308 if (element == EL_LIGHT_SWITCH &&
6309 game.light_time_left > 0)
6311 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6312 TEST_DrawLevelField(x, y);
6314 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6315 game.light_time_left == 0)
6317 Feld[x][y] = EL_LIGHT_SWITCH;
6318 TEST_DrawLevelField(x, y);
6320 else if (element == EL_EMC_DRIPPER &&
6321 game.light_time_left > 0)
6323 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6324 TEST_DrawLevelField(x, y);
6326 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6327 game.light_time_left == 0)
6329 Feld[x][y] = EL_EMC_DRIPPER;
6330 TEST_DrawLevelField(x, y);
6332 else if (element == EL_INVISIBLE_STEELWALL ||
6333 element == EL_INVISIBLE_WALL ||
6334 element == EL_INVISIBLE_SAND)
6336 if (game.light_time_left > 0)
6337 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6339 TEST_DrawLevelField(x, y);
6341 /* uncrumble neighbour fields, if needed */
6342 if (element == EL_INVISIBLE_SAND)
6343 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6345 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6346 element == EL_INVISIBLE_WALL_ACTIVE ||
6347 element == EL_INVISIBLE_SAND_ACTIVE)
6349 if (game.light_time_left == 0)
6350 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6352 TEST_DrawLevelField(x, y);
6354 /* re-crumble neighbour fields, if needed */
6355 if (element == EL_INVISIBLE_SAND)
6356 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6361 static void RedrawAllInvisibleElementsForLenses()
6365 SCAN_PLAYFIELD(x, y)
6367 int element = Feld[x][y];
6369 if (element == EL_EMC_DRIPPER &&
6370 game.lenses_time_left > 0)
6372 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6373 TEST_DrawLevelField(x, y);
6375 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6376 game.lenses_time_left == 0)
6378 Feld[x][y] = EL_EMC_DRIPPER;
6379 TEST_DrawLevelField(x, y);
6381 else if (element == EL_INVISIBLE_STEELWALL ||
6382 element == EL_INVISIBLE_WALL ||
6383 element == EL_INVISIBLE_SAND)
6385 if (game.lenses_time_left > 0)
6386 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6388 TEST_DrawLevelField(x, y);
6390 /* uncrumble neighbour fields, if needed */
6391 if (element == EL_INVISIBLE_SAND)
6392 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6394 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6395 element == EL_INVISIBLE_WALL_ACTIVE ||
6396 element == EL_INVISIBLE_SAND_ACTIVE)
6398 if (game.lenses_time_left == 0)
6399 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6401 TEST_DrawLevelField(x, y);
6403 /* re-crumble neighbour fields, if needed */
6404 if (element == EL_INVISIBLE_SAND)
6405 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6410 static void RedrawAllInvisibleElementsForMagnifier()
6414 SCAN_PLAYFIELD(x, y)
6416 int element = Feld[x][y];
6418 if (element == EL_EMC_FAKE_GRASS &&
6419 game.magnify_time_left > 0)
6421 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6422 TEST_DrawLevelField(x, y);
6424 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6425 game.magnify_time_left == 0)
6427 Feld[x][y] = EL_EMC_FAKE_GRASS;
6428 TEST_DrawLevelField(x, y);
6430 else if (IS_GATE_GRAY(element) &&
6431 game.magnify_time_left > 0)
6433 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6434 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6435 IS_EM_GATE_GRAY(element) ?
6436 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6437 IS_EMC_GATE_GRAY(element) ?
6438 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6439 IS_DC_GATE_GRAY(element) ?
6440 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6442 TEST_DrawLevelField(x, y);
6444 else if (IS_GATE_GRAY_ACTIVE(element) &&
6445 game.magnify_time_left == 0)
6447 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6448 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6449 IS_EM_GATE_GRAY_ACTIVE(element) ?
6450 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6451 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6452 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6453 IS_DC_GATE_GRAY_ACTIVE(element) ?
6454 EL_DC_GATE_WHITE_GRAY :
6456 TEST_DrawLevelField(x, y);
6461 static void ToggleLightSwitch(int x, int y)
6463 int element = Feld[x][y];
6465 game.light_time_left =
6466 (element == EL_LIGHT_SWITCH ?
6467 level.time_light * FRAMES_PER_SECOND : 0);
6469 RedrawAllLightSwitchesAndInvisibleElements();
6472 static void ActivateTimegateSwitch(int x, int y)
6476 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6478 SCAN_PLAYFIELD(xx, yy)
6480 int element = Feld[xx][yy];
6482 if (element == EL_TIMEGATE_CLOSED ||
6483 element == EL_TIMEGATE_CLOSING)
6485 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6486 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6490 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6492 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6493 TEST_DrawLevelField(xx, yy);
6500 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6501 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6503 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6507 void Impact(int x, int y)
6509 boolean last_line = (y == lev_fieldy - 1);
6510 boolean object_hit = FALSE;
6511 boolean impact = (last_line || object_hit);
6512 int element = Feld[x][y];
6513 int smashed = EL_STEELWALL;
6515 if (!last_line) /* check if element below was hit */
6517 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6520 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6521 MovDir[x][y + 1] != MV_DOWN ||
6522 MovPos[x][y + 1] <= TILEY / 2));
6524 /* do not smash moving elements that left the smashed field in time */
6525 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6526 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6529 #if USE_QUICKSAND_IMPACT_BUGFIX
6530 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6532 RemoveMovingField(x, y + 1);
6533 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6534 Feld[x][y + 2] = EL_ROCK;
6535 TEST_DrawLevelField(x, y + 2);
6540 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6542 RemoveMovingField(x, y + 1);
6543 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6544 Feld[x][y + 2] = EL_ROCK;
6545 TEST_DrawLevelField(x, y + 2);
6552 smashed = MovingOrBlocked2Element(x, y + 1);
6554 impact = (last_line || object_hit);
6557 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6559 SplashAcid(x, y + 1);
6563 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6564 /* only reset graphic animation if graphic really changes after impact */
6566 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6568 ResetGfxAnimation(x, y);
6569 TEST_DrawLevelField(x, y);
6572 if (impact && CAN_EXPLODE_IMPACT(element))
6577 else if (impact && element == EL_PEARL &&
6578 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6580 ResetGfxAnimation(x, y);
6582 Feld[x][y] = EL_PEARL_BREAKING;
6583 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6586 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6588 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6593 if (impact && element == EL_AMOEBA_DROP)
6595 if (object_hit && IS_PLAYER(x, y + 1))
6596 KillPlayerUnlessEnemyProtected(x, y + 1);
6597 else if (object_hit && smashed == EL_PENGUIN)
6601 Feld[x][y] = EL_AMOEBA_GROWING;
6602 Store[x][y] = EL_AMOEBA_WET;
6604 ResetRandomAnimationValue(x, y);
6609 if (object_hit) /* check which object was hit */
6611 if ((CAN_PASS_MAGIC_WALL(element) &&
6612 (smashed == EL_MAGIC_WALL ||
6613 smashed == EL_BD_MAGIC_WALL)) ||
6614 (CAN_PASS_DC_MAGIC_WALL(element) &&
6615 smashed == EL_DC_MAGIC_WALL))
6618 int activated_magic_wall =
6619 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6620 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6621 EL_DC_MAGIC_WALL_ACTIVE);
6623 /* activate magic wall / mill */
6624 SCAN_PLAYFIELD(xx, yy)
6626 if (Feld[xx][yy] == smashed)
6627 Feld[xx][yy] = activated_magic_wall;
6630 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6631 game.magic_wall_active = TRUE;
6633 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6634 SND_MAGIC_WALL_ACTIVATING :
6635 smashed == EL_BD_MAGIC_WALL ?
6636 SND_BD_MAGIC_WALL_ACTIVATING :
6637 SND_DC_MAGIC_WALL_ACTIVATING));
6640 if (IS_PLAYER(x, y + 1))
6642 if (CAN_SMASH_PLAYER(element))
6644 KillPlayerUnlessEnemyProtected(x, y + 1);
6648 else if (smashed == EL_PENGUIN)
6650 if (CAN_SMASH_PLAYER(element))
6656 else if (element == EL_BD_DIAMOND)
6658 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6664 else if (((element == EL_SP_INFOTRON ||
6665 element == EL_SP_ZONK) &&
6666 (smashed == EL_SP_SNIKSNAK ||
6667 smashed == EL_SP_ELECTRON ||
6668 smashed == EL_SP_DISK_ORANGE)) ||
6669 (element == EL_SP_INFOTRON &&
6670 smashed == EL_SP_DISK_YELLOW))
6675 else if (CAN_SMASH_EVERYTHING(element))
6677 if (IS_CLASSIC_ENEMY(smashed) ||
6678 CAN_EXPLODE_SMASHED(smashed))
6683 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6685 if (smashed == EL_LAMP ||
6686 smashed == EL_LAMP_ACTIVE)
6691 else if (smashed == EL_NUT)
6693 Feld[x][y + 1] = EL_NUT_BREAKING;
6694 PlayLevelSound(x, y, SND_NUT_BREAKING);
6695 RaiseScoreElement(EL_NUT);
6698 else if (smashed == EL_PEARL)
6700 ResetGfxAnimation(x, y);
6702 Feld[x][y + 1] = EL_PEARL_BREAKING;
6703 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6706 else if (smashed == EL_DIAMOND)
6708 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6709 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6712 else if (IS_BELT_SWITCH(smashed))
6714 ToggleBeltSwitch(x, y + 1);
6716 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6717 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6718 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6719 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6721 ToggleSwitchgateSwitch(x, y + 1);
6723 else if (smashed == EL_LIGHT_SWITCH ||
6724 smashed == EL_LIGHT_SWITCH_ACTIVE)
6726 ToggleLightSwitch(x, y + 1);
6731 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6734 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6736 CheckElementChangeBySide(x, y + 1, smashed, element,
6737 CE_SWITCHED, CH_SIDE_TOP);
6738 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6744 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6749 /* play sound of magic wall / mill */
6751 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6752 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6753 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6755 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6756 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6757 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6758 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6759 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6760 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6765 /* play sound of object that hits the ground */
6766 if (last_line || object_hit)
6767 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6770 inline static void TurnRoundExt(int x, int y)
6782 { 0, 0 }, { 0, 0 }, { 0, 0 },
6787 int left, right, back;
6791 { MV_DOWN, MV_UP, MV_RIGHT },
6792 { MV_UP, MV_DOWN, MV_LEFT },
6794 { MV_LEFT, MV_RIGHT, MV_DOWN },
6798 { MV_RIGHT, MV_LEFT, MV_UP }
6801 int element = Feld[x][y];
6802 int move_pattern = element_info[element].move_pattern;
6804 int old_move_dir = MovDir[x][y];
6805 int left_dir = turn[old_move_dir].left;
6806 int right_dir = turn[old_move_dir].right;
6807 int back_dir = turn[old_move_dir].back;
6809 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6810 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6811 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6812 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6814 int left_x = x + left_dx, left_y = y + left_dy;
6815 int right_x = x + right_dx, right_y = y + right_dy;
6816 int move_x = x + move_dx, move_y = y + move_dy;
6820 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6822 TestIfBadThingTouchesOtherBadThing(x, y);
6824 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6825 MovDir[x][y] = right_dir;
6826 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6827 MovDir[x][y] = left_dir;
6829 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6831 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6834 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6836 TestIfBadThingTouchesOtherBadThing(x, y);
6838 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6839 MovDir[x][y] = left_dir;
6840 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6841 MovDir[x][y] = right_dir;
6843 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6845 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6848 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6850 TestIfBadThingTouchesOtherBadThing(x, y);
6852 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6853 MovDir[x][y] = left_dir;
6854 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6855 MovDir[x][y] = right_dir;
6857 if (MovDir[x][y] != old_move_dir)
6860 else if (element == EL_YAMYAM)
6862 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6863 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6865 if (can_turn_left && can_turn_right)
6866 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6867 else if (can_turn_left)
6868 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6869 else if (can_turn_right)
6870 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6872 MovDir[x][y] = back_dir;
6874 MovDelay[x][y] = 16 + 16 * RND(3);
6876 else if (element == EL_DARK_YAMYAM)
6878 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6880 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6883 if (can_turn_left && can_turn_right)
6884 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6885 else if (can_turn_left)
6886 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6887 else if (can_turn_right)
6888 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6890 MovDir[x][y] = back_dir;
6892 MovDelay[x][y] = 16 + 16 * RND(3);
6894 else if (element == EL_PACMAN)
6896 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6897 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6899 if (can_turn_left && can_turn_right)
6900 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6901 else if (can_turn_left)
6902 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6903 else if (can_turn_right)
6904 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6906 MovDir[x][y] = back_dir;
6908 MovDelay[x][y] = 6 + RND(40);
6910 else if (element == EL_PIG)
6912 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6913 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6914 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6915 boolean should_turn_left, should_turn_right, should_move_on;
6917 int rnd = RND(rnd_value);
6919 should_turn_left = (can_turn_left &&
6921 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6922 y + back_dy + left_dy)));
6923 should_turn_right = (can_turn_right &&
6925 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6926 y + back_dy + right_dy)));
6927 should_move_on = (can_move_on &&
6930 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6931 y + move_dy + left_dy) ||
6932 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6933 y + move_dy + right_dy)));
6935 if (should_turn_left || should_turn_right || should_move_on)
6937 if (should_turn_left && should_turn_right && should_move_on)
6938 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6939 rnd < 2 * rnd_value / 3 ? right_dir :
6941 else if (should_turn_left && should_turn_right)
6942 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6943 else if (should_turn_left && should_move_on)
6944 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6945 else if (should_turn_right && should_move_on)
6946 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6947 else if (should_turn_left)
6948 MovDir[x][y] = left_dir;
6949 else if (should_turn_right)
6950 MovDir[x][y] = right_dir;
6951 else if (should_move_on)
6952 MovDir[x][y] = old_move_dir;
6954 else if (can_move_on && rnd > rnd_value / 8)
6955 MovDir[x][y] = old_move_dir;
6956 else if (can_turn_left && can_turn_right)
6957 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6958 else if (can_turn_left && rnd > rnd_value / 8)
6959 MovDir[x][y] = left_dir;
6960 else if (can_turn_right && rnd > rnd_value/8)
6961 MovDir[x][y] = right_dir;
6963 MovDir[x][y] = back_dir;
6965 xx = x + move_xy[MovDir[x][y]].dx;
6966 yy = y + move_xy[MovDir[x][y]].dy;
6968 if (!IN_LEV_FIELD(xx, yy) ||
6969 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6970 MovDir[x][y] = old_move_dir;
6974 else if (element == EL_DRAGON)
6976 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6977 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6978 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6980 int rnd = RND(rnd_value);
6982 if (can_move_on && rnd > rnd_value / 8)
6983 MovDir[x][y] = old_move_dir;
6984 else if (can_turn_left && can_turn_right)
6985 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6986 else if (can_turn_left && rnd > rnd_value / 8)
6987 MovDir[x][y] = left_dir;
6988 else if (can_turn_right && rnd > rnd_value / 8)
6989 MovDir[x][y] = right_dir;
6991 MovDir[x][y] = back_dir;
6993 xx = x + move_xy[MovDir[x][y]].dx;
6994 yy = y + move_xy[MovDir[x][y]].dy;
6996 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6997 MovDir[x][y] = old_move_dir;
7001 else if (element == EL_MOLE)
7003 boolean can_move_on =
7004 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7005 IS_AMOEBOID(Feld[move_x][move_y]) ||
7006 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7009 boolean can_turn_left =
7010 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7011 IS_AMOEBOID(Feld[left_x][left_y])));
7013 boolean can_turn_right =
7014 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7015 IS_AMOEBOID(Feld[right_x][right_y])));
7017 if (can_turn_left && can_turn_right)
7018 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7019 else if (can_turn_left)
7020 MovDir[x][y] = left_dir;
7022 MovDir[x][y] = right_dir;
7025 if (MovDir[x][y] != old_move_dir)
7028 else if (element == EL_BALLOON)
7030 MovDir[x][y] = game.wind_direction;
7033 else if (element == EL_SPRING)
7035 #if USE_NEW_SPRING_BUMPER
7036 if (MovDir[x][y] & MV_HORIZONTAL)
7038 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7039 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7041 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7042 ResetGfxAnimation(move_x, move_y);
7043 TEST_DrawLevelField(move_x, move_y);
7045 MovDir[x][y] = back_dir;
7047 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7048 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7049 MovDir[x][y] = MV_NONE;
7052 if (MovDir[x][y] & MV_HORIZONTAL &&
7053 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7054 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7055 MovDir[x][y] = MV_NONE;
7060 else if (element == EL_ROBOT ||
7061 element == EL_SATELLITE ||
7062 element == EL_PENGUIN ||
7063 element == EL_EMC_ANDROID)
7065 int attr_x = -1, attr_y = -1;
7076 for (i = 0; i < MAX_PLAYERS; i++)
7078 struct PlayerInfo *player = &stored_player[i];
7079 int jx = player->jx, jy = player->jy;
7081 if (!player->active)
7085 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7093 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7094 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7095 game.engine_version < VERSION_IDENT(3,1,0,0)))
7101 if (element == EL_PENGUIN)
7104 static int xy[4][2] =
7112 for (i = 0; i < NUM_DIRECTIONS; i++)
7114 int ex = x + xy[i][0];
7115 int ey = y + xy[i][1];
7117 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7118 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7119 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7120 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7129 MovDir[x][y] = MV_NONE;
7131 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7132 else if (attr_x > x)
7133 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7135 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7136 else if (attr_y > y)
7137 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7139 if (element == EL_ROBOT)
7143 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7144 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7145 Moving2Blocked(x, y, &newx, &newy);
7147 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7148 MovDelay[x][y] = 8 + 8 * !RND(3);
7150 MovDelay[x][y] = 16;
7152 else if (element == EL_PENGUIN)
7158 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7160 boolean first_horiz = RND(2);
7161 int new_move_dir = MovDir[x][y];
7164 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7165 Moving2Blocked(x, y, &newx, &newy);
7167 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7171 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7172 Moving2Blocked(x, y, &newx, &newy);
7174 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7177 MovDir[x][y] = old_move_dir;
7181 else if (element == EL_SATELLITE)
7187 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7189 boolean first_horiz = RND(2);
7190 int new_move_dir = MovDir[x][y];
7193 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7194 Moving2Blocked(x, y, &newx, &newy);
7196 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7200 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7201 Moving2Blocked(x, y, &newx, &newy);
7203 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7206 MovDir[x][y] = old_move_dir;
7210 else if (element == EL_EMC_ANDROID)
7212 static int check_pos[16] =
7214 -1, /* 0 => (invalid) */
7215 7, /* 1 => MV_LEFT */
7216 3, /* 2 => MV_RIGHT */
7217 -1, /* 3 => (invalid) */
7219 0, /* 5 => MV_LEFT | MV_UP */
7220 2, /* 6 => MV_RIGHT | MV_UP */
7221 -1, /* 7 => (invalid) */
7222 5, /* 8 => MV_DOWN */
7223 6, /* 9 => MV_LEFT | MV_DOWN */
7224 4, /* 10 => MV_RIGHT | MV_DOWN */
7225 -1, /* 11 => (invalid) */
7226 -1, /* 12 => (invalid) */
7227 -1, /* 13 => (invalid) */
7228 -1, /* 14 => (invalid) */
7229 -1, /* 15 => (invalid) */
7237 { -1, -1, MV_LEFT | MV_UP },
7239 { +1, -1, MV_RIGHT | MV_UP },
7240 { +1, 0, MV_RIGHT },
7241 { +1, +1, MV_RIGHT | MV_DOWN },
7243 { -1, +1, MV_LEFT | MV_DOWN },
7246 int start_pos, check_order;
7247 boolean can_clone = FALSE;
7250 /* check if there is any free field around current position */
7251 for (i = 0; i < 8; i++)
7253 int newx = x + check_xy[i].dx;
7254 int newy = y + check_xy[i].dy;
7256 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7264 if (can_clone) /* randomly find an element to clone */
7268 start_pos = check_pos[RND(8)];
7269 check_order = (RND(2) ? -1 : +1);
7271 for (i = 0; i < 8; i++)
7273 int pos_raw = start_pos + i * check_order;
7274 int pos = (pos_raw + 8) % 8;
7275 int newx = x + check_xy[pos].dx;
7276 int newy = y + check_xy[pos].dy;
7278 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7280 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7281 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7283 Store[x][y] = Feld[newx][newy];
7292 if (can_clone) /* randomly find a direction to move */
7296 start_pos = check_pos[RND(8)];
7297 check_order = (RND(2) ? -1 : +1);
7299 for (i = 0; i < 8; i++)
7301 int pos_raw = start_pos + i * check_order;
7302 int pos = (pos_raw + 8) % 8;
7303 int newx = x + check_xy[pos].dx;
7304 int newy = y + check_xy[pos].dy;
7305 int new_move_dir = check_xy[pos].dir;
7307 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7309 MovDir[x][y] = new_move_dir;
7310 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7319 if (can_clone) /* cloning and moving successful */
7322 /* cannot clone -- try to move towards player */
7324 start_pos = check_pos[MovDir[x][y] & 0x0f];
7325 check_order = (RND(2) ? -1 : +1);
7327 for (i = 0; i < 3; i++)
7329 /* first check start_pos, then previous/next or (next/previous) pos */
7330 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7331 int pos = (pos_raw + 8) % 8;
7332 int newx = x + check_xy[pos].dx;
7333 int newy = y + check_xy[pos].dy;
7334 int new_move_dir = check_xy[pos].dir;
7336 if (IS_PLAYER(newx, newy))
7339 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7341 MovDir[x][y] = new_move_dir;
7342 MovDelay[x][y] = level.android_move_time * 8 + 1;
7349 else if (move_pattern == MV_TURNING_LEFT ||
7350 move_pattern == MV_TURNING_RIGHT ||
7351 move_pattern == MV_TURNING_LEFT_RIGHT ||
7352 move_pattern == MV_TURNING_RIGHT_LEFT ||
7353 move_pattern == MV_TURNING_RANDOM ||
7354 move_pattern == MV_ALL_DIRECTIONS)
7356 boolean can_turn_left =
7357 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7358 boolean can_turn_right =
7359 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7361 if (element_info[element].move_stepsize == 0) /* "not moving" */
7364 if (move_pattern == MV_TURNING_LEFT)
7365 MovDir[x][y] = left_dir;
7366 else if (move_pattern == MV_TURNING_RIGHT)
7367 MovDir[x][y] = right_dir;
7368 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7369 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7370 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7371 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7372 else if (move_pattern == MV_TURNING_RANDOM)
7373 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7374 can_turn_right && !can_turn_left ? right_dir :
7375 RND(2) ? left_dir : right_dir);
7376 else if (can_turn_left && can_turn_right)
7377 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7378 else if (can_turn_left)
7379 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7380 else if (can_turn_right)
7381 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7383 MovDir[x][y] = back_dir;
7385 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7387 else if (move_pattern == MV_HORIZONTAL ||
7388 move_pattern == MV_VERTICAL)
7390 if (move_pattern & old_move_dir)
7391 MovDir[x][y] = back_dir;
7392 else if (move_pattern == MV_HORIZONTAL)
7393 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7394 else if (move_pattern == MV_VERTICAL)
7395 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7397 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7399 else if (move_pattern & MV_ANY_DIRECTION)
7401 MovDir[x][y] = move_pattern;
7402 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7404 else if (move_pattern & MV_WIND_DIRECTION)
7406 MovDir[x][y] = game.wind_direction;
7407 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7409 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7411 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7412 MovDir[x][y] = left_dir;
7413 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7414 MovDir[x][y] = right_dir;
7416 if (MovDir[x][y] != old_move_dir)
7417 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7419 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7421 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7422 MovDir[x][y] = right_dir;
7423 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7424 MovDir[x][y] = left_dir;
7426 if (MovDir[x][y] != old_move_dir)
7427 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7429 else if (move_pattern == MV_TOWARDS_PLAYER ||
7430 move_pattern == MV_AWAY_FROM_PLAYER)
7432 int attr_x = -1, attr_y = -1;
7434 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7445 for (i = 0; i < MAX_PLAYERS; i++)
7447 struct PlayerInfo *player = &stored_player[i];
7448 int jx = player->jx, jy = player->jy;
7450 if (!player->active)
7454 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7462 MovDir[x][y] = MV_NONE;
7464 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7465 else if (attr_x > x)
7466 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7468 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7469 else if (attr_y > y)
7470 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7472 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7474 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7476 boolean first_horiz = RND(2);
7477 int new_move_dir = MovDir[x][y];
7479 if (element_info[element].move_stepsize == 0) /* "not moving" */
7481 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7482 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7488 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489 Moving2Blocked(x, y, &newx, &newy);
7491 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7495 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7496 Moving2Blocked(x, y, &newx, &newy);
7498 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7501 MovDir[x][y] = old_move_dir;
7504 else if (move_pattern == MV_WHEN_PUSHED ||
7505 move_pattern == MV_WHEN_DROPPED)
7507 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7508 MovDir[x][y] = MV_NONE;
7512 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7514 static int test_xy[7][2] =
7524 static int test_dir[7] =
7534 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7535 int move_preference = -1000000; /* start with very low preference */
7536 int new_move_dir = MV_NONE;
7537 int start_test = RND(4);
7540 for (i = 0; i < NUM_DIRECTIONS; i++)
7542 int move_dir = test_dir[start_test + i];
7543 int move_dir_preference;
7545 xx = x + test_xy[start_test + i][0];
7546 yy = y + test_xy[start_test + i][1];
7548 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7549 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7551 new_move_dir = move_dir;
7556 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7559 move_dir_preference = -1 * RunnerVisit[xx][yy];
7560 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7561 move_dir_preference = PlayerVisit[xx][yy];
7563 if (move_dir_preference > move_preference)
7565 /* prefer field that has not been visited for the longest time */
7566 move_preference = move_dir_preference;
7567 new_move_dir = move_dir;
7569 else if (move_dir_preference == move_preference &&
7570 move_dir == old_move_dir)
7572 /* prefer last direction when all directions are preferred equally */
7573 move_preference = move_dir_preference;
7574 new_move_dir = move_dir;
7578 MovDir[x][y] = new_move_dir;
7579 if (old_move_dir != new_move_dir)
7580 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7584 static void TurnRound(int x, int y)
7586 int direction = MovDir[x][y];
7590 GfxDir[x][y] = MovDir[x][y];
7592 if (direction != MovDir[x][y])
7596 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7598 ResetGfxFrame(x, y, FALSE);
7601 static boolean JustBeingPushed(int x, int y)
7605 for (i = 0; i < MAX_PLAYERS; i++)
7607 struct PlayerInfo *player = &stored_player[i];
7609 if (player->active && player->is_pushing && player->MovPos)
7611 int next_jx = player->jx + (player->jx - player->last_jx);
7612 int next_jy = player->jy + (player->jy - player->last_jy);
7614 if (x == next_jx && y == next_jy)
7622 void StartMoving(int x, int y)
7624 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7625 int element = Feld[x][y];
7630 if (MovDelay[x][y] == 0)
7631 GfxAction[x][y] = ACTION_DEFAULT;
7633 if (CAN_FALL(element) && y < lev_fieldy - 1)
7635 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7636 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7637 if (JustBeingPushed(x, y))
7640 if (element == EL_QUICKSAND_FULL)
7642 if (IS_FREE(x, y + 1))
7644 InitMovingField(x, y, MV_DOWN);
7645 started_moving = TRUE;
7647 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7648 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7649 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7650 Store[x][y] = EL_ROCK;
7652 Store[x][y] = EL_ROCK;
7655 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7657 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7659 if (!MovDelay[x][y])
7661 MovDelay[x][y] = TILEY + 1;
7663 ResetGfxAnimation(x, y);
7664 ResetGfxAnimation(x, y + 1);
7669 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7670 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7677 Feld[x][y] = EL_QUICKSAND_EMPTY;
7678 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7679 Store[x][y + 1] = Store[x][y];
7682 PlayLevelSoundAction(x, y, ACTION_FILLING);
7684 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7686 if (!MovDelay[x][y])
7688 MovDelay[x][y] = TILEY + 1;
7690 ResetGfxAnimation(x, y);
7691 ResetGfxAnimation(x, y + 1);
7696 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7697 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7704 Feld[x][y] = EL_QUICKSAND_EMPTY;
7705 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7706 Store[x][y + 1] = Store[x][y];
7709 PlayLevelSoundAction(x, y, ACTION_FILLING);
7712 else if (element == EL_QUICKSAND_FAST_FULL)
7714 if (IS_FREE(x, y + 1))
7716 InitMovingField(x, y, MV_DOWN);
7717 started_moving = TRUE;
7719 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7720 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7721 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7722 Store[x][y] = EL_ROCK;
7724 Store[x][y] = EL_ROCK;
7727 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7729 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7731 if (!MovDelay[x][y])
7733 MovDelay[x][y] = TILEY + 1;
7735 ResetGfxAnimation(x, y);
7736 ResetGfxAnimation(x, y + 1);
7741 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7742 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7749 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7750 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7751 Store[x][y + 1] = Store[x][y];
7754 PlayLevelSoundAction(x, y, ACTION_FILLING);
7756 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7758 if (!MovDelay[x][y])
7760 MovDelay[x][y] = TILEY + 1;
7762 ResetGfxAnimation(x, y);
7763 ResetGfxAnimation(x, y + 1);
7768 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7769 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7776 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7777 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7778 Store[x][y + 1] = Store[x][y];
7781 PlayLevelSoundAction(x, y, ACTION_FILLING);
7784 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7785 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7787 InitMovingField(x, y, MV_DOWN);
7788 started_moving = TRUE;
7790 Feld[x][y] = EL_QUICKSAND_FILLING;
7791 Store[x][y] = element;
7793 PlayLevelSoundAction(x, y, ACTION_FILLING);
7795 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7796 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7798 InitMovingField(x, y, MV_DOWN);
7799 started_moving = TRUE;
7801 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7802 Store[x][y] = element;
7804 PlayLevelSoundAction(x, y, ACTION_FILLING);
7806 else if (element == EL_MAGIC_WALL_FULL)
7808 if (IS_FREE(x, y + 1))
7810 InitMovingField(x, y, MV_DOWN);
7811 started_moving = TRUE;
7813 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7814 Store[x][y] = EL_CHANGED(Store[x][y]);
7816 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7818 if (!MovDelay[x][y])
7819 MovDelay[x][y] = TILEY/4 + 1;
7828 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7829 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7830 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7834 else if (element == EL_BD_MAGIC_WALL_FULL)
7836 if (IS_FREE(x, y + 1))
7838 InitMovingField(x, y, MV_DOWN);
7839 started_moving = TRUE;
7841 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7842 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7844 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7846 if (!MovDelay[x][y])
7847 MovDelay[x][y] = TILEY/4 + 1;
7856 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7857 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7858 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7862 else if (element == EL_DC_MAGIC_WALL_FULL)
7864 if (IS_FREE(x, y + 1))
7866 InitMovingField(x, y, MV_DOWN);
7867 started_moving = TRUE;
7869 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7870 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7872 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7874 if (!MovDelay[x][y])
7875 MovDelay[x][y] = TILEY/4 + 1;
7884 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7885 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7886 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7890 else if ((CAN_PASS_MAGIC_WALL(element) &&
7891 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7892 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7893 (CAN_PASS_DC_MAGIC_WALL(element) &&
7894 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7897 InitMovingField(x, y, MV_DOWN);
7898 started_moving = TRUE;
7901 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7902 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7903 EL_DC_MAGIC_WALL_FILLING);
7904 Store[x][y] = element;
7906 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7908 SplashAcid(x, y + 1);
7910 InitMovingField(x, y, MV_DOWN);
7911 started_moving = TRUE;
7913 Store[x][y] = EL_ACID;
7916 #if USE_FIX_IMPACT_COLLISION
7917 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7918 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7920 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7921 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7923 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7924 CAN_FALL(element) && WasJustFalling[x][y] &&
7925 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7927 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7928 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7929 (Feld[x][y + 1] == EL_BLOCKED)))
7931 /* this is needed for a special case not covered by calling "Impact()"
7932 from "ContinueMoving()": if an element moves to a tile directly below
7933 another element which was just falling on that tile (which was empty
7934 in the previous frame), the falling element above would just stop
7935 instead of smashing the element below (in previous version, the above
7936 element was just checked for "moving" instead of "falling", resulting
7937 in incorrect smashes caused by horizontal movement of the above
7938 element; also, the case of the player being the element to smash was
7939 simply not covered here... :-/ ) */
7941 CheckCollision[x][y] = 0;
7942 CheckImpact[x][y] = 0;
7946 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7948 if (MovDir[x][y] == MV_NONE)
7950 InitMovingField(x, y, MV_DOWN);
7951 started_moving = TRUE;
7954 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7956 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7957 MovDir[x][y] = MV_DOWN;
7959 InitMovingField(x, y, MV_DOWN);
7960 started_moving = TRUE;
7962 else if (element == EL_AMOEBA_DROP)
7964 Feld[x][y] = EL_AMOEBA_GROWING;
7965 Store[x][y] = EL_AMOEBA_WET;
7967 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7968 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7969 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7970 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7972 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7973 (IS_FREE(x - 1, y + 1) ||
7974 Feld[x - 1][y + 1] == EL_ACID));
7975 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7976 (IS_FREE(x + 1, y + 1) ||
7977 Feld[x + 1][y + 1] == EL_ACID));
7978 boolean can_fall_any = (can_fall_left || can_fall_right);
7979 boolean can_fall_both = (can_fall_left && can_fall_right);
7980 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7982 #if USE_NEW_ALL_SLIPPERY
7983 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7985 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7986 can_fall_right = FALSE;
7987 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7988 can_fall_left = FALSE;
7989 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7990 can_fall_right = FALSE;
7991 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7992 can_fall_left = FALSE;
7994 can_fall_any = (can_fall_left || can_fall_right);
7995 can_fall_both = FALSE;
7998 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8000 if (slippery_type == SLIPPERY_ONLY_LEFT)
8001 can_fall_right = FALSE;
8002 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8003 can_fall_left = FALSE;
8004 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8005 can_fall_right = FALSE;
8006 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8007 can_fall_left = FALSE;
8009 can_fall_any = (can_fall_left || can_fall_right);
8010 can_fall_both = (can_fall_left && can_fall_right);
8014 #if USE_NEW_ALL_SLIPPERY
8016 #if USE_NEW_SP_SLIPPERY
8017 /* !!! better use the same properties as for custom elements here !!! */
8018 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8019 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8021 can_fall_right = FALSE; /* slip down on left side */
8022 can_fall_both = FALSE;
8027 #if USE_NEW_ALL_SLIPPERY
8030 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8031 can_fall_right = FALSE; /* slip down on left side */
8033 can_fall_left = !(can_fall_right = RND(2));
8035 can_fall_both = FALSE;
8040 if (game.emulation == EMU_BOULDERDASH ||
8041 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8042 can_fall_right = FALSE; /* slip down on left side */
8044 can_fall_left = !(can_fall_right = RND(2));
8046 can_fall_both = FALSE;
8052 /* if not determined otherwise, prefer left side for slipping down */
8053 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8054 started_moving = TRUE;
8058 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8060 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8063 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8064 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8065 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8066 int belt_dir = game.belt_dir[belt_nr];
8068 if ((belt_dir == MV_LEFT && left_is_free) ||
8069 (belt_dir == MV_RIGHT && right_is_free))
8071 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8073 InitMovingField(x, y, belt_dir);
8074 started_moving = TRUE;
8076 Pushed[x][y] = TRUE;
8077 Pushed[nextx][y] = TRUE;
8079 GfxAction[x][y] = ACTION_DEFAULT;
8083 MovDir[x][y] = 0; /* if element was moving, stop it */
8088 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8090 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8092 if (CAN_MOVE(element) && !started_moving)
8095 int move_pattern = element_info[element].move_pattern;
8100 if (MovDir[x][y] == MV_NONE)
8102 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8103 x, y, element, element_info[element].token_name);
8104 printf("StartMoving(): This should never happen!\n");
8109 Moving2Blocked(x, y, &newx, &newy);
8111 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8114 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8115 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8117 WasJustMoving[x][y] = 0;
8118 CheckCollision[x][y] = 0;
8120 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8122 if (Feld[x][y] != element) /* element has changed */
8126 if (!MovDelay[x][y]) /* start new movement phase */
8128 /* all objects that can change their move direction after each step
8129 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8131 if (element != EL_YAMYAM &&
8132 element != EL_DARK_YAMYAM &&
8133 element != EL_PACMAN &&
8134 !(move_pattern & MV_ANY_DIRECTION) &&
8135 move_pattern != MV_TURNING_LEFT &&
8136 move_pattern != MV_TURNING_RIGHT &&
8137 move_pattern != MV_TURNING_LEFT_RIGHT &&
8138 move_pattern != MV_TURNING_RIGHT_LEFT &&
8139 move_pattern != MV_TURNING_RANDOM)
8143 if (MovDelay[x][y] && (element == EL_BUG ||
8144 element == EL_SPACESHIP ||
8145 element == EL_SP_SNIKSNAK ||
8146 element == EL_SP_ELECTRON ||
8147 element == EL_MOLE))
8148 TEST_DrawLevelField(x, y);
8152 if (MovDelay[x][y]) /* wait some time before next movement */
8156 if (element == EL_ROBOT ||
8157 element == EL_YAMYAM ||
8158 element == EL_DARK_YAMYAM)
8160 DrawLevelElementAnimationIfNeeded(x, y, element);
8161 PlayLevelSoundAction(x, y, ACTION_WAITING);
8163 else if (element == EL_SP_ELECTRON)
8164 DrawLevelElementAnimationIfNeeded(x, y, element);
8165 else if (element == EL_DRAGON)
8168 int dir = MovDir[x][y];
8169 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8170 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8171 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8172 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8173 dir == MV_UP ? IMG_FLAMES_1_UP :
8174 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8175 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8177 GfxAction[x][y] = ACTION_ATTACKING;
8179 if (IS_PLAYER(x, y))
8180 DrawPlayerField(x, y);
8182 TEST_DrawLevelField(x, y);
8184 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8186 for (i = 1; i <= 3; i++)
8188 int xx = x + i * dx;
8189 int yy = y + i * dy;
8190 int sx = SCREENX(xx);
8191 int sy = SCREENY(yy);
8192 int flame_graphic = graphic + (i - 1);
8194 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8199 int flamed = MovingOrBlocked2Element(xx, yy);
8203 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8205 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8206 RemoveMovingField(xx, yy);
8208 RemoveField(xx, yy);
8210 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8213 RemoveMovingField(xx, yy);
8216 ChangeDelay[xx][yy] = 0;
8218 Feld[xx][yy] = EL_FLAMES;
8220 if (IN_SCR_FIELD(sx, sy))
8222 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8223 DrawGraphic(sx, sy, flame_graphic, frame);
8228 if (Feld[xx][yy] == EL_FLAMES)
8229 Feld[xx][yy] = EL_EMPTY;
8230 TEST_DrawLevelField(xx, yy);
8235 if (MovDelay[x][y]) /* element still has to wait some time */
8237 PlayLevelSoundAction(x, y, ACTION_WAITING);
8243 /* now make next step */
8245 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8247 if (DONT_COLLIDE_WITH(element) &&
8248 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8249 !PLAYER_ENEMY_PROTECTED(newx, newy))
8251 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8256 else if (CAN_MOVE_INTO_ACID(element) &&
8257 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8258 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8259 (MovDir[x][y] == MV_DOWN ||
8260 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8262 SplashAcid(newx, newy);
8263 Store[x][y] = EL_ACID;
8265 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8267 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8268 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8269 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8270 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8273 TEST_DrawLevelField(x, y);
8275 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8276 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8277 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8279 local_player->friends_still_needed--;
8280 if (!local_player->friends_still_needed &&
8281 !local_player->GameOver && AllPlayersGone)
8282 PlayerWins(local_player);
8286 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8288 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8289 TEST_DrawLevelField(newx, newy);
8291 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8293 else if (!IS_FREE(newx, newy))
8295 GfxAction[x][y] = ACTION_WAITING;
8297 if (IS_PLAYER(x, y))
8298 DrawPlayerField(x, y);
8300 TEST_DrawLevelField(x, y);
8305 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8307 if (IS_FOOD_PIG(Feld[newx][newy]))
8309 if (IS_MOVING(newx, newy))
8310 RemoveMovingField(newx, newy);
8313 Feld[newx][newy] = EL_EMPTY;
8314 TEST_DrawLevelField(newx, newy);
8317 PlayLevelSound(x, y, SND_PIG_DIGGING);
8319 else if (!IS_FREE(newx, newy))
8321 if (IS_PLAYER(x, y))
8322 DrawPlayerField(x, y);
8324 TEST_DrawLevelField(x, y);
8329 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8331 if (Store[x][y] != EL_EMPTY)
8333 boolean can_clone = FALSE;
8336 /* check if element to clone is still there */
8337 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8339 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8347 /* cannot clone or target field not free anymore -- do not clone */
8348 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8349 Store[x][y] = EL_EMPTY;
8352 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8354 if (IS_MV_DIAGONAL(MovDir[x][y]))
8356 int diagonal_move_dir = MovDir[x][y];
8357 int stored = Store[x][y];
8358 int change_delay = 8;
8361 /* android is moving diagonally */
8363 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8365 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8366 GfxElement[x][y] = EL_EMC_ANDROID;
8367 GfxAction[x][y] = ACTION_SHRINKING;
8368 GfxDir[x][y] = diagonal_move_dir;
8369 ChangeDelay[x][y] = change_delay;
8371 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8374 DrawLevelGraphicAnimation(x, y, graphic);
8375 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8377 if (Feld[newx][newy] == EL_ACID)
8379 SplashAcid(newx, newy);
8384 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8386 Store[newx][newy] = EL_EMC_ANDROID;
8387 GfxElement[newx][newy] = EL_EMC_ANDROID;
8388 GfxAction[newx][newy] = ACTION_GROWING;
8389 GfxDir[newx][newy] = diagonal_move_dir;
8390 ChangeDelay[newx][newy] = change_delay;
8392 graphic = el_act_dir2img(GfxElement[newx][newy],
8393 GfxAction[newx][newy], GfxDir[newx][newy]);
8395 DrawLevelGraphicAnimation(newx, newy, graphic);
8396 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8402 Feld[newx][newy] = EL_EMPTY;
8403 TEST_DrawLevelField(newx, newy);
8405 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8408 else if (!IS_FREE(newx, newy))
8411 if (IS_PLAYER(x, y))
8412 DrawPlayerField(x, y);
8414 TEST_DrawLevelField(x, y);
8420 else if (IS_CUSTOM_ELEMENT(element) &&
8421 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8424 if (!DigFieldByCE(newx, newy, element))
8427 int new_element = Feld[newx][newy];
8429 if (!IS_FREE(newx, newy))
8431 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8432 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8435 /* no element can dig solid indestructible elements */
8436 if (IS_INDESTRUCTIBLE(new_element) &&
8437 !IS_DIGGABLE(new_element) &&
8438 !IS_COLLECTIBLE(new_element))
8441 if (AmoebaNr[newx][newy] &&
8442 (new_element == EL_AMOEBA_FULL ||
8443 new_element == EL_BD_AMOEBA ||
8444 new_element == EL_AMOEBA_GROWING))
8446 AmoebaCnt[AmoebaNr[newx][newy]]--;
8447 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8450 if (IS_MOVING(newx, newy))
8451 RemoveMovingField(newx, newy);
8454 RemoveField(newx, newy);
8455 TEST_DrawLevelField(newx, newy);
8458 /* if digged element was about to explode, prevent the explosion */
8459 ExplodeField[newx][newy] = EX_TYPE_NONE;
8461 PlayLevelSoundAction(x, y, action);
8464 Store[newx][newy] = EL_EMPTY;
8467 /* this makes it possible to leave the removed element again */
8468 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8469 Store[newx][newy] = new_element;
8471 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8473 int move_leave_element = element_info[element].move_leave_element;
8475 /* this makes it possible to leave the removed element again */
8476 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8477 new_element : move_leave_element);
8483 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8485 RunnerVisit[x][y] = FrameCounter;
8486 PlayerVisit[x][y] /= 8; /* expire player visit path */
8489 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8491 if (!IS_FREE(newx, newy))
8493 if (IS_PLAYER(x, y))
8494 DrawPlayerField(x, y);
8496 TEST_DrawLevelField(x, y);
8502 boolean wanna_flame = !RND(10);
8503 int dx = newx - x, dy = newy - y;
8504 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8505 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8506 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8507 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8508 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8509 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8512 IS_CLASSIC_ENEMY(element1) ||
8513 IS_CLASSIC_ENEMY(element2)) &&
8514 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8515 element1 != EL_FLAMES && element2 != EL_FLAMES)
8517 ResetGfxAnimation(x, y);
8518 GfxAction[x][y] = ACTION_ATTACKING;
8520 if (IS_PLAYER(x, y))
8521 DrawPlayerField(x, y);
8523 TEST_DrawLevelField(x, y);
8525 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8527 MovDelay[x][y] = 50;
8531 RemoveField(newx, newy);
8533 Feld[newx][newy] = EL_FLAMES;
8534 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8537 RemoveField(newx1, newy1);
8539 Feld[newx1][newy1] = EL_FLAMES;
8541 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8544 RemoveField(newx2, newy2);
8546 Feld[newx2][newy2] = EL_FLAMES;
8553 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8554 Feld[newx][newy] == EL_DIAMOND)
8556 if (IS_MOVING(newx, newy))
8557 RemoveMovingField(newx, newy);
8560 Feld[newx][newy] = EL_EMPTY;
8561 TEST_DrawLevelField(newx, newy);
8564 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8566 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8567 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8569 if (AmoebaNr[newx][newy])
8571 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8572 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8573 Feld[newx][newy] == EL_BD_AMOEBA)
8574 AmoebaCnt[AmoebaNr[newx][newy]]--;
8579 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8581 RemoveMovingField(newx, newy);
8584 if (IS_MOVING(newx, newy))
8586 RemoveMovingField(newx, newy);
8591 Feld[newx][newy] = EL_EMPTY;
8592 TEST_DrawLevelField(newx, newy);
8595 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8597 else if ((element == EL_PACMAN || element == EL_MOLE)
8598 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8600 if (AmoebaNr[newx][newy])
8602 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8603 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8604 Feld[newx][newy] == EL_BD_AMOEBA)
8605 AmoebaCnt[AmoebaNr[newx][newy]]--;
8608 if (element == EL_MOLE)
8610 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8611 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8613 ResetGfxAnimation(x, y);
8614 GfxAction[x][y] = ACTION_DIGGING;
8615 TEST_DrawLevelField(x, y);
8617 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8619 return; /* wait for shrinking amoeba */
8621 else /* element == EL_PACMAN */
8623 Feld[newx][newy] = EL_EMPTY;
8624 TEST_DrawLevelField(newx, newy);
8625 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8628 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8629 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8630 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8632 /* wait for shrinking amoeba to completely disappear */
8635 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8637 /* object was running against a wall */
8642 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8643 if (move_pattern & MV_ANY_DIRECTION &&
8644 move_pattern == MovDir[x][y])
8646 int blocking_element =
8647 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8649 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8652 element = Feld[x][y]; /* element might have changed */
8656 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8657 DrawLevelElementAnimation(x, y, element);
8659 if (DONT_TOUCH(element))
8660 TestIfBadThingTouchesPlayer(x, y);
8665 InitMovingField(x, y, MovDir[x][y]);
8667 PlayLevelSoundAction(x, y, ACTION_MOVING);
8671 ContinueMoving(x, y);
8674 void ContinueMoving(int x, int y)
8676 int element = Feld[x][y];
8677 struct ElementInfo *ei = &element_info[element];
8678 int direction = MovDir[x][y];
8679 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8680 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8681 int newx = x + dx, newy = y + dy;
8682 int stored = Store[x][y];
8683 int stored_new = Store[newx][newy];
8684 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8685 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8686 boolean last_line = (newy == lev_fieldy - 1);
8688 MovPos[x][y] += getElementMoveStepsize(x, y);
8690 if (pushed_by_player) /* special case: moving object pushed by player */
8691 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8693 if (ABS(MovPos[x][y]) < TILEX)
8696 int ee = Feld[x][y];
8697 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8698 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8700 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8701 x, y, ABS(MovPos[x][y]),
8703 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8706 TEST_DrawLevelField(x, y);
8708 return; /* element is still moving */
8711 /* element reached destination field */
8713 Feld[x][y] = EL_EMPTY;
8714 Feld[newx][newy] = element;
8715 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8717 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8719 element = Feld[newx][newy] = EL_ACID;
8721 else if (element == EL_MOLE)
8723 Feld[x][y] = EL_SAND;
8725 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8727 else if (element == EL_QUICKSAND_FILLING)
8729 element = Feld[newx][newy] = get_next_element(element);
8730 Store[newx][newy] = Store[x][y];
8732 else if (element == EL_QUICKSAND_EMPTYING)
8734 Feld[x][y] = get_next_element(element);
8735 element = Feld[newx][newy] = Store[x][y];
8737 else if (element == EL_QUICKSAND_FAST_FILLING)
8739 element = Feld[newx][newy] = get_next_element(element);
8740 Store[newx][newy] = Store[x][y];
8742 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8744 Feld[x][y] = get_next_element(element);
8745 element = Feld[newx][newy] = Store[x][y];
8747 else if (element == EL_MAGIC_WALL_FILLING)
8749 element = Feld[newx][newy] = get_next_element(element);
8750 if (!game.magic_wall_active)
8751 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8752 Store[newx][newy] = Store[x][y];
8754 else if (element == EL_MAGIC_WALL_EMPTYING)
8756 Feld[x][y] = get_next_element(element);
8757 if (!game.magic_wall_active)
8758 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8759 element = Feld[newx][newy] = Store[x][y];
8761 #if USE_NEW_CUSTOM_VALUE
8762 InitField(newx, newy, FALSE);
8765 else if (element == EL_BD_MAGIC_WALL_FILLING)
8767 element = Feld[newx][newy] = get_next_element(element);
8768 if (!game.magic_wall_active)
8769 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8770 Store[newx][newy] = Store[x][y];
8772 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8774 Feld[x][y] = get_next_element(element);
8775 if (!game.magic_wall_active)
8776 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8777 element = Feld[newx][newy] = Store[x][y];
8779 #if USE_NEW_CUSTOM_VALUE
8780 InitField(newx, newy, FALSE);
8783 else if (element == EL_DC_MAGIC_WALL_FILLING)
8785 element = Feld[newx][newy] = get_next_element(element);
8786 if (!game.magic_wall_active)
8787 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8788 Store[newx][newy] = Store[x][y];
8790 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8792 Feld[x][y] = get_next_element(element);
8793 if (!game.magic_wall_active)
8794 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8795 element = Feld[newx][newy] = Store[x][y];
8797 #if USE_NEW_CUSTOM_VALUE
8798 InitField(newx, newy, FALSE);
8801 else if (element == EL_AMOEBA_DROPPING)
8803 Feld[x][y] = get_next_element(element);
8804 element = Feld[newx][newy] = Store[x][y];
8806 else if (element == EL_SOKOBAN_OBJECT)
8809 Feld[x][y] = Back[x][y];
8811 if (Back[newx][newy])
8812 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8814 Back[x][y] = Back[newx][newy] = 0;
8817 Store[x][y] = EL_EMPTY;
8822 MovDelay[newx][newy] = 0;
8824 if (CAN_CHANGE_OR_HAS_ACTION(element))
8826 /* copy element change control values to new field */
8827 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8828 ChangePage[newx][newy] = ChangePage[x][y];
8829 ChangeCount[newx][newy] = ChangeCount[x][y];
8830 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8833 #if USE_NEW_CUSTOM_VALUE
8834 CustomValue[newx][newy] = CustomValue[x][y];
8837 ChangeDelay[x][y] = 0;
8838 ChangePage[x][y] = -1;
8839 ChangeCount[x][y] = 0;
8840 ChangeEvent[x][y] = -1;
8842 #if USE_NEW_CUSTOM_VALUE
8843 CustomValue[x][y] = 0;
8846 /* copy animation control values to new field */
8847 GfxFrame[newx][newy] = GfxFrame[x][y];
8848 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8849 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8850 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8852 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8854 /* some elements can leave other elements behind after moving */
8856 if (ei->move_leave_element != EL_EMPTY &&
8857 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8858 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8860 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8861 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8862 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8865 int move_leave_element = ei->move_leave_element;
8869 /* this makes it possible to leave the removed element again */
8870 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8871 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8873 /* this makes it possible to leave the removed element again */
8874 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8875 move_leave_element = stored;
8878 /* this makes it possible to leave the removed element again */
8879 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8880 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8881 move_leave_element = stored;
8884 Feld[x][y] = move_leave_element;
8886 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8887 MovDir[x][y] = direction;
8889 InitField(x, y, FALSE);
8891 if (GFX_CRUMBLED(Feld[x][y]))
8892 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8894 if (ELEM_IS_PLAYER(move_leave_element))
8895 RelocatePlayer(x, y, move_leave_element);
8898 /* do this after checking for left-behind element */
8899 ResetGfxAnimation(x, y); /* reset animation values for old field */
8901 if (!CAN_MOVE(element) ||
8902 (CAN_FALL(element) && direction == MV_DOWN &&
8903 (element == EL_SPRING ||
8904 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8905 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8906 GfxDir[x][y] = MovDir[newx][newy] = 0;
8908 TEST_DrawLevelField(x, y);
8909 TEST_DrawLevelField(newx, newy);
8911 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8913 /* prevent pushed element from moving on in pushed direction */
8914 if (pushed_by_player && CAN_MOVE(element) &&
8915 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8916 !(element_info[element].move_pattern & direction))
8917 TurnRound(newx, newy);
8919 /* prevent elements on conveyor belt from moving on in last direction */
8920 if (pushed_by_conveyor && CAN_FALL(element) &&
8921 direction & MV_HORIZONTAL)
8922 MovDir[newx][newy] = 0;
8924 if (!pushed_by_player)
8926 int nextx = newx + dx, nexty = newy + dy;
8927 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8929 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8931 if (CAN_FALL(element) && direction == MV_DOWN)
8932 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8934 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8935 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8937 #if USE_FIX_IMPACT_COLLISION
8938 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8939 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8943 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8945 TestIfBadThingTouchesPlayer(newx, newy);
8946 TestIfBadThingTouchesFriend(newx, newy);
8948 if (!IS_CUSTOM_ELEMENT(element))
8949 TestIfBadThingTouchesOtherBadThing(newx, newy);
8951 else if (element == EL_PENGUIN)
8952 TestIfFriendTouchesBadThing(newx, newy);
8954 if (DONT_GET_HIT_BY(element))
8956 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8959 /* give the player one last chance (one more frame) to move away */
8960 if (CAN_FALL(element) && direction == MV_DOWN &&
8961 (last_line || (!IS_FREE(x, newy + 1) &&
8962 (!IS_PLAYER(x, newy + 1) ||
8963 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8966 if (pushed_by_player && !game.use_change_when_pushing_bug)
8968 int push_side = MV_DIR_OPPOSITE(direction);
8969 struct PlayerInfo *player = PLAYERINFO(x, y);
8971 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8972 player->index_bit, push_side);
8973 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8974 player->index_bit, push_side);
8977 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8978 MovDelay[newx][newy] = 1;
8980 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8982 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8985 if (ChangePage[newx][newy] != -1) /* delayed change */
8987 int page = ChangePage[newx][newy];
8988 struct ElementChangeInfo *change = &ei->change_page[page];
8990 ChangePage[newx][newy] = -1;
8992 if (change->can_change)
8994 if (ChangeElement(newx, newy, element, page))
8996 if (change->post_change_function)
8997 change->post_change_function(newx, newy);
9001 if (change->has_action)
9002 ExecuteCustomElementAction(newx, newy, element, page);
9006 TestIfElementHitsCustomElement(newx, newy, direction);
9007 TestIfPlayerTouchesCustomElement(newx, newy);
9008 TestIfElementTouchesCustomElement(newx, newy);
9010 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9011 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9012 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9013 MV_DIR_OPPOSITE(direction));
9016 int AmoebeNachbarNr(int ax, int ay)
9019 int element = Feld[ax][ay];
9021 static int xy[4][2] =
9029 for (i = 0; i < NUM_DIRECTIONS; i++)
9031 int x = ax + xy[i][0];
9032 int y = ay + xy[i][1];
9034 if (!IN_LEV_FIELD(x, y))
9037 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9038 group_nr = AmoebaNr[x][y];
9044 void AmoebenVereinigen(int ax, int ay)
9046 int i, x, y, xx, yy;
9047 int new_group_nr = AmoebaNr[ax][ay];
9048 static int xy[4][2] =
9056 if (new_group_nr == 0)
9059 for (i = 0; i < NUM_DIRECTIONS; i++)
9064 if (!IN_LEV_FIELD(x, y))
9067 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9068 Feld[x][y] == EL_BD_AMOEBA ||
9069 Feld[x][y] == EL_AMOEBA_DEAD) &&
9070 AmoebaNr[x][y] != new_group_nr)
9072 int old_group_nr = AmoebaNr[x][y];
9074 if (old_group_nr == 0)
9077 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9078 AmoebaCnt[old_group_nr] = 0;
9079 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9080 AmoebaCnt2[old_group_nr] = 0;
9082 SCAN_PLAYFIELD(xx, yy)
9084 if (AmoebaNr[xx][yy] == old_group_nr)
9085 AmoebaNr[xx][yy] = new_group_nr;
9091 void AmoebeUmwandeln(int ax, int ay)
9095 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9097 int group_nr = AmoebaNr[ax][ay];
9102 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9103 printf("AmoebeUmwandeln(): This should never happen!\n");
9108 SCAN_PLAYFIELD(x, y)
9110 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9113 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9117 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9118 SND_AMOEBA_TURNING_TO_GEM :
9119 SND_AMOEBA_TURNING_TO_ROCK));
9124 static int xy[4][2] =
9132 for (i = 0; i < NUM_DIRECTIONS; i++)
9137 if (!IN_LEV_FIELD(x, y))
9140 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9142 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9143 SND_AMOEBA_TURNING_TO_GEM :
9144 SND_AMOEBA_TURNING_TO_ROCK));
9151 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9154 int group_nr = AmoebaNr[ax][ay];
9155 boolean done = FALSE;
9160 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9161 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9166 SCAN_PLAYFIELD(x, y)
9168 if (AmoebaNr[x][y] == group_nr &&
9169 (Feld[x][y] == EL_AMOEBA_DEAD ||
9170 Feld[x][y] == EL_BD_AMOEBA ||
9171 Feld[x][y] == EL_AMOEBA_GROWING))
9174 Feld[x][y] = new_element;
9175 InitField(x, y, FALSE);
9176 TEST_DrawLevelField(x, y);
9182 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9183 SND_BD_AMOEBA_TURNING_TO_ROCK :
9184 SND_BD_AMOEBA_TURNING_TO_GEM));
9187 void AmoebeWaechst(int x, int y)
9189 static unsigned long sound_delay = 0;
9190 static unsigned long sound_delay_value = 0;
9192 if (!MovDelay[x][y]) /* start new growing cycle */
9196 if (DelayReached(&sound_delay, sound_delay_value))
9198 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9199 sound_delay_value = 30;
9203 if (MovDelay[x][y]) /* wait some time before growing bigger */
9206 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9208 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9209 6 - MovDelay[x][y]);
9211 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9214 if (!MovDelay[x][y])
9216 Feld[x][y] = Store[x][y];
9218 TEST_DrawLevelField(x, y);
9223 void AmoebaDisappearing(int x, int y)
9225 static unsigned long sound_delay = 0;
9226 static unsigned long sound_delay_value = 0;
9228 if (!MovDelay[x][y]) /* start new shrinking cycle */
9232 if (DelayReached(&sound_delay, sound_delay_value))
9233 sound_delay_value = 30;
9236 if (MovDelay[x][y]) /* wait some time before shrinking */
9239 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9241 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9242 6 - MovDelay[x][y]);
9244 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9247 if (!MovDelay[x][y])
9249 Feld[x][y] = EL_EMPTY;
9250 TEST_DrawLevelField(x, y);
9252 /* don't let mole enter this field in this cycle;
9253 (give priority to objects falling to this field from above) */
9259 void AmoebeAbleger(int ax, int ay)
9262 int element = Feld[ax][ay];
9263 int graphic = el2img(element);
9264 int newax = ax, neway = ay;
9265 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9266 static int xy[4][2] =
9274 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9276 Feld[ax][ay] = EL_AMOEBA_DEAD;
9277 TEST_DrawLevelField(ax, ay);
9281 if (IS_ANIMATED(graphic))
9282 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9284 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9285 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9287 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9290 if (MovDelay[ax][ay])
9294 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9297 int x = ax + xy[start][0];
9298 int y = ay + xy[start][1];
9300 if (!IN_LEV_FIELD(x, y))
9303 if (IS_FREE(x, y) ||
9304 CAN_GROW_INTO(Feld[x][y]) ||
9305 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9306 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9312 if (newax == ax && neway == ay)
9315 else /* normal or "filled" (BD style) amoeba */
9318 boolean waiting_for_player = FALSE;
9320 for (i = 0; i < NUM_DIRECTIONS; i++)
9322 int j = (start + i) % 4;
9323 int x = ax + xy[j][0];
9324 int y = ay + xy[j][1];
9326 if (!IN_LEV_FIELD(x, y))
9329 if (IS_FREE(x, y) ||
9330 CAN_GROW_INTO(Feld[x][y]) ||
9331 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9332 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9338 else if (IS_PLAYER(x, y))
9339 waiting_for_player = TRUE;
9342 if (newax == ax && neway == ay) /* amoeba cannot grow */
9344 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9346 Feld[ax][ay] = EL_AMOEBA_DEAD;
9347 TEST_DrawLevelField(ax, ay);
9348 AmoebaCnt[AmoebaNr[ax][ay]]--;
9350 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9352 if (element == EL_AMOEBA_FULL)
9353 AmoebeUmwandeln(ax, ay);
9354 else if (element == EL_BD_AMOEBA)
9355 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9360 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9362 /* amoeba gets larger by growing in some direction */
9364 int new_group_nr = AmoebaNr[ax][ay];
9367 if (new_group_nr == 0)
9369 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9370 printf("AmoebeAbleger(): This should never happen!\n");
9375 AmoebaNr[newax][neway] = new_group_nr;
9376 AmoebaCnt[new_group_nr]++;
9377 AmoebaCnt2[new_group_nr]++;
9379 /* if amoeba touches other amoeba(s) after growing, unify them */
9380 AmoebenVereinigen(newax, neway);
9382 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9384 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9390 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9391 (neway == lev_fieldy - 1 && newax != ax))
9393 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9394 Store[newax][neway] = element;
9396 else if (neway == ay || element == EL_EMC_DRIPPER)
9398 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9400 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9404 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9405 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9406 Store[ax][ay] = EL_AMOEBA_DROP;
9407 ContinueMoving(ax, ay);
9411 TEST_DrawLevelField(newax, neway);
9414 void Life(int ax, int ay)
9418 int element = Feld[ax][ay];
9419 int graphic = el2img(element);
9420 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9422 boolean changed = FALSE;
9424 if (IS_ANIMATED(graphic))
9425 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9430 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9431 MovDelay[ax][ay] = life_time;
9433 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9436 if (MovDelay[ax][ay])
9440 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9442 int xx = ax+x1, yy = ay+y1;
9445 if (!IN_LEV_FIELD(xx, yy))
9448 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9450 int x = xx+x2, y = yy+y2;
9452 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9455 if (((Feld[x][y] == element ||
9456 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9458 (IS_FREE(x, y) && Stop[x][y]))
9462 if (xx == ax && yy == ay) /* field in the middle */
9464 if (nachbarn < life_parameter[0] ||
9465 nachbarn > life_parameter[1])
9467 Feld[xx][yy] = EL_EMPTY;
9469 TEST_DrawLevelField(xx, yy);
9470 Stop[xx][yy] = TRUE;
9474 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9475 { /* free border field */
9476 if (nachbarn >= life_parameter[2] &&
9477 nachbarn <= life_parameter[3])
9479 Feld[xx][yy] = element;
9480 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9482 TEST_DrawLevelField(xx, yy);
9483 Stop[xx][yy] = TRUE;
9490 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9491 SND_GAME_OF_LIFE_GROWING);
9494 static void InitRobotWheel(int x, int y)
9496 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9499 static void RunRobotWheel(int x, int y)
9501 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9504 static void StopRobotWheel(int x, int y)
9506 if (ZX == x && ZY == y)
9510 game.robot_wheel_active = FALSE;
9514 static void InitTimegateWheel(int x, int y)
9516 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9519 static void RunTimegateWheel(int x, int y)
9521 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9524 static void InitMagicBallDelay(int x, int y)
9527 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9529 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9533 static void ActivateMagicBall(int bx, int by)
9537 if (level.ball_random)
9539 int pos_border = RND(8); /* select one of the eight border elements */
9540 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9541 int xx = pos_content % 3;
9542 int yy = pos_content / 3;
9547 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9548 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9552 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9554 int xx = x - bx + 1;
9555 int yy = y - by + 1;
9557 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9558 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9562 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9565 void CheckExit(int x, int y)
9567 if (local_player->gems_still_needed > 0 ||
9568 local_player->sokobanfields_still_needed > 0 ||
9569 local_player->lights_still_needed > 0)
9571 int element = Feld[x][y];
9572 int graphic = el2img(element);
9574 if (IS_ANIMATED(graphic))
9575 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9580 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9583 Feld[x][y] = EL_EXIT_OPENING;
9585 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9588 void CheckExitEM(int x, int y)
9590 if (local_player->gems_still_needed > 0 ||
9591 local_player->sokobanfields_still_needed > 0 ||
9592 local_player->lights_still_needed > 0)
9594 int element = Feld[x][y];
9595 int graphic = el2img(element);
9597 if (IS_ANIMATED(graphic))
9598 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9603 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9606 Feld[x][y] = EL_EM_EXIT_OPENING;
9608 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9611 void CheckExitSteel(int x, int y)
9613 if (local_player->gems_still_needed > 0 ||
9614 local_player->sokobanfields_still_needed > 0 ||
9615 local_player->lights_still_needed > 0)
9617 int element = Feld[x][y];
9618 int graphic = el2img(element);
9620 if (IS_ANIMATED(graphic))
9621 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9626 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9629 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9631 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9634 void CheckExitSteelEM(int x, int y)
9636 if (local_player->gems_still_needed > 0 ||
9637 local_player->sokobanfields_still_needed > 0 ||
9638 local_player->lights_still_needed > 0)
9640 int element = Feld[x][y];
9641 int graphic = el2img(element);
9643 if (IS_ANIMATED(graphic))
9644 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9649 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9652 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9654 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9657 void CheckExitSP(int x, int y)
9659 if (local_player->gems_still_needed > 0)
9661 int element = Feld[x][y];
9662 int graphic = el2img(element);
9664 if (IS_ANIMATED(graphic))
9665 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9670 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9673 Feld[x][y] = EL_SP_EXIT_OPENING;
9675 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9678 static void CloseAllOpenTimegates()
9682 SCAN_PLAYFIELD(x, y)
9684 int element = Feld[x][y];
9686 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9688 Feld[x][y] = EL_TIMEGATE_CLOSING;
9690 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9695 void DrawTwinkleOnField(int x, int y)
9697 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9700 if (Feld[x][y] == EL_BD_DIAMOND)
9703 if (MovDelay[x][y] == 0) /* next animation frame */
9704 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9706 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9710 DrawLevelElementAnimation(x, y, Feld[x][y]);
9712 if (MovDelay[x][y] != 0)
9714 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9715 10 - MovDelay[x][y]);
9717 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9722 void MauerWaechst(int x, int y)
9726 if (!MovDelay[x][y]) /* next animation frame */
9727 MovDelay[x][y] = 3 * delay;
9729 if (MovDelay[x][y]) /* wait some time before next frame */
9733 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9735 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9736 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9738 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9741 if (!MovDelay[x][y])
9743 if (MovDir[x][y] == MV_LEFT)
9745 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9746 TEST_DrawLevelField(x - 1, y);
9748 else if (MovDir[x][y] == MV_RIGHT)
9750 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9751 TEST_DrawLevelField(x + 1, y);
9753 else if (MovDir[x][y] == MV_UP)
9755 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9756 TEST_DrawLevelField(x, y - 1);
9760 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9761 TEST_DrawLevelField(x, y + 1);
9764 Feld[x][y] = Store[x][y];
9766 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9767 TEST_DrawLevelField(x, y);
9772 void MauerAbleger(int ax, int ay)
9774 int element = Feld[ax][ay];
9775 int graphic = el2img(element);
9776 boolean oben_frei = FALSE, unten_frei = FALSE;
9777 boolean links_frei = FALSE, rechts_frei = FALSE;
9778 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9779 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9780 boolean new_wall = FALSE;
9782 if (IS_ANIMATED(graphic))
9783 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9785 if (!MovDelay[ax][ay]) /* start building new wall */
9786 MovDelay[ax][ay] = 6;
9788 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9791 if (MovDelay[ax][ay])
9795 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9797 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9799 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9801 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9804 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9805 element == EL_EXPANDABLE_WALL_ANY)
9809 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9810 Store[ax][ay-1] = element;
9811 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9812 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9813 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9814 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9819 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9820 Store[ax][ay+1] = element;
9821 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9822 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9823 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9824 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9829 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9830 element == EL_EXPANDABLE_WALL_ANY ||
9831 element == EL_EXPANDABLE_WALL ||
9832 element == EL_BD_EXPANDABLE_WALL)
9836 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9837 Store[ax-1][ay] = element;
9838 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9839 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9840 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9841 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9847 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9848 Store[ax+1][ay] = element;
9849 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9850 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9851 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9852 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9857 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9858 TEST_DrawLevelField(ax, ay);
9860 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9862 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9863 unten_massiv = TRUE;
9864 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9865 links_massiv = TRUE;
9866 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9867 rechts_massiv = TRUE;
9869 if (((oben_massiv && unten_massiv) ||
9870 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9871 element == EL_EXPANDABLE_WALL) &&
9872 ((links_massiv && rechts_massiv) ||
9873 element == EL_EXPANDABLE_WALL_VERTICAL))
9874 Feld[ax][ay] = EL_WALL;
9877 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9880 void MauerAblegerStahl(int ax, int ay)
9882 int element = Feld[ax][ay];
9883 int graphic = el2img(element);
9884 boolean oben_frei = FALSE, unten_frei = FALSE;
9885 boolean links_frei = FALSE, rechts_frei = FALSE;
9886 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9887 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9888 boolean new_wall = FALSE;
9890 if (IS_ANIMATED(graphic))
9891 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9893 if (!MovDelay[ax][ay]) /* start building new wall */
9894 MovDelay[ax][ay] = 6;
9896 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9899 if (MovDelay[ax][ay])
9903 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9905 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9907 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9909 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9912 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9913 element == EL_EXPANDABLE_STEELWALL_ANY)
9917 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9918 Store[ax][ay-1] = element;
9919 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9920 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9921 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9922 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9927 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9928 Store[ax][ay+1] = element;
9929 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9930 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9931 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9932 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9937 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9938 element == EL_EXPANDABLE_STEELWALL_ANY)
9942 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9943 Store[ax-1][ay] = element;
9944 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9945 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9946 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9947 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9953 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9954 Store[ax+1][ay] = element;
9955 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9956 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9957 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9958 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9963 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9965 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9966 unten_massiv = TRUE;
9967 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9968 links_massiv = TRUE;
9969 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9970 rechts_massiv = TRUE;
9972 if (((oben_massiv && unten_massiv) ||
9973 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9974 ((links_massiv && rechts_massiv) ||
9975 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9976 Feld[ax][ay] = EL_STEELWALL;
9979 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9982 void CheckForDragon(int x, int y)
9985 boolean dragon_found = FALSE;
9986 static int xy[4][2] =
9994 for (i = 0; i < NUM_DIRECTIONS; i++)
9996 for (j = 0; j < 4; j++)
9998 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10000 if (IN_LEV_FIELD(xx, yy) &&
10001 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10003 if (Feld[xx][yy] == EL_DRAGON)
10004 dragon_found = TRUE;
10013 for (i = 0; i < NUM_DIRECTIONS; i++)
10015 for (j = 0; j < 3; j++)
10017 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10019 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10021 Feld[xx][yy] = EL_EMPTY;
10022 TEST_DrawLevelField(xx, yy);
10031 static void InitBuggyBase(int x, int y)
10033 int element = Feld[x][y];
10034 int activating_delay = FRAMES_PER_SECOND / 4;
10036 ChangeDelay[x][y] =
10037 (element == EL_SP_BUGGY_BASE ?
10038 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10039 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10041 element == EL_SP_BUGGY_BASE_ACTIVE ?
10042 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10045 static void WarnBuggyBase(int x, int y)
10048 static int xy[4][2] =
10056 for (i = 0; i < NUM_DIRECTIONS; i++)
10058 int xx = x + xy[i][0];
10059 int yy = y + xy[i][1];
10061 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10063 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10070 static void InitTrap(int x, int y)
10072 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10075 static void ActivateTrap(int x, int y)
10077 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10080 static void ChangeActiveTrap(int x, int y)
10082 int graphic = IMG_TRAP_ACTIVE;
10084 /* if new animation frame was drawn, correct crumbled sand border */
10085 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10086 TEST_DrawLevelFieldCrumbledSand(x, y);
10089 static int getSpecialActionElement(int element, int number, int base_element)
10091 return (element != EL_EMPTY ? element :
10092 number != -1 ? base_element + number - 1 :
10096 static int getModifiedActionNumber(int value_old, int operator, int operand,
10097 int value_min, int value_max)
10099 int value_new = (operator == CA_MODE_SET ? operand :
10100 operator == CA_MODE_ADD ? value_old + operand :
10101 operator == CA_MODE_SUBTRACT ? value_old - operand :
10102 operator == CA_MODE_MULTIPLY ? value_old * operand :
10103 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10104 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10107 return (value_new < value_min ? value_min :
10108 value_new > value_max ? value_max :
10112 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10114 struct ElementInfo *ei = &element_info[element];
10115 struct ElementChangeInfo *change = &ei->change_page[page];
10116 int target_element = change->target_element;
10117 int action_type = change->action_type;
10118 int action_mode = change->action_mode;
10119 int action_arg = change->action_arg;
10122 if (!change->has_action)
10125 /* ---------- determine action paramater values -------------------------- */
10127 int level_time_value =
10128 (level.time > 0 ? TimeLeft :
10131 int action_arg_element =
10132 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10133 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10134 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10137 int action_arg_direction =
10138 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10139 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10140 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10141 change->actual_trigger_side :
10142 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10143 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10146 int action_arg_number_min =
10147 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10150 int action_arg_number_max =
10151 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10152 action_type == CA_SET_LEVEL_GEMS ? 999 :
10153 action_type == CA_SET_LEVEL_TIME ? 9999 :
10154 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10155 action_type == CA_SET_CE_VALUE ? 9999 :
10156 action_type == CA_SET_CE_SCORE ? 9999 :
10159 int action_arg_number_reset =
10160 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10161 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10162 action_type == CA_SET_LEVEL_TIME ? level.time :
10163 action_type == CA_SET_LEVEL_SCORE ? 0 :
10164 #if USE_NEW_CUSTOM_VALUE
10165 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10167 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10169 action_type == CA_SET_CE_SCORE ? 0 :
10172 int action_arg_number =
10173 (action_arg <= CA_ARG_MAX ? action_arg :
10174 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10175 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10176 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10177 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10178 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10179 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10180 #if USE_NEW_CUSTOM_VALUE
10181 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10183 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10185 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10186 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10187 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10188 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10189 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10190 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10191 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10192 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10193 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10194 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10195 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10198 int action_arg_number_old =
10199 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10200 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10201 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10202 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10203 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10206 int action_arg_number_new =
10207 getModifiedActionNumber(action_arg_number_old,
10208 action_mode, action_arg_number,
10209 action_arg_number_min, action_arg_number_max);
10212 int trigger_player_bits = change->actual_trigger_player_bits;
10214 int trigger_player_bits =
10215 (change->actual_trigger_player >= EL_PLAYER_1 &&
10216 change->actual_trigger_player <= EL_PLAYER_4 ?
10217 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
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 :
10227 /* ---------- execute action -------------------------------------------- */
10229 switch (action_type)
10236 /* ---------- level actions ------------------------------------------- */
10238 case CA_RESTART_LEVEL:
10240 game.restart_level = TRUE;
10245 case CA_SHOW_ENVELOPE:
10247 int element = getSpecialActionElement(action_arg_element,
10248 action_arg_number, EL_ENVELOPE_1);
10250 if (IS_ENVELOPE(element))
10251 local_player->show_envelope = element;
10256 case CA_SET_LEVEL_TIME:
10258 if (level.time > 0) /* only modify limited time value */
10260 TimeLeft = action_arg_number_new;
10263 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10265 DisplayGameControlValues();
10267 DrawGameValue_Time(TimeLeft);
10270 if (!TimeLeft && setup.time_limit)
10271 for (i = 0; i < MAX_PLAYERS; i++)
10272 KillPlayer(&stored_player[i]);
10278 case CA_SET_LEVEL_SCORE:
10280 local_player->score = action_arg_number_new;
10283 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10285 DisplayGameControlValues();
10287 DrawGameValue_Score(local_player->score);
10293 case CA_SET_LEVEL_GEMS:
10295 local_player->gems_still_needed = action_arg_number_new;
10298 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10300 DisplayGameControlValues();
10302 DrawGameValue_Emeralds(local_player->gems_still_needed);
10308 #if !USE_PLAYER_GRAVITY
10309 case CA_SET_LEVEL_GRAVITY:
10311 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10312 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10313 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10319 case CA_SET_LEVEL_WIND:
10321 game.wind_direction = action_arg_direction;
10326 /* ---------- player actions ------------------------------------------ */
10328 case CA_MOVE_PLAYER:
10330 /* automatically move to the next field in specified direction */
10331 for (i = 0; i < MAX_PLAYERS; i++)
10332 if (trigger_player_bits & (1 << i))
10333 stored_player[i].programmed_action = action_arg_direction;
10338 case CA_EXIT_PLAYER:
10340 for (i = 0; i < MAX_PLAYERS; i++)
10341 if (action_arg_player_bits & (1 << i))
10342 PlayerWins(&stored_player[i]);
10347 case CA_KILL_PLAYER:
10349 for (i = 0; i < MAX_PLAYERS; i++)
10350 if (action_arg_player_bits & (1 << i))
10351 KillPlayer(&stored_player[i]);
10356 case CA_SET_PLAYER_KEYS:
10358 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10359 int element = getSpecialActionElement(action_arg_element,
10360 action_arg_number, EL_KEY_1);
10362 if (IS_KEY(element))
10364 for (i = 0; i < MAX_PLAYERS; i++)
10366 if (trigger_player_bits & (1 << i))
10368 stored_player[i].key[KEY_NR(element)] = key_state;
10370 DrawGameDoorValues();
10378 case CA_SET_PLAYER_SPEED:
10380 for (i = 0; i < MAX_PLAYERS; i++)
10382 if (trigger_player_bits & (1 << i))
10384 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10386 if (action_arg == CA_ARG_SPEED_FASTER &&
10387 stored_player[i].cannot_move)
10389 action_arg_number = STEPSIZE_VERY_SLOW;
10391 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10392 action_arg == CA_ARG_SPEED_FASTER)
10394 action_arg_number = 2;
10395 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10398 else if (action_arg == CA_ARG_NUMBER_RESET)
10400 action_arg_number = level.initial_player_stepsize[i];
10404 getModifiedActionNumber(move_stepsize,
10407 action_arg_number_min,
10408 action_arg_number_max);
10410 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10417 case CA_SET_PLAYER_SHIELD:
10419 for (i = 0; i < MAX_PLAYERS; i++)
10421 if (trigger_player_bits & (1 << i))
10423 if (action_arg == CA_ARG_SHIELD_OFF)
10425 stored_player[i].shield_normal_time_left = 0;
10426 stored_player[i].shield_deadly_time_left = 0;
10428 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10430 stored_player[i].shield_normal_time_left = 999999;
10432 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10434 stored_player[i].shield_normal_time_left = 999999;
10435 stored_player[i].shield_deadly_time_left = 999999;
10443 #if USE_PLAYER_GRAVITY
10444 case CA_SET_PLAYER_GRAVITY:
10446 for (i = 0; i < MAX_PLAYERS; i++)
10448 if (trigger_player_bits & (1 << i))
10450 stored_player[i].gravity =
10451 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10452 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10453 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10454 stored_player[i].gravity);
10462 case CA_SET_PLAYER_ARTWORK:
10464 for (i = 0; i < MAX_PLAYERS; i++)
10466 if (trigger_player_bits & (1 << i))
10468 int artwork_element = action_arg_element;
10470 if (action_arg == CA_ARG_ELEMENT_RESET)
10472 (level.use_artwork_element[i] ? level.artwork_element[i] :
10473 stored_player[i].element_nr);
10475 #if USE_GFX_RESET_PLAYER_ARTWORK
10476 if (stored_player[i].artwork_element != artwork_element)
10477 stored_player[i].Frame = 0;
10480 stored_player[i].artwork_element = artwork_element;
10482 SetPlayerWaiting(&stored_player[i], FALSE);
10484 /* set number of special actions for bored and sleeping animation */
10485 stored_player[i].num_special_action_bored =
10486 get_num_special_action(artwork_element,
10487 ACTION_BORING_1, ACTION_BORING_LAST);
10488 stored_player[i].num_special_action_sleeping =
10489 get_num_special_action(artwork_element,
10490 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10497 /* ---------- CE actions ---------------------------------------------- */
10499 case CA_SET_CE_VALUE:
10501 #if USE_NEW_CUSTOM_VALUE
10502 int last_ce_value = CustomValue[x][y];
10504 CustomValue[x][y] = action_arg_number_new;
10506 if (CustomValue[x][y] != last_ce_value)
10508 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10509 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10511 if (CustomValue[x][y] == 0)
10513 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10514 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10522 case CA_SET_CE_SCORE:
10524 #if USE_NEW_CUSTOM_VALUE
10525 int last_ce_score = ei->collect_score;
10527 ei->collect_score = action_arg_number_new;
10529 if (ei->collect_score != last_ce_score)
10531 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10532 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10534 if (ei->collect_score == 0)
10538 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10539 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10542 This is a very special case that seems to be a mixture between
10543 CheckElementChange() and CheckTriggeredElementChange(): while
10544 the first one only affects single elements that are triggered
10545 directly, the second one affects multiple elements in the playfield
10546 that are triggered indirectly by another element. This is a third
10547 case: Changing the CE score always affects multiple identical CEs,
10548 so every affected CE must be checked, not only the single CE for
10549 which the CE score was changed in the first place (as every instance
10550 of that CE shares the same CE score, and therefore also can change)!
10552 SCAN_PLAYFIELD(xx, yy)
10554 if (Feld[xx][yy] == element)
10555 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10556 CE_SCORE_GETS_ZERO);
10565 /* ---------- engine actions ------------------------------------------ */
10567 case CA_SET_ENGINE_SCAN_MODE:
10569 InitPlayfieldScanMode(action_arg);
10579 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10581 int old_element = Feld[x][y];
10582 int new_element = GetElementFromGroupElement(element);
10583 int previous_move_direction = MovDir[x][y];
10584 #if USE_NEW_CUSTOM_VALUE
10585 int last_ce_value = CustomValue[x][y];
10587 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10588 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10589 boolean add_player_onto_element = (new_element_is_player &&
10590 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10591 /* this breaks SnakeBite when a snake is
10592 halfway through a door that closes */
10593 /* NOW FIXED AT LEVEL INIT IN files.c */
10594 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10596 IS_WALKABLE(old_element));
10599 /* check if element under the player changes from accessible to unaccessible
10600 (needed for special case of dropping element which then changes) */
10601 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10602 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10610 if (!add_player_onto_element)
10612 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10613 RemoveMovingField(x, y);
10617 Feld[x][y] = new_element;
10619 #if !USE_GFX_RESET_GFX_ANIMATION
10620 ResetGfxAnimation(x, y);
10621 ResetRandomAnimationValue(x, y);
10624 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10625 MovDir[x][y] = previous_move_direction;
10627 #if USE_NEW_CUSTOM_VALUE
10628 if (element_info[new_element].use_last_ce_value)
10629 CustomValue[x][y] = last_ce_value;
10632 InitField_WithBug1(x, y, FALSE);
10634 new_element = Feld[x][y]; /* element may have changed */
10636 #if USE_GFX_RESET_GFX_ANIMATION
10637 ResetGfxAnimation(x, y);
10638 ResetRandomAnimationValue(x, y);
10641 TEST_DrawLevelField(x, y);
10643 if (GFX_CRUMBLED(new_element))
10644 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10648 /* check if element under the player changes from accessible to unaccessible
10649 (needed for special case of dropping element which then changes) */
10650 /* (must be checked after creating new element for walkable group elements) */
10651 #if USE_FIX_KILLED_BY_NON_WALKABLE
10652 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10653 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10660 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10661 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10670 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10671 if (new_element_is_player)
10672 RelocatePlayer(x, y, new_element);
10675 ChangeCount[x][y]++; /* count number of changes in the same frame */
10677 TestIfBadThingTouchesPlayer(x, y);
10678 TestIfPlayerTouchesCustomElement(x, y);
10679 TestIfElementTouchesCustomElement(x, y);
10682 static void CreateField(int x, int y, int element)
10684 CreateFieldExt(x, y, element, FALSE);
10687 static void CreateElementFromChange(int x, int y, int element)
10689 element = GET_VALID_RUNTIME_ELEMENT(element);
10691 #if USE_STOP_CHANGED_ELEMENTS
10692 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10694 int old_element = Feld[x][y];
10696 /* prevent changed element from moving in same engine frame
10697 unless both old and new element can either fall or move */
10698 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10699 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10704 CreateFieldExt(x, y, element, TRUE);
10707 static boolean ChangeElement(int x, int y, int element, int page)
10709 struct ElementInfo *ei = &element_info[element];
10710 struct ElementChangeInfo *change = &ei->change_page[page];
10711 int ce_value = CustomValue[x][y];
10712 int ce_score = ei->collect_score;
10713 int target_element;
10714 int old_element = Feld[x][y];
10716 /* always use default change event to prevent running into a loop */
10717 if (ChangeEvent[x][y] == -1)
10718 ChangeEvent[x][y] = CE_DELAY;
10720 if (ChangeEvent[x][y] == CE_DELAY)
10722 /* reset actual trigger element, trigger player and action element */
10723 change->actual_trigger_element = EL_EMPTY;
10724 change->actual_trigger_player = EL_PLAYER_1;
10725 change->actual_trigger_player_bits = CH_PLAYER_1;
10726 change->actual_trigger_side = CH_SIDE_NONE;
10727 change->actual_trigger_ce_value = 0;
10728 change->actual_trigger_ce_score = 0;
10731 /* do not change elements more than a specified maximum number of changes */
10732 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10735 ChangeCount[x][y]++; /* count number of changes in the same frame */
10737 if (change->explode)
10744 if (change->use_target_content)
10746 boolean complete_replace = TRUE;
10747 boolean can_replace[3][3];
10750 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10753 boolean is_walkable;
10754 boolean is_diggable;
10755 boolean is_collectible;
10756 boolean is_removable;
10757 boolean is_destructible;
10758 int ex = x + xx - 1;
10759 int ey = y + yy - 1;
10760 int content_element = change->target_content.e[xx][yy];
10763 can_replace[xx][yy] = TRUE;
10765 if (ex == x && ey == y) /* do not check changing element itself */
10768 if (content_element == EL_EMPTY_SPACE)
10770 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10775 if (!IN_LEV_FIELD(ex, ey))
10777 can_replace[xx][yy] = FALSE;
10778 complete_replace = FALSE;
10785 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10786 e = MovingOrBlocked2Element(ex, ey);
10788 is_empty = (IS_FREE(ex, ey) ||
10789 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10791 is_walkable = (is_empty || IS_WALKABLE(e));
10792 is_diggable = (is_empty || IS_DIGGABLE(e));
10793 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10794 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10795 is_removable = (is_diggable || is_collectible);
10797 can_replace[xx][yy] =
10798 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10799 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10800 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10801 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10802 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10803 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10804 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10806 if (!can_replace[xx][yy])
10807 complete_replace = FALSE;
10810 if (!change->only_if_complete || complete_replace)
10812 boolean something_has_changed = FALSE;
10814 if (change->only_if_complete && change->use_random_replace &&
10815 RND(100) < change->random_percentage)
10818 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10820 int ex = x + xx - 1;
10821 int ey = y + yy - 1;
10822 int content_element;
10824 if (can_replace[xx][yy] && (!change->use_random_replace ||
10825 RND(100) < change->random_percentage))
10827 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10828 RemoveMovingField(ex, ey);
10830 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10832 content_element = change->target_content.e[xx][yy];
10833 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10834 ce_value, ce_score);
10836 CreateElementFromChange(ex, ey, target_element);
10838 something_has_changed = TRUE;
10840 /* for symmetry reasons, freeze newly created border elements */
10841 if (ex != x || ey != y)
10842 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10846 if (something_has_changed)
10848 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10849 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10855 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10856 ce_value, ce_score);
10858 if (element == EL_DIAGONAL_GROWING ||
10859 element == EL_DIAGONAL_SHRINKING)
10861 target_element = Store[x][y];
10863 Store[x][y] = EL_EMPTY;
10866 CreateElementFromChange(x, y, target_element);
10868 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10869 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10872 /* this uses direct change before indirect change */
10873 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10878 #if USE_NEW_DELAYED_ACTION
10880 static void HandleElementChange(int x, int y, int page)
10882 int element = MovingOrBlocked2Element(x, y);
10883 struct ElementInfo *ei = &element_info[element];
10884 struct ElementChangeInfo *change = &ei->change_page[page];
10887 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10888 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10891 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10892 x, y, element, element_info[element].token_name);
10893 printf("HandleElementChange(): This should never happen!\n");
10898 /* this can happen with classic bombs on walkable, changing elements */
10899 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10902 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10903 ChangeDelay[x][y] = 0;
10909 if (ChangeDelay[x][y] == 0) /* initialize element change */
10911 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10913 if (change->can_change)
10916 /* !!! not clear why graphic animation should be reset at all here !!! */
10917 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10918 #if USE_GFX_RESET_WHEN_NOT_MOVING
10919 /* when a custom element is about to change (for example by change delay),
10920 do not reset graphic animation when the custom element is moving */
10921 if (!IS_MOVING(x, y))
10924 ResetGfxAnimation(x, y);
10925 ResetRandomAnimationValue(x, y);
10929 if (change->pre_change_function)
10930 change->pre_change_function(x, y);
10934 ChangeDelay[x][y]--;
10936 if (ChangeDelay[x][y] != 0) /* continue element change */
10938 if (change->can_change)
10940 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10942 if (IS_ANIMATED(graphic))
10943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10945 if (change->change_function)
10946 change->change_function(x, y);
10949 else /* finish element change */
10951 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10953 page = ChangePage[x][y];
10954 ChangePage[x][y] = -1;
10956 change = &ei->change_page[page];
10959 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10961 ChangeDelay[x][y] = 1; /* try change after next move step */
10962 ChangePage[x][y] = page; /* remember page to use for change */
10967 if (change->can_change)
10969 if (ChangeElement(x, y, element, page))
10971 if (change->post_change_function)
10972 change->post_change_function(x, y);
10976 if (change->has_action)
10977 ExecuteCustomElementAction(x, y, element, page);
10983 static void HandleElementChange(int x, int y, int page)
10985 int element = MovingOrBlocked2Element(x, y);
10986 struct ElementInfo *ei = &element_info[element];
10987 struct ElementChangeInfo *change = &ei->change_page[page];
10990 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10993 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10994 x, y, element, element_info[element].token_name);
10995 printf("HandleElementChange(): This should never happen!\n");
11000 /* this can happen with classic bombs on walkable, changing elements */
11001 if (!CAN_CHANGE(element))
11004 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11005 ChangeDelay[x][y] = 0;
11011 if (ChangeDelay[x][y] == 0) /* initialize element change */
11013 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11015 ResetGfxAnimation(x, y);
11016 ResetRandomAnimationValue(x, y);
11018 if (change->pre_change_function)
11019 change->pre_change_function(x, y);
11022 ChangeDelay[x][y]--;
11024 if (ChangeDelay[x][y] != 0) /* continue element change */
11026 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11028 if (IS_ANIMATED(graphic))
11029 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11031 if (change->change_function)
11032 change->change_function(x, y);
11034 else /* finish element change */
11036 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11038 page = ChangePage[x][y];
11039 ChangePage[x][y] = -1;
11041 change = &ei->change_page[page];
11044 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11046 ChangeDelay[x][y] = 1; /* try change after next move step */
11047 ChangePage[x][y] = page; /* remember page to use for change */
11052 if (ChangeElement(x, y, element, page))
11054 if (change->post_change_function)
11055 change->post_change_function(x, y);
11062 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11063 int trigger_element,
11065 int trigger_player,
11069 boolean change_done_any = FALSE;
11070 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11073 if (!(trigger_events[trigger_element][trigger_event]))
11077 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11078 trigger_event, recursion_loop_depth, recursion_loop_detected,
11079 recursion_loop_element, EL_NAME(recursion_loop_element));
11082 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11084 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11086 int element = EL_CUSTOM_START + i;
11087 boolean change_done = FALSE;
11090 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11091 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11094 for (p = 0; p < element_info[element].num_change_pages; p++)
11096 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11098 if (change->can_change_or_has_action &&
11099 change->has_event[trigger_event] &&
11100 change->trigger_side & trigger_side &&
11101 change->trigger_player & trigger_player &&
11102 change->trigger_page & trigger_page_bits &&
11103 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11105 change->actual_trigger_element = trigger_element;
11106 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11107 change->actual_trigger_player_bits = trigger_player;
11108 change->actual_trigger_side = trigger_side;
11109 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11110 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11112 if ((change->can_change && !change_done) || change->has_action)
11116 SCAN_PLAYFIELD(x, y)
11118 if (Feld[x][y] == element)
11120 if (change->can_change && !change_done)
11122 ChangeDelay[x][y] = 1;
11123 ChangeEvent[x][y] = trigger_event;
11125 HandleElementChange(x, y, p);
11127 #if USE_NEW_DELAYED_ACTION
11128 else if (change->has_action)
11130 ExecuteCustomElementAction(x, y, element, p);
11131 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11134 if (change->has_action)
11136 ExecuteCustomElementAction(x, y, element, p);
11137 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11143 if (change->can_change)
11145 change_done = TRUE;
11146 change_done_any = TRUE;
11153 RECURSION_LOOP_DETECTION_END();
11155 return change_done_any;
11158 static boolean CheckElementChangeExt(int x, int y,
11160 int trigger_element,
11162 int trigger_player,
11165 boolean change_done = FALSE;
11168 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11169 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11172 if (Feld[x][y] == EL_BLOCKED)
11174 Blocked2Moving(x, y, &x, &y);
11175 element = Feld[x][y];
11179 /* check if element has already changed */
11180 if (Feld[x][y] != element)
11183 /* check if element has already changed or is about to change after moving */
11184 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11185 Feld[x][y] != element) ||
11187 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11188 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11189 ChangePage[x][y] != -1)))
11194 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11195 trigger_event, recursion_loop_depth, recursion_loop_detected,
11196 recursion_loop_element, EL_NAME(recursion_loop_element));
11199 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11201 for (p = 0; p < element_info[element].num_change_pages; p++)
11203 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11205 /* check trigger element for all events where the element that is checked
11206 for changing interacts with a directly adjacent element -- this is
11207 different to element changes that affect other elements to change on the
11208 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11209 boolean check_trigger_element =
11210 (trigger_event == CE_TOUCHING_X ||
11211 trigger_event == CE_HITTING_X ||
11212 trigger_event == CE_HIT_BY_X ||
11214 /* this one was forgotten until 3.2.3 */
11215 trigger_event == CE_DIGGING_X);
11218 if (change->can_change_or_has_action &&
11219 change->has_event[trigger_event] &&
11220 change->trigger_side & trigger_side &&
11221 change->trigger_player & trigger_player &&
11222 (!check_trigger_element ||
11223 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11225 change->actual_trigger_element = trigger_element;
11226 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11227 change->actual_trigger_player_bits = trigger_player;
11228 change->actual_trigger_side = trigger_side;
11229 change->actual_trigger_ce_value = CustomValue[x][y];
11230 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11232 /* special case: trigger element not at (x,y) position for some events */
11233 if (check_trigger_element)
11245 { 0, 0 }, { 0, 0 }, { 0, 0 },
11249 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11250 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11252 change->actual_trigger_ce_value = CustomValue[xx][yy];
11253 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11256 if (change->can_change && !change_done)
11258 ChangeDelay[x][y] = 1;
11259 ChangeEvent[x][y] = trigger_event;
11261 HandleElementChange(x, y, p);
11263 change_done = TRUE;
11265 #if USE_NEW_DELAYED_ACTION
11266 else if (change->has_action)
11268 ExecuteCustomElementAction(x, y, element, p);
11269 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11272 if (change->has_action)
11274 ExecuteCustomElementAction(x, y, element, p);
11275 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11281 RECURSION_LOOP_DETECTION_END();
11283 return change_done;
11286 static void PlayPlayerSound(struct PlayerInfo *player)
11288 int jx = player->jx, jy = player->jy;
11289 int sound_element = player->artwork_element;
11290 int last_action = player->last_action_waiting;
11291 int action = player->action_waiting;
11293 if (player->is_waiting)
11295 if (action != last_action)
11296 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11298 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11302 if (action != last_action)
11303 StopSound(element_info[sound_element].sound[last_action]);
11305 if (last_action == ACTION_SLEEPING)
11306 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11310 static void PlayAllPlayersSound()
11314 for (i = 0; i < MAX_PLAYERS; i++)
11315 if (stored_player[i].active)
11316 PlayPlayerSound(&stored_player[i]);
11319 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11321 boolean last_waiting = player->is_waiting;
11322 int move_dir = player->MovDir;
11324 player->dir_waiting = move_dir;
11325 player->last_action_waiting = player->action_waiting;
11329 if (!last_waiting) /* not waiting -> waiting */
11331 player->is_waiting = TRUE;
11333 player->frame_counter_bored =
11335 game.player_boring_delay_fixed +
11336 GetSimpleRandom(game.player_boring_delay_random);
11337 player->frame_counter_sleeping =
11339 game.player_sleeping_delay_fixed +
11340 GetSimpleRandom(game.player_sleeping_delay_random);
11342 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11345 if (game.player_sleeping_delay_fixed +
11346 game.player_sleeping_delay_random > 0 &&
11347 player->anim_delay_counter == 0 &&
11348 player->post_delay_counter == 0 &&
11349 FrameCounter >= player->frame_counter_sleeping)
11350 player->is_sleeping = TRUE;
11351 else if (game.player_boring_delay_fixed +
11352 game.player_boring_delay_random > 0 &&
11353 FrameCounter >= player->frame_counter_bored)
11354 player->is_bored = TRUE;
11356 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11357 player->is_bored ? ACTION_BORING :
11360 if (player->is_sleeping && player->use_murphy)
11362 /* special case for sleeping Murphy when leaning against non-free tile */
11364 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11365 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11366 !IS_MOVING(player->jx - 1, player->jy)))
11367 move_dir = MV_LEFT;
11368 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11369 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11370 !IS_MOVING(player->jx + 1, player->jy)))
11371 move_dir = MV_RIGHT;
11373 player->is_sleeping = FALSE;
11375 player->dir_waiting = move_dir;
11378 if (player->is_sleeping)
11380 if (player->num_special_action_sleeping > 0)
11382 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11384 int last_special_action = player->special_action_sleeping;
11385 int num_special_action = player->num_special_action_sleeping;
11386 int special_action =
11387 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11388 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11389 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11390 last_special_action + 1 : ACTION_SLEEPING);
11391 int special_graphic =
11392 el_act_dir2img(player->artwork_element, special_action, move_dir);
11394 player->anim_delay_counter =
11395 graphic_info[special_graphic].anim_delay_fixed +
11396 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11397 player->post_delay_counter =
11398 graphic_info[special_graphic].post_delay_fixed +
11399 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11401 player->special_action_sleeping = special_action;
11404 if (player->anim_delay_counter > 0)
11406 player->action_waiting = player->special_action_sleeping;
11407 player->anim_delay_counter--;
11409 else if (player->post_delay_counter > 0)
11411 player->post_delay_counter--;
11415 else if (player->is_bored)
11417 if (player->num_special_action_bored > 0)
11419 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11421 int special_action =
11422 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11423 int special_graphic =
11424 el_act_dir2img(player->artwork_element, special_action, move_dir);
11426 player->anim_delay_counter =
11427 graphic_info[special_graphic].anim_delay_fixed +
11428 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11429 player->post_delay_counter =
11430 graphic_info[special_graphic].post_delay_fixed +
11431 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11433 player->special_action_bored = special_action;
11436 if (player->anim_delay_counter > 0)
11438 player->action_waiting = player->special_action_bored;
11439 player->anim_delay_counter--;
11441 else if (player->post_delay_counter > 0)
11443 player->post_delay_counter--;
11448 else if (last_waiting) /* waiting -> not waiting */
11450 player->is_waiting = FALSE;
11451 player->is_bored = FALSE;
11452 player->is_sleeping = FALSE;
11454 player->frame_counter_bored = -1;
11455 player->frame_counter_sleeping = -1;
11457 player->anim_delay_counter = 0;
11458 player->post_delay_counter = 0;
11460 player->dir_waiting = player->MovDir;
11461 player->action_waiting = ACTION_DEFAULT;
11463 player->special_action_bored = ACTION_DEFAULT;
11464 player->special_action_sleeping = ACTION_DEFAULT;
11468 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11470 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11471 int left = player_action & JOY_LEFT;
11472 int right = player_action & JOY_RIGHT;
11473 int up = player_action & JOY_UP;
11474 int down = player_action & JOY_DOWN;
11475 int button1 = player_action & JOY_BUTTON_1;
11476 int button2 = player_action & JOY_BUTTON_2;
11477 int dx = (left ? -1 : right ? 1 : 0);
11478 int dy = (up ? -1 : down ? 1 : 0);
11480 if (!player->active || tape.pausing)
11486 snapped = SnapField(player, dx, dy);
11490 dropped = DropElement(player);
11492 moved = MovePlayer(player, dx, dy);
11495 if (tape.single_step && tape.recording && !tape.pausing)
11497 if (button1 || (dropped && !moved))
11499 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11500 SnapField(player, 0, 0); /* stop snapping */
11504 SetPlayerWaiting(player, FALSE);
11506 return player_action;
11510 /* no actions for this player (no input at player's configured device) */
11512 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11513 SnapField(player, 0, 0);
11514 CheckGravityMovementWhenNotMoving(player);
11516 if (player->MovPos == 0)
11517 SetPlayerWaiting(player, TRUE);
11519 if (player->MovPos == 0) /* needed for tape.playing */
11520 player->is_moving = FALSE;
11522 player->is_dropping = FALSE;
11523 player->is_dropping_pressed = FALSE;
11524 player->drop_pressed_delay = 0;
11530 static void CheckLevelTime()
11534 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11536 if (level.native_em_level->lev->home == 0) /* all players at home */
11538 PlayerWins(local_player);
11540 AllPlayersGone = TRUE;
11542 level.native_em_level->lev->home = -1;
11545 if (level.native_em_level->ply[0]->alive == 0 &&
11546 level.native_em_level->ply[1]->alive == 0 &&
11547 level.native_em_level->ply[2]->alive == 0 &&
11548 level.native_em_level->ply[3]->alive == 0) /* all dead */
11549 AllPlayersGone = TRUE;
11552 if (TimeFrames >= FRAMES_PER_SECOND)
11557 for (i = 0; i < MAX_PLAYERS; i++)
11559 struct PlayerInfo *player = &stored_player[i];
11561 if (SHIELD_ON(player))
11563 player->shield_normal_time_left--;
11565 if (player->shield_deadly_time_left > 0)
11566 player->shield_deadly_time_left--;
11570 if (!local_player->LevelSolved && !level.use_step_counter)
11578 if (TimeLeft <= 10 && setup.time_limit)
11579 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11582 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11584 DisplayGameControlValues();
11586 DrawGameValue_Time(TimeLeft);
11589 if (!TimeLeft && setup.time_limit)
11591 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11592 level.native_em_level->lev->killed_out_of_time = TRUE;
11594 for (i = 0; i < MAX_PLAYERS; i++)
11595 KillPlayer(&stored_player[i]);
11599 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11601 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11603 DisplayGameControlValues();
11606 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11607 DrawGameValue_Time(TimePlayed);
11610 level.native_em_level->lev->time =
11611 (level.time == 0 ? TimePlayed : TimeLeft);
11614 if (tape.recording || tape.playing)
11615 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11619 UpdateAndDisplayGameControlValues();
11621 UpdateGameDoorValues();
11622 DrawGameDoorValues();
11626 void AdvanceFrameAndPlayerCounters(int player_nr)
11630 /* advance frame counters (global frame counter and time frame counter) */
11634 /* advance player counters (counters for move delay, move animation etc.) */
11635 for (i = 0; i < MAX_PLAYERS; i++)
11637 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11638 int move_delay_value = stored_player[i].move_delay_value;
11639 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11641 if (!advance_player_counters) /* not all players may be affected */
11644 #if USE_NEW_PLAYER_ANIM
11645 if (move_frames == 0) /* less than one move per game frame */
11647 int stepsize = TILEX / move_delay_value;
11648 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11649 int count = (stored_player[i].is_moving ?
11650 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11652 if (count % delay == 0)
11657 stored_player[i].Frame += move_frames;
11659 if (stored_player[i].MovPos != 0)
11660 stored_player[i].StepFrame += move_frames;
11662 if (stored_player[i].move_delay > 0)
11663 stored_player[i].move_delay--;
11665 /* due to bugs in previous versions, counter must count up, not down */
11666 if (stored_player[i].push_delay != -1)
11667 stored_player[i].push_delay++;
11669 if (stored_player[i].drop_delay > 0)
11670 stored_player[i].drop_delay--;
11672 if (stored_player[i].is_dropping_pressed)
11673 stored_player[i].drop_pressed_delay++;
11677 void StartGameActions(boolean init_network_game, boolean record_tape,
11680 unsigned long new_random_seed = InitRND(random_seed);
11683 TapeStartRecording(new_random_seed);
11685 #if defined(NETWORK_AVALIABLE)
11686 if (init_network_game)
11688 SendToServer_StartPlaying();
11699 static unsigned long game_frame_delay = 0;
11700 unsigned long game_frame_delay_value;
11701 byte *recorded_player_action;
11702 byte summarized_player_action = 0;
11703 byte tape_action[MAX_PLAYERS];
11706 /* detect endless loops, caused by custom element programming */
11707 if (recursion_loop_detected && recursion_loop_depth == 0)
11709 char *message = getStringCat3("Internal Error ! Element ",
11710 EL_NAME(recursion_loop_element),
11711 " caused endless loop ! Quit the game ?");
11713 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11714 EL_NAME(recursion_loop_element));
11716 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11718 recursion_loop_detected = FALSE; /* if game should be continued */
11725 if (game.restart_level)
11726 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11728 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11730 if (level.native_em_level->lev->home == 0) /* all players at home */
11732 PlayerWins(local_player);
11734 AllPlayersGone = TRUE;
11736 level.native_em_level->lev->home = -1;
11739 if (level.native_em_level->ply[0]->alive == 0 &&
11740 level.native_em_level->ply[1]->alive == 0 &&
11741 level.native_em_level->ply[2]->alive == 0 &&
11742 level.native_em_level->ply[3]->alive == 0) /* all dead */
11743 AllPlayersGone = TRUE;
11746 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11749 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11752 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11755 game_frame_delay_value =
11756 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11758 if (tape.playing && tape.warp_forward && !tape.pausing)
11759 game_frame_delay_value = 0;
11761 /* ---------- main game synchronization point ---------- */
11763 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11765 if (network_playing && !network_player_action_received)
11767 /* try to get network player actions in time */
11769 #if defined(NETWORK_AVALIABLE)
11770 /* last chance to get network player actions without main loop delay */
11771 HandleNetworking();
11774 /* game was quit by network peer */
11775 if (game_status != GAME_MODE_PLAYING)
11778 if (!network_player_action_received)
11779 return; /* failed to get network player actions in time */
11781 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11787 /* at this point we know that we really continue executing the game */
11789 network_player_action_received = FALSE;
11791 /* when playing tape, read previously recorded player input from tape data */
11792 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11795 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11800 if (tape.set_centered_player)
11802 game.centered_player_nr_next = tape.centered_player_nr_next;
11803 game.set_centered_player = TRUE;
11806 for (i = 0; i < MAX_PLAYERS; i++)
11808 summarized_player_action |= stored_player[i].action;
11810 if (!network_playing)
11811 stored_player[i].effective_action = stored_player[i].action;
11814 #if defined(NETWORK_AVALIABLE)
11815 if (network_playing)
11816 SendToServer_MovePlayer(summarized_player_action);
11819 if (!options.network && !setup.team_mode)
11820 local_player->effective_action = summarized_player_action;
11822 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11824 for (i = 0; i < MAX_PLAYERS; i++)
11825 stored_player[i].effective_action =
11826 (i == game.centered_player_nr ? summarized_player_action : 0);
11829 if (recorded_player_action != NULL)
11830 for (i = 0; i < MAX_PLAYERS; i++)
11831 stored_player[i].effective_action = recorded_player_action[i];
11833 for (i = 0; i < MAX_PLAYERS; i++)
11835 tape_action[i] = stored_player[i].effective_action;
11837 /* (this can only happen in the R'n'D game engine) */
11838 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11839 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11842 /* only record actions from input devices, but not programmed actions */
11843 if (tape.recording)
11844 TapeRecordAction(tape_action);
11846 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11848 GameActions_EM_Main();
11856 void GameActions_EM_Main()
11858 byte effective_action[MAX_PLAYERS];
11859 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11862 for (i = 0; i < MAX_PLAYERS; i++)
11863 effective_action[i] = stored_player[i].effective_action;
11865 GameActions_EM(effective_action, warp_mode);
11869 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11872 void GameActions_RND()
11874 int magic_wall_x = 0, magic_wall_y = 0;
11875 int i, x, y, element, graphic;
11877 InitPlayfieldScanModeVars();
11879 #if USE_ONE_MORE_CHANGE_PER_FRAME
11880 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11882 SCAN_PLAYFIELD(x, y)
11884 ChangeCount[x][y] = 0;
11885 ChangeEvent[x][y] = -1;
11890 if (game.set_centered_player)
11892 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11894 /* switching to "all players" only possible if all players fit to screen */
11895 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11897 game.centered_player_nr_next = game.centered_player_nr;
11898 game.set_centered_player = FALSE;
11901 /* do not switch focus to non-existing (or non-active) player */
11902 if (game.centered_player_nr_next >= 0 &&
11903 !stored_player[game.centered_player_nr_next].active)
11905 game.centered_player_nr_next = game.centered_player_nr;
11906 game.set_centered_player = FALSE;
11910 if (game.set_centered_player &&
11911 ScreenMovPos == 0) /* screen currently aligned at tile position */
11915 if (game.centered_player_nr_next == -1)
11917 setScreenCenteredToAllPlayers(&sx, &sy);
11921 sx = stored_player[game.centered_player_nr_next].jx;
11922 sy = stored_player[game.centered_player_nr_next].jy;
11925 game.centered_player_nr = game.centered_player_nr_next;
11926 game.set_centered_player = FALSE;
11928 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11929 DrawGameDoorValues();
11932 for (i = 0; i < MAX_PLAYERS; i++)
11934 int actual_player_action = stored_player[i].effective_action;
11937 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11938 - rnd_equinox_tetrachloride 048
11939 - rnd_equinox_tetrachloride_ii 096
11940 - rnd_emanuel_schmieg 002
11941 - doctor_sloan_ww 001, 020
11943 if (stored_player[i].MovPos == 0)
11944 CheckGravityMovement(&stored_player[i]);
11947 /* overwrite programmed action with tape action */
11948 if (stored_player[i].programmed_action)
11949 actual_player_action = stored_player[i].programmed_action;
11951 PlayerActions(&stored_player[i], actual_player_action);
11953 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11956 ScrollScreen(NULL, SCROLL_GO_ON);
11958 /* for backwards compatibility, the following code emulates a fixed bug that
11959 occured when pushing elements (causing elements that just made their last
11960 pushing step to already (if possible) make their first falling step in the
11961 same game frame, which is bad); this code is also needed to use the famous
11962 "spring push bug" which is used in older levels and might be wanted to be
11963 used also in newer levels, but in this case the buggy pushing code is only
11964 affecting the "spring" element and no other elements */
11966 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11968 for (i = 0; i < MAX_PLAYERS; i++)
11970 struct PlayerInfo *player = &stored_player[i];
11971 int x = player->jx;
11972 int y = player->jy;
11974 if (player->active && player->is_pushing && player->is_moving &&
11976 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11977 Feld[x][y] == EL_SPRING))
11979 ContinueMoving(x, y);
11981 /* continue moving after pushing (this is actually a bug) */
11982 if (!IS_MOVING(x, y))
11983 Stop[x][y] = FALSE;
11989 debug_print_timestamp(0, "start main loop profiling");
11992 SCAN_PLAYFIELD(x, y)
11994 ChangeCount[x][y] = 0;
11995 ChangeEvent[x][y] = -1;
11997 /* this must be handled before main playfield loop */
11998 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12001 if (MovDelay[x][y] <= 0)
12005 #if USE_NEW_SNAP_DELAY
12006 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12009 if (MovDelay[x][y] <= 0)
12012 TEST_DrawLevelField(x, y);
12014 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12020 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12022 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12023 printf("GameActions(): This should never happen!\n");
12025 ChangePage[x][y] = -1;
12029 Stop[x][y] = FALSE;
12030 if (WasJustMoving[x][y] > 0)
12031 WasJustMoving[x][y]--;
12032 if (WasJustFalling[x][y] > 0)
12033 WasJustFalling[x][y]--;
12034 if (CheckCollision[x][y] > 0)
12035 CheckCollision[x][y]--;
12036 if (CheckImpact[x][y] > 0)
12037 CheckImpact[x][y]--;
12041 /* reset finished pushing action (not done in ContinueMoving() to allow
12042 continuous pushing animation for elements with zero push delay) */
12043 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12045 ResetGfxAnimation(x, y);
12046 TEST_DrawLevelField(x, y);
12050 if (IS_BLOCKED(x, y))
12054 Blocked2Moving(x, y, &oldx, &oldy);
12055 if (!IS_MOVING(oldx, oldy))
12057 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12058 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12059 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12060 printf("GameActions(): This should never happen!\n");
12067 debug_print_timestamp(0, "- time for pre-main loop:");
12070 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12071 SCAN_PLAYFIELD(x, y)
12073 element = Feld[x][y];
12074 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12079 int element2 = element;
12080 int graphic2 = graphic;
12082 int element2 = Feld[x][y];
12083 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12085 int last_gfx_frame = GfxFrame[x][y];
12087 if (graphic_info[graphic2].anim_global_sync)
12088 GfxFrame[x][y] = FrameCounter;
12089 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12090 GfxFrame[x][y] = CustomValue[x][y];
12091 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12092 GfxFrame[x][y] = element_info[element2].collect_score;
12093 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12094 GfxFrame[x][y] = ChangeDelay[x][y];
12096 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12097 DrawLevelGraphicAnimation(x, y, graphic2);
12100 ResetGfxFrame(x, y, TRUE);
12104 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12105 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12106 ResetRandomAnimationValue(x, y);
12110 SetRandomAnimationValue(x, y);
12114 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12117 #endif // -------------------- !!! TEST ONLY !!! --------------------
12120 debug_print_timestamp(0, "- time for TEST loop: -->");
12123 SCAN_PLAYFIELD(x, y)
12125 element = Feld[x][y];
12126 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12128 ResetGfxFrame(x, y, TRUE);
12130 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12131 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12132 ResetRandomAnimationValue(x, y);
12134 SetRandomAnimationValue(x, y);
12136 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12138 if (IS_INACTIVE(element))
12140 if (IS_ANIMATED(graphic))
12141 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12146 /* this may take place after moving, so 'element' may have changed */
12147 if (IS_CHANGING(x, y) &&
12148 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12150 int page = element_info[element].event_page_nr[CE_DELAY];
12153 HandleElementChange(x, y, page);
12155 if (CAN_CHANGE(element))
12156 HandleElementChange(x, y, page);
12158 if (HAS_ACTION(element))
12159 ExecuteCustomElementAction(x, y, element, page);
12162 element = Feld[x][y];
12163 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12166 #if 0 // ---------------------------------------------------------------------
12168 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12172 element = Feld[x][y];
12173 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12175 if (IS_ANIMATED(graphic) &&
12176 !IS_MOVING(x, y) &&
12178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12180 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12181 TEST_DrawTwinkleOnField(x, y);
12183 else if (IS_MOVING(x, y))
12184 ContinueMoving(x, y);
12191 case EL_EM_EXIT_OPEN:
12192 case EL_SP_EXIT_OPEN:
12193 case EL_STEEL_EXIT_OPEN:
12194 case EL_EM_STEEL_EXIT_OPEN:
12195 case EL_SP_TERMINAL:
12196 case EL_SP_TERMINAL_ACTIVE:
12197 case EL_EXTRA_TIME:
12198 case EL_SHIELD_NORMAL:
12199 case EL_SHIELD_DEADLY:
12200 if (IS_ANIMATED(graphic))
12201 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12204 case EL_DYNAMITE_ACTIVE:
12205 case EL_EM_DYNAMITE_ACTIVE:
12206 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12207 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12208 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12209 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12210 case EL_SP_DISK_RED_ACTIVE:
12211 CheckDynamite(x, y);
12214 case EL_AMOEBA_GROWING:
12215 AmoebeWaechst(x, y);
12218 case EL_AMOEBA_SHRINKING:
12219 AmoebaDisappearing(x, y);
12222 #if !USE_NEW_AMOEBA_CODE
12223 case EL_AMOEBA_WET:
12224 case EL_AMOEBA_DRY:
12225 case EL_AMOEBA_FULL:
12227 case EL_EMC_DRIPPER:
12228 AmoebeAbleger(x, y);
12232 case EL_GAME_OF_LIFE:
12237 case EL_EXIT_CLOSED:
12241 case EL_EM_EXIT_CLOSED:
12245 case EL_STEEL_EXIT_CLOSED:
12246 CheckExitSteel(x, y);
12249 case EL_EM_STEEL_EXIT_CLOSED:
12250 CheckExitSteelEM(x, y);
12253 case EL_SP_EXIT_CLOSED:
12257 case EL_EXPANDABLE_WALL_GROWING:
12258 case EL_EXPANDABLE_STEELWALL_GROWING:
12259 MauerWaechst(x, y);
12262 case EL_EXPANDABLE_WALL:
12263 case EL_EXPANDABLE_WALL_HORIZONTAL:
12264 case EL_EXPANDABLE_WALL_VERTICAL:
12265 case EL_EXPANDABLE_WALL_ANY:
12266 case EL_BD_EXPANDABLE_WALL:
12267 MauerAbleger(x, y);
12270 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12271 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12272 case EL_EXPANDABLE_STEELWALL_ANY:
12273 MauerAblegerStahl(x, y);
12277 CheckForDragon(x, y);
12283 case EL_ELEMENT_SNAPPING:
12284 case EL_DIAGONAL_SHRINKING:
12285 case EL_DIAGONAL_GROWING:
12288 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12290 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12295 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12296 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12301 #else // ---------------------------------------------------------------------
12303 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12307 element = Feld[x][y];
12308 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12310 if (IS_ANIMATED(graphic) &&
12311 !IS_MOVING(x, y) &&
12313 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12315 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12316 TEST_DrawTwinkleOnField(x, y);
12318 else if ((element == EL_ACID ||
12319 element == EL_EXIT_OPEN ||
12320 element == EL_EM_EXIT_OPEN ||
12321 element == EL_SP_EXIT_OPEN ||
12322 element == EL_STEEL_EXIT_OPEN ||
12323 element == EL_EM_STEEL_EXIT_OPEN ||
12324 element == EL_SP_TERMINAL ||
12325 element == EL_SP_TERMINAL_ACTIVE ||
12326 element == EL_EXTRA_TIME ||
12327 element == EL_SHIELD_NORMAL ||
12328 element == EL_SHIELD_DEADLY) &&
12329 IS_ANIMATED(graphic))
12330 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12331 else if (IS_MOVING(x, y))
12332 ContinueMoving(x, y);
12333 else if (IS_ACTIVE_BOMB(element))
12334 CheckDynamite(x, y);
12335 else if (element == EL_AMOEBA_GROWING)
12336 AmoebeWaechst(x, y);
12337 else if (element == EL_AMOEBA_SHRINKING)
12338 AmoebaDisappearing(x, y);
12340 #if !USE_NEW_AMOEBA_CODE
12341 else if (IS_AMOEBALIVE(element))
12342 AmoebeAbleger(x, y);
12345 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12347 else if (element == EL_EXIT_CLOSED)
12349 else if (element == EL_EM_EXIT_CLOSED)
12351 else if (element == EL_STEEL_EXIT_CLOSED)
12352 CheckExitSteel(x, y);
12353 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12354 CheckExitSteelEM(x, y);
12355 else if (element == EL_SP_EXIT_CLOSED)
12357 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12358 element == EL_EXPANDABLE_STEELWALL_GROWING)
12359 MauerWaechst(x, y);
12360 else if (element == EL_EXPANDABLE_WALL ||
12361 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12362 element == EL_EXPANDABLE_WALL_VERTICAL ||
12363 element == EL_EXPANDABLE_WALL_ANY ||
12364 element == EL_BD_EXPANDABLE_WALL)
12365 MauerAbleger(x, y);
12366 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12367 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12368 element == EL_EXPANDABLE_STEELWALL_ANY)
12369 MauerAblegerStahl(x, y);
12370 else if (element == EL_FLAMES)
12371 CheckForDragon(x, y);
12372 else if (element == EL_EXPLOSION)
12373 ; /* drawing of correct explosion animation is handled separately */
12374 else if (element == EL_ELEMENT_SNAPPING ||
12375 element == EL_DIAGONAL_SHRINKING ||
12376 element == EL_DIAGONAL_GROWING)
12378 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12380 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12382 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12383 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12385 #endif // ---------------------------------------------------------------------
12387 if (IS_BELT_ACTIVE(element))
12388 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12390 if (game.magic_wall_active)
12392 int jx = local_player->jx, jy = local_player->jy;
12394 /* play the element sound at the position nearest to the player */
12395 if ((element == EL_MAGIC_WALL_FULL ||
12396 element == EL_MAGIC_WALL_ACTIVE ||
12397 element == EL_MAGIC_WALL_EMPTYING ||
12398 element == EL_BD_MAGIC_WALL_FULL ||
12399 element == EL_BD_MAGIC_WALL_ACTIVE ||
12400 element == EL_BD_MAGIC_WALL_EMPTYING ||
12401 element == EL_DC_MAGIC_WALL_FULL ||
12402 element == EL_DC_MAGIC_WALL_ACTIVE ||
12403 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12404 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12413 debug_print_timestamp(0, "- time for MAIN loop: -->");
12416 #if USE_NEW_AMOEBA_CODE
12417 /* new experimental amoeba growth stuff */
12418 if (!(FrameCounter % 8))
12420 static unsigned long random = 1684108901;
12422 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12424 x = RND(lev_fieldx);
12425 y = RND(lev_fieldy);
12426 element = Feld[x][y];
12428 if (!IS_PLAYER(x,y) &&
12429 (element == EL_EMPTY ||
12430 CAN_GROW_INTO(element) ||
12431 element == EL_QUICKSAND_EMPTY ||
12432 element == EL_QUICKSAND_FAST_EMPTY ||
12433 element == EL_ACID_SPLASH_LEFT ||
12434 element == EL_ACID_SPLASH_RIGHT))
12436 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12437 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12438 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12439 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12440 Feld[x][y] = EL_AMOEBA_DROP;
12443 random = random * 129 + 1;
12449 if (game.explosions_delayed)
12452 game.explosions_delayed = FALSE;
12454 SCAN_PLAYFIELD(x, y)
12456 element = Feld[x][y];
12458 if (ExplodeField[x][y])
12459 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12460 else if (element == EL_EXPLOSION)
12461 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12463 ExplodeField[x][y] = EX_TYPE_NONE;
12466 game.explosions_delayed = TRUE;
12469 if (game.magic_wall_active)
12471 if (!(game.magic_wall_time_left % 4))
12473 int element = Feld[magic_wall_x][magic_wall_y];
12475 if (element == EL_BD_MAGIC_WALL_FULL ||
12476 element == EL_BD_MAGIC_WALL_ACTIVE ||
12477 element == EL_BD_MAGIC_WALL_EMPTYING)
12478 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12479 else if (element == EL_DC_MAGIC_WALL_FULL ||
12480 element == EL_DC_MAGIC_WALL_ACTIVE ||
12481 element == EL_DC_MAGIC_WALL_EMPTYING)
12482 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12484 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12487 if (game.magic_wall_time_left > 0)
12489 game.magic_wall_time_left--;
12491 if (!game.magic_wall_time_left)
12493 SCAN_PLAYFIELD(x, y)
12495 element = Feld[x][y];
12497 if (element == EL_MAGIC_WALL_ACTIVE ||
12498 element == EL_MAGIC_WALL_FULL)
12500 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12501 TEST_DrawLevelField(x, y);
12503 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12504 element == EL_BD_MAGIC_WALL_FULL)
12506 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12507 TEST_DrawLevelField(x, y);
12509 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12510 element == EL_DC_MAGIC_WALL_FULL)
12512 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12513 TEST_DrawLevelField(x, y);
12517 game.magic_wall_active = FALSE;
12522 if (game.light_time_left > 0)
12524 game.light_time_left--;
12526 if (game.light_time_left == 0)
12527 RedrawAllLightSwitchesAndInvisibleElements();
12530 if (game.timegate_time_left > 0)
12532 game.timegate_time_left--;
12534 if (game.timegate_time_left == 0)
12535 CloseAllOpenTimegates();
12538 if (game.lenses_time_left > 0)
12540 game.lenses_time_left--;
12542 if (game.lenses_time_left == 0)
12543 RedrawAllInvisibleElementsForLenses();
12546 if (game.magnify_time_left > 0)
12548 game.magnify_time_left--;
12550 if (game.magnify_time_left == 0)
12551 RedrawAllInvisibleElementsForMagnifier();
12554 for (i = 0; i < MAX_PLAYERS; i++)
12556 struct PlayerInfo *player = &stored_player[i];
12558 if (SHIELD_ON(player))
12560 if (player->shield_deadly_time_left)
12561 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12562 else if (player->shield_normal_time_left)
12563 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12567 #if USE_DELAYED_GFX_REDRAW
12568 SCAN_PLAYFIELD(x, y)
12571 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12573 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12574 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12577 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12578 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12580 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12581 DrawLevelField(x, y);
12583 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12584 DrawLevelFieldCrumbledSand(x, y);
12586 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12587 DrawLevelFieldCrumbledSandNeighbours(x, y);
12589 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12590 DrawTwinkleOnField(x, y);
12593 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12600 PlayAllPlayersSound();
12602 if (options.debug) /* calculate frames per second */
12604 static unsigned long fps_counter = 0;
12605 static int fps_frames = 0;
12606 unsigned long fps_delay_ms = Counter() - fps_counter;
12610 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12612 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12615 fps_counter = Counter();
12618 redraw_mask |= REDRAW_FPS;
12621 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12623 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12625 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12627 local_player->show_envelope = 0;
12631 debug_print_timestamp(0, "stop main loop profiling ");
12632 printf("----------------------------------------------------------\n");
12635 /* use random number generator in every frame to make it less predictable */
12636 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12640 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12642 int min_x = x, min_y = y, max_x = x, max_y = y;
12645 for (i = 0; i < MAX_PLAYERS; i++)
12647 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12649 if (!stored_player[i].active || &stored_player[i] == player)
12652 min_x = MIN(min_x, jx);
12653 min_y = MIN(min_y, jy);
12654 max_x = MAX(max_x, jx);
12655 max_y = MAX(max_y, jy);
12658 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12661 static boolean AllPlayersInVisibleScreen()
12665 for (i = 0; i < MAX_PLAYERS; i++)
12667 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12669 if (!stored_player[i].active)
12672 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12679 void ScrollLevel(int dx, int dy)
12682 /* (directly solved in BlitBitmap() now) */
12683 static Bitmap *bitmap_db_field2 = NULL;
12684 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12691 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12692 /* only horizontal XOR vertical scroll direction allowed */
12693 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12698 /* (directly solved in BlitBitmap() now) */
12699 if (bitmap_db_field2 == NULL)
12700 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12702 /* needed when blitting directly to same bitmap -- should not be needed with
12703 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12704 BlitBitmap(drawto_field, bitmap_db_field2,
12705 FX + TILEX * (dx == -1) - softscroll_offset,
12706 FY + TILEY * (dy == -1) - softscroll_offset,
12707 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12708 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12709 FX + TILEX * (dx == 1) - softscroll_offset,
12710 FY + TILEY * (dy == 1) - softscroll_offset);
12711 BlitBitmap(bitmap_db_field2, drawto_field,
12712 FX + TILEX * (dx == 1) - softscroll_offset,
12713 FY + TILEY * (dy == 1) - softscroll_offset,
12714 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12715 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12716 FX + TILEX * (dx == 1) - softscroll_offset,
12717 FY + TILEY * (dy == 1) - softscroll_offset);
12722 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12723 int xsize = (BX2 - BX1 + 1);
12724 int ysize = (BY2 - BY1 + 1);
12725 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12726 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12727 int step = (start < end ? +1 : -1);
12729 for (i = start; i != end; i += step)
12731 BlitBitmap(drawto_field, drawto_field,
12732 FX + TILEX * (dx != 0 ? i + step : 0),
12733 FY + TILEY * (dy != 0 ? i + step : 0),
12734 TILEX * (dx != 0 ? 1 : xsize),
12735 TILEY * (dy != 0 ? 1 : ysize),
12736 FX + TILEX * (dx != 0 ? i : 0),
12737 FY + TILEY * (dy != 0 ? i : 0));
12742 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12744 BlitBitmap(drawto_field, drawto_field,
12745 FX + TILEX * (dx == -1) - softscroll_offset,
12746 FY + TILEY * (dy == -1) - softscroll_offset,
12747 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12748 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12749 FX + TILEX * (dx == 1) - softscroll_offset,
12750 FY + TILEY * (dy == 1) - softscroll_offset);
12756 x = (dx == 1 ? BX1 : BX2);
12757 for (y = BY1; y <= BY2; y++)
12758 DrawScreenField(x, y);
12763 y = (dy == 1 ? BY1 : BY2);
12764 for (x = BX1; x <= BX2; x++)
12765 DrawScreenField(x, y);
12768 redraw_mask |= REDRAW_FIELD;
12771 static boolean canFallDown(struct PlayerInfo *player)
12773 int jx = player->jx, jy = player->jy;
12775 return (IN_LEV_FIELD(jx, jy + 1) &&
12776 (IS_FREE(jx, jy + 1) ||
12777 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12778 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12779 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12782 static boolean canPassField(int x, int y, int move_dir)
12784 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12785 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12786 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12787 int nextx = x + dx;
12788 int nexty = y + dy;
12789 int element = Feld[x][y];
12791 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12792 !CAN_MOVE(element) &&
12793 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12794 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12795 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12798 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12800 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12801 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12802 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12806 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12807 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12808 (IS_DIGGABLE(Feld[newx][newy]) ||
12809 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12810 canPassField(newx, newy, move_dir)));
12813 static void CheckGravityMovement(struct PlayerInfo *player)
12815 #if USE_PLAYER_GRAVITY
12816 if (player->gravity && !player->programmed_action)
12818 if (game.gravity && !player->programmed_action)
12821 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12822 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12823 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12824 int jx = player->jx, jy = player->jy;
12825 boolean player_is_moving_to_valid_field =
12826 (!player_is_snapping &&
12827 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12828 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12829 boolean player_can_fall_down = canFallDown(player);
12831 if (player_can_fall_down &&
12832 !player_is_moving_to_valid_field)
12833 player->programmed_action = MV_DOWN;
12837 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12839 return CheckGravityMovement(player);
12841 #if USE_PLAYER_GRAVITY
12842 if (player->gravity && !player->programmed_action)
12844 if (game.gravity && !player->programmed_action)
12847 int jx = player->jx, jy = player->jy;
12848 boolean field_under_player_is_free =
12849 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12850 boolean player_is_standing_on_valid_field =
12851 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12852 (IS_WALKABLE(Feld[jx][jy]) &&
12853 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12855 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12856 player->programmed_action = MV_DOWN;
12861 MovePlayerOneStep()
12862 -----------------------------------------------------------------------------
12863 dx, dy: direction (non-diagonal) to try to move the player to
12864 real_dx, real_dy: direction as read from input device (can be diagonal)
12867 boolean MovePlayerOneStep(struct PlayerInfo *player,
12868 int dx, int dy, int real_dx, int real_dy)
12870 int jx = player->jx, jy = player->jy;
12871 int new_jx = jx + dx, new_jy = jy + dy;
12872 #if !USE_FIXED_DONT_RUN_INTO
12876 boolean player_can_move = !player->cannot_move;
12878 if (!player->active || (!dx && !dy))
12879 return MP_NO_ACTION;
12881 player->MovDir = (dx < 0 ? MV_LEFT :
12882 dx > 0 ? MV_RIGHT :
12884 dy > 0 ? MV_DOWN : MV_NONE);
12886 if (!IN_LEV_FIELD(new_jx, new_jy))
12887 return MP_NO_ACTION;
12889 if (!player_can_move)
12891 if (player->MovPos == 0)
12893 player->is_moving = FALSE;
12894 player->is_digging = FALSE;
12895 player->is_collecting = FALSE;
12896 player->is_snapping = FALSE;
12897 player->is_pushing = FALSE;
12902 if (!options.network && game.centered_player_nr == -1 &&
12903 !AllPlayersInSight(player, new_jx, new_jy))
12904 return MP_NO_ACTION;
12906 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12907 return MP_NO_ACTION;
12910 #if !USE_FIXED_DONT_RUN_INTO
12911 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12913 /* (moved to DigField()) */
12914 if (player_can_move && DONT_RUN_INTO(element))
12916 if (element == EL_ACID && dx == 0 && dy == 1)
12918 SplashAcid(new_jx, new_jy);
12919 Feld[jx][jy] = EL_PLAYER_1;
12920 InitMovingField(jx, jy, MV_DOWN);
12921 Store[jx][jy] = EL_ACID;
12922 ContinueMoving(jx, jy);
12923 BuryPlayer(player);
12926 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12932 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12933 if (can_move != MP_MOVING)
12936 /* check if DigField() has caused relocation of the player */
12937 if (player->jx != jx || player->jy != jy)
12938 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12940 StorePlayer[jx][jy] = 0;
12941 player->last_jx = jx;
12942 player->last_jy = jy;
12943 player->jx = new_jx;
12944 player->jy = new_jy;
12945 StorePlayer[new_jx][new_jy] = player->element_nr;
12947 if (player->move_delay_value_next != -1)
12949 player->move_delay_value = player->move_delay_value_next;
12950 player->move_delay_value_next = -1;
12954 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12956 player->step_counter++;
12958 PlayerVisit[jx][jy] = FrameCounter;
12960 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12961 player->is_moving = TRUE;
12965 /* should better be called in MovePlayer(), but this breaks some tapes */
12966 ScrollPlayer(player, SCROLL_INIT);
12972 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12974 int jx = player->jx, jy = player->jy;
12975 int old_jx = jx, old_jy = jy;
12976 int moved = MP_NO_ACTION;
12978 if (!player->active)
12983 if (player->MovPos == 0)
12985 player->is_moving = FALSE;
12986 player->is_digging = FALSE;
12987 player->is_collecting = FALSE;
12988 player->is_snapping = FALSE;
12989 player->is_pushing = FALSE;
12995 if (player->move_delay > 0)
12998 player->move_delay = -1; /* set to "uninitialized" value */
13000 /* store if player is automatically moved to next field */
13001 player->is_auto_moving = (player->programmed_action != MV_NONE);
13003 /* remove the last programmed player action */
13004 player->programmed_action = 0;
13006 if (player->MovPos)
13008 /* should only happen if pre-1.2 tape recordings are played */
13009 /* this is only for backward compatibility */
13011 int original_move_delay_value = player->move_delay_value;
13014 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13018 /* scroll remaining steps with finest movement resolution */
13019 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13021 while (player->MovPos)
13023 ScrollPlayer(player, SCROLL_GO_ON);
13024 ScrollScreen(NULL, SCROLL_GO_ON);
13026 AdvanceFrameAndPlayerCounters(player->index_nr);
13032 player->move_delay_value = original_move_delay_value;
13035 player->is_active = FALSE;
13037 if (player->last_move_dir & MV_HORIZONTAL)
13039 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13040 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13044 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13045 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13048 #if USE_FIXED_BORDER_RUNNING_GFX
13049 if (!moved && !player->is_active)
13051 player->is_moving = FALSE;
13052 player->is_digging = FALSE;
13053 player->is_collecting = FALSE;
13054 player->is_snapping = FALSE;
13055 player->is_pushing = FALSE;
13063 if (moved & MP_MOVING && !ScreenMovPos &&
13064 (player->index_nr == game.centered_player_nr ||
13065 game.centered_player_nr == -1))
13067 if (moved & MP_MOVING && !ScreenMovPos &&
13068 (player == local_player || !options.network))
13071 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13072 int offset = game.scroll_delay_value;
13074 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13076 /* actual player has left the screen -- scroll in that direction */
13077 if (jx != old_jx) /* player has moved horizontally */
13078 scroll_x += (jx - old_jx);
13079 else /* player has moved vertically */
13080 scroll_y += (jy - old_jy);
13084 if (jx != old_jx) /* player has moved horizontally */
13086 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13087 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13088 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13090 /* don't scroll over playfield boundaries */
13091 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13092 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13094 /* don't scroll more than one field at a time */
13095 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13097 /* don't scroll against the player's moving direction */
13098 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13099 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13100 scroll_x = old_scroll_x;
13102 else /* player has moved vertically */
13104 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13105 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13106 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13108 /* don't scroll over playfield boundaries */
13109 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13110 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13112 /* don't scroll more than one field at a time */
13113 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13115 /* don't scroll against the player's moving direction */
13116 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13117 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13118 scroll_y = old_scroll_y;
13122 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13125 if (!options.network && game.centered_player_nr == -1 &&
13126 !AllPlayersInVisibleScreen())
13128 scroll_x = old_scroll_x;
13129 scroll_y = old_scroll_y;
13133 if (!options.network && !AllPlayersInVisibleScreen())
13135 scroll_x = old_scroll_x;
13136 scroll_y = old_scroll_y;
13141 ScrollScreen(player, SCROLL_INIT);
13142 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13147 player->StepFrame = 0;
13149 if (moved & MP_MOVING)
13151 if (old_jx != jx && old_jy == jy)
13152 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13153 else if (old_jx == jx && old_jy != jy)
13154 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13156 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13158 player->last_move_dir = player->MovDir;
13159 player->is_moving = TRUE;
13160 player->is_snapping = FALSE;
13161 player->is_switching = FALSE;
13162 player->is_dropping = FALSE;
13163 player->is_dropping_pressed = FALSE;
13164 player->drop_pressed_delay = 0;
13167 /* should better be called here than above, but this breaks some tapes */
13168 ScrollPlayer(player, SCROLL_INIT);
13173 CheckGravityMovementWhenNotMoving(player);
13175 player->is_moving = FALSE;
13177 /* at this point, the player is allowed to move, but cannot move right now
13178 (e.g. because of something blocking the way) -- ensure that the player
13179 is also allowed to move in the next frame (in old versions before 3.1.1,
13180 the player was forced to wait again for eight frames before next try) */
13182 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13183 player->move_delay = 0; /* allow direct movement in the next frame */
13186 if (player->move_delay == -1) /* not yet initialized by DigField() */
13187 player->move_delay = player->move_delay_value;
13189 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13191 TestIfPlayerTouchesBadThing(jx, jy);
13192 TestIfPlayerTouchesCustomElement(jx, jy);
13195 if (!player->active)
13196 RemovePlayer(player);
13201 void ScrollPlayer(struct PlayerInfo *player, int mode)
13203 int jx = player->jx, jy = player->jy;
13204 int last_jx = player->last_jx, last_jy = player->last_jy;
13205 int move_stepsize = TILEX / player->move_delay_value;
13207 #if USE_NEW_PLAYER_SPEED
13208 if (!player->active)
13211 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13214 if (!player->active || player->MovPos == 0)
13218 if (mode == SCROLL_INIT)
13220 player->actual_frame_counter = FrameCounter;
13221 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13223 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13224 Feld[last_jx][last_jy] == EL_EMPTY)
13226 int last_field_block_delay = 0; /* start with no blocking at all */
13227 int block_delay_adjustment = player->block_delay_adjustment;
13229 /* if player blocks last field, add delay for exactly one move */
13230 if (player->block_last_field)
13232 last_field_block_delay += player->move_delay_value;
13234 /* when blocking enabled, prevent moving up despite gravity */
13235 #if USE_PLAYER_GRAVITY
13236 if (player->gravity && player->MovDir == MV_UP)
13237 block_delay_adjustment = -1;
13239 if (game.gravity && player->MovDir == MV_UP)
13240 block_delay_adjustment = -1;
13244 /* add block delay adjustment (also possible when not blocking) */
13245 last_field_block_delay += block_delay_adjustment;
13247 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13248 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13251 #if USE_NEW_PLAYER_SPEED
13252 if (player->MovPos != 0) /* player has not yet reached destination */
13258 else if (!FrameReached(&player->actual_frame_counter, 1))
13261 #if USE_NEW_PLAYER_SPEED
13262 if (player->MovPos != 0)
13264 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13265 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13267 /* before DrawPlayer() to draw correct player graphic for this case */
13268 if (player->MovPos == 0)
13269 CheckGravityMovement(player);
13272 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13273 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13275 /* before DrawPlayer() to draw correct player graphic for this case */
13276 if (player->MovPos == 0)
13277 CheckGravityMovement(player);
13280 if (player->MovPos == 0) /* player reached destination field */
13282 if (player->move_delay_reset_counter > 0)
13284 player->move_delay_reset_counter--;
13286 if (player->move_delay_reset_counter == 0)
13288 /* continue with normal speed after quickly moving through gate */
13289 HALVE_PLAYER_SPEED(player);
13291 /* be able to make the next move without delay */
13292 player->move_delay = 0;
13296 player->last_jx = jx;
13297 player->last_jy = jy;
13299 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13300 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13301 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13302 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13303 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13304 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13306 DrawPlayer(player); /* needed here only to cleanup last field */
13307 RemovePlayer(player);
13309 if (local_player->friends_still_needed == 0 ||
13310 IS_SP_ELEMENT(Feld[jx][jy]))
13311 PlayerWins(player);
13314 /* this breaks one level: "machine", level 000 */
13316 int move_direction = player->MovDir;
13317 int enter_side = MV_DIR_OPPOSITE(move_direction);
13318 int leave_side = move_direction;
13319 int old_jx = last_jx;
13320 int old_jy = last_jy;
13321 int old_element = Feld[old_jx][old_jy];
13322 int new_element = Feld[jx][jy];
13324 if (IS_CUSTOM_ELEMENT(old_element))
13325 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13327 player->index_bit, leave_side);
13329 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13330 CE_PLAYER_LEAVES_X,
13331 player->index_bit, leave_side);
13333 if (IS_CUSTOM_ELEMENT(new_element))
13334 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13335 player->index_bit, enter_side);
13337 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13338 CE_PLAYER_ENTERS_X,
13339 player->index_bit, enter_side);
13341 #if USE_FIX_CE_ACTION_WITH_PLAYER
13342 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13343 CE_MOVE_OF_X, move_direction);
13345 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13346 CE_MOVE_OF_X, move_direction);
13350 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13352 TestIfPlayerTouchesBadThing(jx, jy);
13353 TestIfPlayerTouchesCustomElement(jx, jy);
13355 /* needed because pushed element has not yet reached its destination,
13356 so it would trigger a change event at its previous field location */
13357 if (!player->is_pushing)
13358 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13360 if (!player->active)
13361 RemovePlayer(player);
13364 if (!local_player->LevelSolved && level.use_step_counter)
13374 if (TimeLeft <= 10 && setup.time_limit)
13375 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13378 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13380 DisplayGameControlValues();
13382 DrawGameValue_Time(TimeLeft);
13385 if (!TimeLeft && setup.time_limit)
13386 for (i = 0; i < MAX_PLAYERS; i++)
13387 KillPlayer(&stored_player[i]);
13390 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13392 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13394 DisplayGameControlValues();
13397 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13398 DrawGameValue_Time(TimePlayed);
13402 if (tape.single_step && tape.recording && !tape.pausing &&
13403 !player->programmed_action)
13404 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13408 void ScrollScreen(struct PlayerInfo *player, int mode)
13410 static unsigned long screen_frame_counter = 0;
13412 if (mode == SCROLL_INIT)
13414 /* set scrolling step size according to actual player's moving speed */
13415 ScrollStepSize = TILEX / player->move_delay_value;
13417 screen_frame_counter = FrameCounter;
13418 ScreenMovDir = player->MovDir;
13419 ScreenMovPos = player->MovPos;
13420 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13423 else if (!FrameReached(&screen_frame_counter, 1))
13428 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13429 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13430 redraw_mask |= REDRAW_FIELD;
13433 ScreenMovDir = MV_NONE;
13436 void TestIfPlayerTouchesCustomElement(int x, int y)
13438 static int xy[4][2] =
13445 static int trigger_sides[4][2] =
13447 /* center side border side */
13448 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13449 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13450 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13451 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13453 static int touch_dir[4] =
13455 MV_LEFT | MV_RIGHT,
13460 int center_element = Feld[x][y]; /* should always be non-moving! */
13463 for (i = 0; i < NUM_DIRECTIONS; i++)
13465 int xx = x + xy[i][0];
13466 int yy = y + xy[i][1];
13467 int center_side = trigger_sides[i][0];
13468 int border_side = trigger_sides[i][1];
13469 int border_element;
13471 if (!IN_LEV_FIELD(xx, yy))
13474 if (IS_PLAYER(x, y)) /* player found at center element */
13476 struct PlayerInfo *player = PLAYERINFO(x, y);
13478 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13479 border_element = Feld[xx][yy]; /* may be moving! */
13480 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13481 border_element = Feld[xx][yy];
13482 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13483 border_element = MovingOrBlocked2Element(xx, yy);
13485 continue; /* center and border element do not touch */
13487 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13488 player->index_bit, border_side);
13489 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13490 CE_PLAYER_TOUCHES_X,
13491 player->index_bit, border_side);
13493 #if USE_FIX_CE_ACTION_WITH_PLAYER
13495 /* use player element that is initially defined in the level playfield,
13496 not the player element that corresponds to the runtime player number
13497 (example: a level that contains EL_PLAYER_3 as the only player would
13498 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13499 int player_element = PLAYERINFO(x, y)->initial_element;
13501 CheckElementChangeBySide(xx, yy, border_element, player_element,
13502 CE_TOUCHING_X, border_side);
13506 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13508 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13510 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13512 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13513 continue; /* center and border element do not touch */
13516 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13517 player->index_bit, center_side);
13518 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13519 CE_PLAYER_TOUCHES_X,
13520 player->index_bit, center_side);
13522 #if USE_FIX_CE_ACTION_WITH_PLAYER
13524 /* use player element that is initially defined in the level playfield,
13525 not the player element that corresponds to the runtime player number
13526 (example: a level that contains EL_PLAYER_3 as the only player would
13527 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13528 int player_element = PLAYERINFO(xx, yy)->initial_element;
13530 CheckElementChangeBySide(x, y, center_element, player_element,
13531 CE_TOUCHING_X, center_side);
13540 #if USE_ELEMENT_TOUCHING_BUGFIX
13542 void TestIfElementTouchesCustomElement(int x, int y)
13544 static int xy[4][2] =
13551 static int trigger_sides[4][2] =
13553 /* center side border side */
13554 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13555 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13556 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13557 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13559 static int touch_dir[4] =
13561 MV_LEFT | MV_RIGHT,
13566 boolean change_center_element = FALSE;
13567 int center_element = Feld[x][y]; /* should always be non-moving! */
13568 int border_element_old[NUM_DIRECTIONS];
13571 for (i = 0; i < NUM_DIRECTIONS; i++)
13573 int xx = x + xy[i][0];
13574 int yy = y + xy[i][1];
13575 int border_element;
13577 border_element_old[i] = -1;
13579 if (!IN_LEV_FIELD(xx, yy))
13582 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13583 border_element = Feld[xx][yy]; /* may be moving! */
13584 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13585 border_element = Feld[xx][yy];
13586 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13587 border_element = MovingOrBlocked2Element(xx, yy);
13589 continue; /* center and border element do not touch */
13591 border_element_old[i] = border_element;
13594 for (i = 0; i < NUM_DIRECTIONS; i++)
13596 int xx = x + xy[i][0];
13597 int yy = y + xy[i][1];
13598 int center_side = trigger_sides[i][0];
13599 int border_element = border_element_old[i];
13601 if (border_element == -1)
13604 /* check for change of border element */
13605 CheckElementChangeBySide(xx, yy, border_element, center_element,
13606 CE_TOUCHING_X, center_side);
13609 for (i = 0; i < NUM_DIRECTIONS; i++)
13611 int xx = x + xy[i][0];
13612 int yy = y + xy[i][1];
13613 int border_side = trigger_sides[i][1];
13614 int border_element = border_element_old[i];
13616 if (border_element == -1)
13619 /* check for change of center element (but change it only once) */
13620 if (!change_center_element)
13621 change_center_element =
13622 CheckElementChangeBySide(x, y, center_element, border_element,
13623 CE_TOUCHING_X, border_side);
13625 #if USE_FIX_CE_ACTION_WITH_PLAYER
13626 if (IS_PLAYER(xx, yy))
13628 /* use player element that is initially defined in the level playfield,
13629 not the player element that corresponds to the runtime player number
13630 (example: a level that contains EL_PLAYER_3 as the only player would
13631 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13632 int player_element = PLAYERINFO(xx, yy)->initial_element;
13634 CheckElementChangeBySide(x, y, center_element, player_element,
13635 CE_TOUCHING_X, border_side);
13643 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13645 static int xy[4][2] =
13652 static int trigger_sides[4][2] =
13654 /* center side border side */
13655 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13656 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13657 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13658 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13660 static int touch_dir[4] =
13662 MV_LEFT | MV_RIGHT,
13667 boolean change_center_element = FALSE;
13668 int center_element = Feld[x][y]; /* should always be non-moving! */
13671 for (i = 0; i < NUM_DIRECTIONS; i++)
13673 int xx = x + xy[i][0];
13674 int yy = y + xy[i][1];
13675 int center_side = trigger_sides[i][0];
13676 int border_side = trigger_sides[i][1];
13677 int border_element;
13679 if (!IN_LEV_FIELD(xx, yy))
13682 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13683 border_element = Feld[xx][yy]; /* may be moving! */
13684 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13685 border_element = Feld[xx][yy];
13686 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13687 border_element = MovingOrBlocked2Element(xx, yy);
13689 continue; /* center and border element do not touch */
13691 /* check for change of center element (but change it only once) */
13692 if (!change_center_element)
13693 change_center_element =
13694 CheckElementChangeBySide(x, y, center_element, border_element,
13695 CE_TOUCHING_X, border_side);
13697 /* check for change of border element */
13698 CheckElementChangeBySide(xx, yy, border_element, center_element,
13699 CE_TOUCHING_X, center_side);
13705 void TestIfElementHitsCustomElement(int x, int y, int direction)
13707 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13708 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13709 int hitx = x + dx, hity = y + dy;
13710 int hitting_element = Feld[x][y];
13711 int touched_element;
13713 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13716 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13717 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13719 if (IN_LEV_FIELD(hitx, hity))
13721 int opposite_direction = MV_DIR_OPPOSITE(direction);
13722 int hitting_side = direction;
13723 int touched_side = opposite_direction;
13724 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13725 MovDir[hitx][hity] != direction ||
13726 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13732 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13733 CE_HITTING_X, touched_side);
13735 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13736 CE_HIT_BY_X, hitting_side);
13738 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13739 CE_HIT_BY_SOMETHING, opposite_direction);
13741 #if USE_FIX_CE_ACTION_WITH_PLAYER
13742 if (IS_PLAYER(hitx, hity))
13744 /* use player element that is initially defined in the level playfield,
13745 not the player element that corresponds to the runtime player number
13746 (example: a level that contains EL_PLAYER_3 as the only player would
13747 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13748 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13750 CheckElementChangeBySide(x, y, hitting_element, player_element,
13751 CE_HITTING_X, touched_side);
13757 /* "hitting something" is also true when hitting the playfield border */
13758 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13759 CE_HITTING_SOMETHING, direction);
13763 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13765 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13766 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13767 int hitx = x + dx, hity = y + dy;
13768 int hitting_element = Feld[x][y];
13769 int touched_element;
13771 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13772 !IS_FREE(hitx, hity) &&
13773 (!IS_MOVING(hitx, hity) ||
13774 MovDir[hitx][hity] != direction ||
13775 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13778 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13782 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13786 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13787 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13789 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13790 EP_CAN_SMASH_EVERYTHING, direction);
13792 if (IN_LEV_FIELD(hitx, hity))
13794 int opposite_direction = MV_DIR_OPPOSITE(direction);
13795 int hitting_side = direction;
13796 int touched_side = opposite_direction;
13798 int touched_element = MovingOrBlocked2Element(hitx, hity);
13801 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13802 MovDir[hitx][hity] != direction ||
13803 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13812 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13813 CE_SMASHED_BY_SOMETHING, opposite_direction);
13815 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13816 CE_OTHER_IS_SMASHING, touched_side);
13818 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13819 CE_OTHER_GETS_SMASHED, hitting_side);
13825 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13827 int i, kill_x = -1, kill_y = -1;
13829 int bad_element = -1;
13830 static int test_xy[4][2] =
13837 static int test_dir[4] =
13845 for (i = 0; i < NUM_DIRECTIONS; i++)
13847 int test_x, test_y, test_move_dir, test_element;
13849 test_x = good_x + test_xy[i][0];
13850 test_y = good_y + test_xy[i][1];
13852 if (!IN_LEV_FIELD(test_x, test_y))
13856 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13858 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13860 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13861 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13863 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13864 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13868 bad_element = test_element;
13874 if (kill_x != -1 || kill_y != -1)
13876 if (IS_PLAYER(good_x, good_y))
13878 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13880 if (player->shield_deadly_time_left > 0 &&
13881 !IS_INDESTRUCTIBLE(bad_element))
13882 Bang(kill_x, kill_y);
13883 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13884 KillPlayer(player);
13887 Bang(good_x, good_y);
13891 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13893 int i, kill_x = -1, kill_y = -1;
13894 int bad_element = Feld[bad_x][bad_y];
13895 static int test_xy[4][2] =
13902 static int touch_dir[4] =
13904 MV_LEFT | MV_RIGHT,
13909 static int test_dir[4] =
13917 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13920 for (i = 0; i < NUM_DIRECTIONS; i++)
13922 int test_x, test_y, test_move_dir, test_element;
13924 test_x = bad_x + test_xy[i][0];
13925 test_y = bad_y + test_xy[i][1];
13927 if (!IN_LEV_FIELD(test_x, test_y))
13931 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13933 test_element = Feld[test_x][test_y];
13935 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13936 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13938 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13939 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13941 /* good thing is player or penguin that does not move away */
13942 if (IS_PLAYER(test_x, test_y))
13944 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13946 if (bad_element == EL_ROBOT && player->is_moving)
13947 continue; /* robot does not kill player if he is moving */
13949 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13951 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13952 continue; /* center and border element do not touch */
13960 else if (test_element == EL_PENGUIN)
13970 if (kill_x != -1 || kill_y != -1)
13972 if (IS_PLAYER(kill_x, kill_y))
13974 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13976 if (player->shield_deadly_time_left > 0 &&
13977 !IS_INDESTRUCTIBLE(bad_element))
13978 Bang(bad_x, bad_y);
13979 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13980 KillPlayer(player);
13983 Bang(kill_x, kill_y);
13987 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13989 int bad_element = Feld[bad_x][bad_y];
13990 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13991 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13992 int test_x = bad_x + dx, test_y = bad_y + dy;
13993 int test_move_dir, test_element;
13994 int kill_x = -1, kill_y = -1;
13996 if (!IN_LEV_FIELD(test_x, test_y))
14000 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14002 test_element = Feld[test_x][test_y];
14004 if (test_move_dir != bad_move_dir)
14006 /* good thing can be player or penguin that does not move away */
14007 if (IS_PLAYER(test_x, test_y))
14009 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14011 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14012 player as being hit when he is moving towards the bad thing, because
14013 the "get hit by" condition would be lost after the player stops) */
14014 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14015 return; /* player moves away from bad thing */
14020 else if (test_element == EL_PENGUIN)
14027 if (kill_x != -1 || kill_y != -1)
14029 if (IS_PLAYER(kill_x, kill_y))
14031 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14033 if (player->shield_deadly_time_left > 0 &&
14034 !IS_INDESTRUCTIBLE(bad_element))
14035 Bang(bad_x, bad_y);
14036 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14037 KillPlayer(player);
14040 Bang(kill_x, kill_y);
14044 void TestIfPlayerTouchesBadThing(int x, int y)
14046 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14049 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14051 TestIfGoodThingHitsBadThing(x, y, move_dir);
14054 void TestIfBadThingTouchesPlayer(int x, int y)
14056 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14059 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14061 TestIfBadThingHitsGoodThing(x, y, move_dir);
14064 void TestIfFriendTouchesBadThing(int x, int y)
14066 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14069 void TestIfBadThingTouchesFriend(int x, int y)
14071 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14074 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14076 int i, kill_x = bad_x, kill_y = bad_y;
14077 static int xy[4][2] =
14085 for (i = 0; i < NUM_DIRECTIONS; i++)
14089 x = bad_x + xy[i][0];
14090 y = bad_y + xy[i][1];
14091 if (!IN_LEV_FIELD(x, y))
14094 element = Feld[x][y];
14095 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14096 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14104 if (kill_x != bad_x || kill_y != bad_y)
14105 Bang(bad_x, bad_y);
14108 void KillPlayer(struct PlayerInfo *player)
14110 int jx = player->jx, jy = player->jy;
14112 if (!player->active)
14115 /* the following code was introduced to prevent an infinite loop when calling
14117 -> CheckTriggeredElementChangeExt()
14118 -> ExecuteCustomElementAction()
14120 -> (infinitely repeating the above sequence of function calls)
14121 which occurs when killing the player while having a CE with the setting
14122 "kill player X when explosion of <player X>"; the solution using a new
14123 field "player->killed" was chosen for backwards compatibility, although
14124 clever use of the fields "player->active" etc. would probably also work */
14126 if (player->killed)
14130 player->killed = TRUE;
14132 /* remove accessible field at the player's position */
14133 Feld[jx][jy] = EL_EMPTY;
14135 /* deactivate shield (else Bang()/Explode() would not work right) */
14136 player->shield_normal_time_left = 0;
14137 player->shield_deadly_time_left = 0;
14140 BuryPlayer(player);
14143 static void KillPlayerUnlessEnemyProtected(int x, int y)
14145 if (!PLAYER_ENEMY_PROTECTED(x, y))
14146 KillPlayer(PLAYERINFO(x, y));
14149 static void KillPlayerUnlessExplosionProtected(int x, int y)
14151 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14152 KillPlayer(PLAYERINFO(x, y));
14155 void BuryPlayer(struct PlayerInfo *player)
14157 int jx = player->jx, jy = player->jy;
14159 if (!player->active)
14162 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14163 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14165 player->GameOver = TRUE;
14166 RemovePlayer(player);
14169 void RemovePlayer(struct PlayerInfo *player)
14171 int jx = player->jx, jy = player->jy;
14172 int i, found = FALSE;
14174 player->present = FALSE;
14175 player->active = FALSE;
14177 if (!ExplodeField[jx][jy])
14178 StorePlayer[jx][jy] = 0;
14180 if (player->is_moving)
14181 TEST_DrawLevelField(player->last_jx, player->last_jy);
14183 for (i = 0; i < MAX_PLAYERS; i++)
14184 if (stored_player[i].active)
14188 AllPlayersGone = TRUE;
14194 #if USE_NEW_SNAP_DELAY
14195 static void setFieldForSnapping(int x, int y, int element, int direction)
14197 struct ElementInfo *ei = &element_info[element];
14198 int direction_bit = MV_DIR_TO_BIT(direction);
14199 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14200 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14201 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14203 Feld[x][y] = EL_ELEMENT_SNAPPING;
14204 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14206 ResetGfxAnimation(x, y);
14208 GfxElement[x][y] = element;
14209 GfxAction[x][y] = action;
14210 GfxDir[x][y] = direction;
14211 GfxFrame[x][y] = -1;
14216 =============================================================================
14217 checkDiagonalPushing()
14218 -----------------------------------------------------------------------------
14219 check if diagonal input device direction results in pushing of object
14220 (by checking if the alternative direction is walkable, diggable, ...)
14221 =============================================================================
14224 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14225 int x, int y, int real_dx, int real_dy)
14227 int jx, jy, dx, dy, xx, yy;
14229 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14232 /* diagonal direction: check alternative direction */
14237 xx = jx + (dx == 0 ? real_dx : 0);
14238 yy = jy + (dy == 0 ? real_dy : 0);
14240 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14244 =============================================================================
14246 -----------------------------------------------------------------------------
14247 x, y: field next to player (non-diagonal) to try to dig to
14248 real_dx, real_dy: direction as read from input device (can be diagonal)
14249 =============================================================================
14252 static int DigField(struct PlayerInfo *player,
14253 int oldx, int oldy, int x, int y,
14254 int real_dx, int real_dy, int mode)
14256 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14257 boolean player_was_pushing = player->is_pushing;
14258 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14259 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14260 int jx = oldx, jy = oldy;
14261 int dx = x - jx, dy = y - jy;
14262 int nextx = x + dx, nexty = y + dy;
14263 int move_direction = (dx == -1 ? MV_LEFT :
14264 dx == +1 ? MV_RIGHT :
14266 dy == +1 ? MV_DOWN : MV_NONE);
14267 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14268 int dig_side = MV_DIR_OPPOSITE(move_direction);
14269 int old_element = Feld[jx][jy];
14270 #if USE_FIXED_DONT_RUN_INTO
14271 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14277 if (is_player) /* function can also be called by EL_PENGUIN */
14279 if (player->MovPos == 0)
14281 player->is_digging = FALSE;
14282 player->is_collecting = FALSE;
14285 if (player->MovPos == 0) /* last pushing move finished */
14286 player->is_pushing = FALSE;
14288 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14290 player->is_switching = FALSE;
14291 player->push_delay = -1;
14293 return MP_NO_ACTION;
14297 #if !USE_FIXED_DONT_RUN_INTO
14298 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14299 return MP_NO_ACTION;
14302 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14303 old_element = Back[jx][jy];
14305 /* in case of element dropped at player position, check background */
14306 else if (Back[jx][jy] != EL_EMPTY &&
14307 game.engine_version >= VERSION_IDENT(2,2,0,0))
14308 old_element = Back[jx][jy];
14310 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14311 return MP_NO_ACTION; /* field has no opening in this direction */
14313 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14314 return MP_NO_ACTION; /* field has no opening in this direction */
14316 #if USE_FIXED_DONT_RUN_INTO
14317 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14321 Feld[jx][jy] = player->artwork_element;
14322 InitMovingField(jx, jy, MV_DOWN);
14323 Store[jx][jy] = EL_ACID;
14324 ContinueMoving(jx, jy);
14325 BuryPlayer(player);
14327 return MP_DONT_RUN_INTO;
14331 #if USE_FIXED_DONT_RUN_INTO
14332 if (player_can_move && DONT_RUN_INTO(element))
14334 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14336 return MP_DONT_RUN_INTO;
14340 #if USE_FIXED_DONT_RUN_INTO
14341 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14342 return MP_NO_ACTION;
14345 #if !USE_FIXED_DONT_RUN_INTO
14346 element = Feld[x][y];
14349 collect_count = element_info[element].collect_count_initial;
14351 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14352 return MP_NO_ACTION;
14354 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14355 player_can_move = player_can_move_or_snap;
14357 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14358 game.engine_version >= VERSION_IDENT(2,2,0,0))
14360 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14361 player->index_bit, dig_side);
14362 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14363 player->index_bit, dig_side);
14365 if (element == EL_DC_LANDMINE)
14368 if (Feld[x][y] != element) /* field changed by snapping */
14371 return MP_NO_ACTION;
14374 #if USE_PLAYER_GRAVITY
14375 if (player->gravity && is_player && !player->is_auto_moving &&
14376 canFallDown(player) && move_direction != MV_DOWN &&
14377 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14378 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14380 if (game.gravity && is_player && !player->is_auto_moving &&
14381 canFallDown(player) && move_direction != MV_DOWN &&
14382 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14383 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14386 if (player_can_move &&
14387 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14389 int sound_element = SND_ELEMENT(element);
14390 int sound_action = ACTION_WALKING;
14392 if (IS_RND_GATE(element))
14394 if (!player->key[RND_GATE_NR(element)])
14395 return MP_NO_ACTION;
14397 else if (IS_RND_GATE_GRAY(element))
14399 if (!player->key[RND_GATE_GRAY_NR(element)])
14400 return MP_NO_ACTION;
14402 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14404 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14405 return MP_NO_ACTION;
14407 else if (element == EL_EXIT_OPEN ||
14408 element == EL_EM_EXIT_OPEN ||
14409 element == EL_STEEL_EXIT_OPEN ||
14410 element == EL_EM_STEEL_EXIT_OPEN ||
14411 element == EL_SP_EXIT_OPEN ||
14412 element == EL_SP_EXIT_OPENING)
14414 sound_action = ACTION_PASSING; /* player is passing exit */
14416 else if (element == EL_EMPTY)
14418 sound_action = ACTION_MOVING; /* nothing to walk on */
14421 /* play sound from background or player, whatever is available */
14422 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14423 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14425 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14427 else if (player_can_move &&
14428 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14430 if (!ACCESS_FROM(element, opposite_direction))
14431 return MP_NO_ACTION; /* field not accessible from this direction */
14433 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14434 return MP_NO_ACTION;
14436 if (IS_EM_GATE(element))
14438 if (!player->key[EM_GATE_NR(element)])
14439 return MP_NO_ACTION;
14441 else if (IS_EM_GATE_GRAY(element))
14443 if (!player->key[EM_GATE_GRAY_NR(element)])
14444 return MP_NO_ACTION;
14446 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14448 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14449 return MP_NO_ACTION;
14451 else if (IS_EMC_GATE(element))
14453 if (!player->key[EMC_GATE_NR(element)])
14454 return MP_NO_ACTION;
14456 else if (IS_EMC_GATE_GRAY(element))
14458 if (!player->key[EMC_GATE_GRAY_NR(element)])
14459 return MP_NO_ACTION;
14461 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14463 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14464 return MP_NO_ACTION;
14466 else if (element == EL_DC_GATE_WHITE ||
14467 element == EL_DC_GATE_WHITE_GRAY ||
14468 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14470 if (player->num_white_keys == 0)
14471 return MP_NO_ACTION;
14473 player->num_white_keys--;
14475 else if (IS_SP_PORT(element))
14477 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14478 element == EL_SP_GRAVITY_PORT_RIGHT ||
14479 element == EL_SP_GRAVITY_PORT_UP ||
14480 element == EL_SP_GRAVITY_PORT_DOWN)
14481 #if USE_PLAYER_GRAVITY
14482 player->gravity = !player->gravity;
14484 game.gravity = !game.gravity;
14486 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14487 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14488 element == EL_SP_GRAVITY_ON_PORT_UP ||
14489 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14490 #if USE_PLAYER_GRAVITY
14491 player->gravity = TRUE;
14493 game.gravity = TRUE;
14495 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14496 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14497 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14498 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14499 #if USE_PLAYER_GRAVITY
14500 player->gravity = FALSE;
14502 game.gravity = FALSE;
14506 /* automatically move to the next field with double speed */
14507 player->programmed_action = move_direction;
14509 if (player->move_delay_reset_counter == 0)
14511 player->move_delay_reset_counter = 2; /* two double speed steps */
14513 DOUBLE_PLAYER_SPEED(player);
14516 PlayLevelSoundAction(x, y, ACTION_PASSING);
14518 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14522 if (mode != DF_SNAP)
14524 GfxElement[x][y] = GFX_ELEMENT(element);
14525 player->is_digging = TRUE;
14528 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14530 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14531 player->index_bit, dig_side);
14533 if (mode == DF_SNAP)
14535 #if USE_NEW_SNAP_DELAY
14536 if (level.block_snap_field)
14537 setFieldForSnapping(x, y, element, move_direction);
14539 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14541 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14544 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14545 player->index_bit, dig_side);
14548 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14552 if (is_player && mode != DF_SNAP)
14554 GfxElement[x][y] = element;
14555 player->is_collecting = TRUE;
14558 if (element == EL_SPEED_PILL)
14560 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14562 else if (element == EL_EXTRA_TIME && level.time > 0)
14564 TimeLeft += level.extra_time;
14567 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14569 DisplayGameControlValues();
14571 DrawGameValue_Time(TimeLeft);
14574 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14576 player->shield_normal_time_left += level.shield_normal_time;
14577 if (element == EL_SHIELD_DEADLY)
14578 player->shield_deadly_time_left += level.shield_deadly_time;
14580 else if (element == EL_DYNAMITE ||
14581 element == EL_EM_DYNAMITE ||
14582 element == EL_SP_DISK_RED)
14584 if (player->inventory_size < MAX_INVENTORY_SIZE)
14585 player->inventory_element[player->inventory_size++] = element;
14587 DrawGameDoorValues();
14589 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14591 player->dynabomb_count++;
14592 player->dynabombs_left++;
14594 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14596 player->dynabomb_size++;
14598 else if (element == EL_DYNABOMB_INCREASE_POWER)
14600 player->dynabomb_xl = TRUE;
14602 else if (IS_KEY(element))
14604 player->key[KEY_NR(element)] = TRUE;
14606 DrawGameDoorValues();
14608 else if (element == EL_DC_KEY_WHITE)
14610 player->num_white_keys++;
14612 /* display white keys? */
14613 /* DrawGameDoorValues(); */
14615 else if (IS_ENVELOPE(element))
14617 player->show_envelope = element;
14619 else if (element == EL_EMC_LENSES)
14621 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14623 RedrawAllInvisibleElementsForLenses();
14625 else if (element == EL_EMC_MAGNIFIER)
14627 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14629 RedrawAllInvisibleElementsForMagnifier();
14631 else if (IS_DROPPABLE(element) ||
14632 IS_THROWABLE(element)) /* can be collected and dropped */
14636 if (collect_count == 0)
14637 player->inventory_infinite_element = element;
14639 for (i = 0; i < collect_count; i++)
14640 if (player->inventory_size < MAX_INVENTORY_SIZE)
14641 player->inventory_element[player->inventory_size++] = element;
14643 DrawGameDoorValues();
14645 else if (collect_count > 0)
14647 local_player->gems_still_needed -= collect_count;
14648 if (local_player->gems_still_needed < 0)
14649 local_player->gems_still_needed = 0;
14652 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14654 DisplayGameControlValues();
14656 DrawGameValue_Emeralds(local_player->gems_still_needed);
14660 RaiseScoreElement(element);
14661 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14664 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14665 player->index_bit, dig_side);
14667 if (mode == DF_SNAP)
14669 #if USE_NEW_SNAP_DELAY
14670 if (level.block_snap_field)
14671 setFieldForSnapping(x, y, element, move_direction);
14673 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14675 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14678 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14679 player->index_bit, dig_side);
14682 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14684 if (mode == DF_SNAP && element != EL_BD_ROCK)
14685 return MP_NO_ACTION;
14687 if (CAN_FALL(element) && dy)
14688 return MP_NO_ACTION;
14690 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14691 !(element == EL_SPRING && level.use_spring_bug))
14692 return MP_NO_ACTION;
14694 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14695 ((move_direction & MV_VERTICAL &&
14696 ((element_info[element].move_pattern & MV_LEFT &&
14697 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14698 (element_info[element].move_pattern & MV_RIGHT &&
14699 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14700 (move_direction & MV_HORIZONTAL &&
14701 ((element_info[element].move_pattern & MV_UP &&
14702 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14703 (element_info[element].move_pattern & MV_DOWN &&
14704 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14705 return MP_NO_ACTION;
14707 /* do not push elements already moving away faster than player */
14708 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14709 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14710 return MP_NO_ACTION;
14712 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14714 if (player->push_delay_value == -1 || !player_was_pushing)
14715 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14717 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14719 if (player->push_delay_value == -1)
14720 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14722 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14724 if (!player->is_pushing)
14725 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14728 player->is_pushing = TRUE;
14729 player->is_active = TRUE;
14731 if (!(IN_LEV_FIELD(nextx, nexty) &&
14732 (IS_FREE(nextx, nexty) ||
14733 (IS_SB_ELEMENT(element) &&
14734 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14735 (IS_CUSTOM_ELEMENT(element) &&
14736 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14737 return MP_NO_ACTION;
14739 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14740 return MP_NO_ACTION;
14742 if (player->push_delay == -1) /* new pushing; restart delay */
14743 player->push_delay = 0;
14745 if (player->push_delay < player->push_delay_value &&
14746 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14747 element != EL_SPRING && element != EL_BALLOON)
14749 /* make sure that there is no move delay before next try to push */
14750 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14751 player->move_delay = 0;
14753 return MP_NO_ACTION;
14756 if (IS_CUSTOM_ELEMENT(element) &&
14757 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14759 if (!DigFieldByCE(nextx, nexty, element))
14760 return MP_NO_ACTION;
14763 if (IS_SB_ELEMENT(element))
14765 if (element == EL_SOKOBAN_FIELD_FULL)
14767 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14768 local_player->sokobanfields_still_needed++;
14771 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14773 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14774 local_player->sokobanfields_still_needed--;
14777 Feld[x][y] = EL_SOKOBAN_OBJECT;
14779 if (Back[x][y] == Back[nextx][nexty])
14780 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14781 else if (Back[x][y] != 0)
14782 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14785 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14788 if (local_player->sokobanfields_still_needed == 0 &&
14789 game.emulation == EMU_SOKOBAN)
14791 PlayerWins(player);
14793 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14797 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14799 InitMovingField(x, y, move_direction);
14800 GfxAction[x][y] = ACTION_PUSHING;
14802 if (mode == DF_SNAP)
14803 ContinueMoving(x, y);
14805 MovPos[x][y] = (dx != 0 ? dx : dy);
14807 Pushed[x][y] = TRUE;
14808 Pushed[nextx][nexty] = TRUE;
14810 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14811 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14813 player->push_delay_value = -1; /* get new value later */
14815 /* check for element change _after_ element has been pushed */
14816 if (game.use_change_when_pushing_bug)
14818 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14819 player->index_bit, dig_side);
14820 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14821 player->index_bit, dig_side);
14824 else if (IS_SWITCHABLE(element))
14826 if (PLAYER_SWITCHING(player, x, y))
14828 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14829 player->index_bit, dig_side);
14834 player->is_switching = TRUE;
14835 player->switch_x = x;
14836 player->switch_y = y;
14838 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14840 if (element == EL_ROBOT_WHEEL)
14842 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14846 game.robot_wheel_active = TRUE;
14848 TEST_DrawLevelField(x, y);
14850 else if (element == EL_SP_TERMINAL)
14854 SCAN_PLAYFIELD(xx, yy)
14856 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14858 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14859 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14862 else if (IS_BELT_SWITCH(element))
14864 ToggleBeltSwitch(x, y);
14866 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14867 element == EL_SWITCHGATE_SWITCH_DOWN ||
14868 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14869 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14871 ToggleSwitchgateSwitch(x, y);
14873 else if (element == EL_LIGHT_SWITCH ||
14874 element == EL_LIGHT_SWITCH_ACTIVE)
14876 ToggleLightSwitch(x, y);
14878 else if (element == EL_TIMEGATE_SWITCH ||
14879 element == EL_DC_TIMEGATE_SWITCH)
14881 ActivateTimegateSwitch(x, y);
14883 else if (element == EL_BALLOON_SWITCH_LEFT ||
14884 element == EL_BALLOON_SWITCH_RIGHT ||
14885 element == EL_BALLOON_SWITCH_UP ||
14886 element == EL_BALLOON_SWITCH_DOWN ||
14887 element == EL_BALLOON_SWITCH_NONE ||
14888 element == EL_BALLOON_SWITCH_ANY)
14890 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14891 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14892 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14893 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14894 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14897 else if (element == EL_LAMP)
14899 Feld[x][y] = EL_LAMP_ACTIVE;
14900 local_player->lights_still_needed--;
14902 ResetGfxAnimation(x, y);
14903 TEST_DrawLevelField(x, y);
14905 else if (element == EL_TIME_ORB_FULL)
14907 Feld[x][y] = EL_TIME_ORB_EMPTY;
14909 if (level.time > 0 || level.use_time_orb_bug)
14911 TimeLeft += level.time_orb_time;
14914 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14916 DisplayGameControlValues();
14918 DrawGameValue_Time(TimeLeft);
14922 ResetGfxAnimation(x, y);
14923 TEST_DrawLevelField(x, y);
14925 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14926 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14930 game.ball_state = !game.ball_state;
14932 SCAN_PLAYFIELD(xx, yy)
14934 int e = Feld[xx][yy];
14936 if (game.ball_state)
14938 if (e == EL_EMC_MAGIC_BALL)
14939 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14940 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14941 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14945 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14946 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14947 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14948 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14953 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14954 player->index_bit, dig_side);
14956 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14957 player->index_bit, dig_side);
14959 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14960 player->index_bit, dig_side);
14966 if (!PLAYER_SWITCHING(player, x, y))
14968 player->is_switching = TRUE;
14969 player->switch_x = x;
14970 player->switch_y = y;
14972 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14973 player->index_bit, dig_side);
14974 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14975 player->index_bit, dig_side);
14977 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14978 player->index_bit, dig_side);
14979 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14980 player->index_bit, dig_side);
14983 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14984 player->index_bit, dig_side);
14985 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14986 player->index_bit, dig_side);
14988 return MP_NO_ACTION;
14991 player->push_delay = -1;
14993 if (is_player) /* function can also be called by EL_PENGUIN */
14995 if (Feld[x][y] != element) /* really digged/collected something */
14997 player->is_collecting = !player->is_digging;
14998 player->is_active = TRUE;
15005 static boolean DigFieldByCE(int x, int y, int digging_element)
15007 int element = Feld[x][y];
15009 if (!IS_FREE(x, y))
15011 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15012 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15015 /* no element can dig solid indestructible elements */
15016 if (IS_INDESTRUCTIBLE(element) &&
15017 !IS_DIGGABLE(element) &&
15018 !IS_COLLECTIBLE(element))
15021 if (AmoebaNr[x][y] &&
15022 (element == EL_AMOEBA_FULL ||
15023 element == EL_BD_AMOEBA ||
15024 element == EL_AMOEBA_GROWING))
15026 AmoebaCnt[AmoebaNr[x][y]]--;
15027 AmoebaCnt2[AmoebaNr[x][y]]--;
15030 if (IS_MOVING(x, y))
15031 RemoveMovingField(x, y);
15035 TEST_DrawLevelField(x, y);
15038 /* if digged element was about to explode, prevent the explosion */
15039 ExplodeField[x][y] = EX_TYPE_NONE;
15041 PlayLevelSoundAction(x, y, action);
15044 Store[x][y] = EL_EMPTY;
15047 /* this makes it possible to leave the removed element again */
15048 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15049 Store[x][y] = element;
15051 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15053 int move_leave_element = element_info[digging_element].move_leave_element;
15055 /* this makes it possible to leave the removed element again */
15056 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15057 element : move_leave_element);
15064 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15066 int jx = player->jx, jy = player->jy;
15067 int x = jx + dx, y = jy + dy;
15068 int snap_direction = (dx == -1 ? MV_LEFT :
15069 dx == +1 ? MV_RIGHT :
15071 dy == +1 ? MV_DOWN : MV_NONE);
15072 boolean can_continue_snapping = (level.continuous_snapping &&
15073 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15075 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15078 if (!player->active || !IN_LEV_FIELD(x, y))
15086 if (player->MovPos == 0)
15087 player->is_pushing = FALSE;
15089 player->is_snapping = FALSE;
15091 if (player->MovPos == 0)
15093 player->is_moving = FALSE;
15094 player->is_digging = FALSE;
15095 player->is_collecting = FALSE;
15101 #if USE_NEW_CONTINUOUS_SNAPPING
15102 /* prevent snapping with already pressed snap key when not allowed */
15103 if (player->is_snapping && !can_continue_snapping)
15106 if (player->is_snapping)
15110 player->MovDir = snap_direction;
15112 if (player->MovPos == 0)
15114 player->is_moving = FALSE;
15115 player->is_digging = FALSE;
15116 player->is_collecting = FALSE;
15119 player->is_dropping = FALSE;
15120 player->is_dropping_pressed = FALSE;
15121 player->drop_pressed_delay = 0;
15123 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15126 player->is_snapping = TRUE;
15127 player->is_active = TRUE;
15129 if (player->MovPos == 0)
15131 player->is_moving = FALSE;
15132 player->is_digging = FALSE;
15133 player->is_collecting = FALSE;
15136 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15137 TEST_DrawLevelField(player->last_jx, player->last_jy);
15139 TEST_DrawLevelField(x, y);
15144 static boolean DropElement(struct PlayerInfo *player)
15146 int old_element, new_element;
15147 int dropx = player->jx, dropy = player->jy;
15148 int drop_direction = player->MovDir;
15149 int drop_side = drop_direction;
15151 int drop_element = get_next_dropped_element(player);
15153 int drop_element = (player->inventory_size > 0 ?
15154 player->inventory_element[player->inventory_size - 1] :
15155 player->inventory_infinite_element != EL_UNDEFINED ?
15156 player->inventory_infinite_element :
15157 player->dynabombs_left > 0 ?
15158 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15162 player->is_dropping_pressed = TRUE;
15164 /* do not drop an element on top of another element; when holding drop key
15165 pressed without moving, dropped element must move away before the next
15166 element can be dropped (this is especially important if the next element
15167 is dynamite, which can be placed on background for historical reasons) */
15168 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15171 if (IS_THROWABLE(drop_element))
15173 dropx += GET_DX_FROM_DIR(drop_direction);
15174 dropy += GET_DY_FROM_DIR(drop_direction);
15176 if (!IN_LEV_FIELD(dropx, dropy))
15180 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15181 new_element = drop_element; /* default: no change when dropping */
15183 /* check if player is active, not moving and ready to drop */
15184 if (!player->active || player->MovPos || player->drop_delay > 0)
15187 /* check if player has anything that can be dropped */
15188 if (new_element == EL_UNDEFINED)
15191 /* check if drop key was pressed long enough for EM style dynamite */
15192 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15195 /* check if anything can be dropped at the current position */
15196 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15199 /* collected custom elements can only be dropped on empty fields */
15200 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15203 if (old_element != EL_EMPTY)
15204 Back[dropx][dropy] = old_element; /* store old element on this field */
15206 ResetGfxAnimation(dropx, dropy);
15207 ResetRandomAnimationValue(dropx, dropy);
15209 if (player->inventory_size > 0 ||
15210 player->inventory_infinite_element != EL_UNDEFINED)
15212 if (player->inventory_size > 0)
15214 player->inventory_size--;
15216 DrawGameDoorValues();
15218 if (new_element == EL_DYNAMITE)
15219 new_element = EL_DYNAMITE_ACTIVE;
15220 else if (new_element == EL_EM_DYNAMITE)
15221 new_element = EL_EM_DYNAMITE_ACTIVE;
15222 else if (new_element == EL_SP_DISK_RED)
15223 new_element = EL_SP_DISK_RED_ACTIVE;
15226 Feld[dropx][dropy] = new_element;
15228 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15229 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15230 el2img(Feld[dropx][dropy]), 0);
15232 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15234 /* needed if previous element just changed to "empty" in the last frame */
15235 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15237 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15238 player->index_bit, drop_side);
15239 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15241 player->index_bit, drop_side);
15243 TestIfElementTouchesCustomElement(dropx, dropy);
15245 else /* player is dropping a dyna bomb */
15247 player->dynabombs_left--;
15249 Feld[dropx][dropy] = new_element;
15251 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15252 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15253 el2img(Feld[dropx][dropy]), 0);
15255 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15258 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15259 InitField_WithBug1(dropx, dropy, FALSE);
15261 new_element = Feld[dropx][dropy]; /* element might have changed */
15263 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15264 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15266 int move_direction, nextx, nexty;
15268 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15269 MovDir[dropx][dropy] = drop_direction;
15271 move_direction = MovDir[dropx][dropy];
15272 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15273 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15275 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15277 #if USE_FIX_IMPACT_COLLISION
15278 /* do not cause impact style collision by dropping elements that can fall */
15279 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15281 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15285 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15286 player->is_dropping = TRUE;
15288 player->drop_pressed_delay = 0;
15289 player->is_dropping_pressed = FALSE;
15291 player->drop_x = dropx;
15292 player->drop_y = dropy;
15297 /* ------------------------------------------------------------------------- */
15298 /* game sound playing functions */
15299 /* ------------------------------------------------------------------------- */
15301 static int *loop_sound_frame = NULL;
15302 static int *loop_sound_volume = NULL;
15304 void InitPlayLevelSound()
15306 int num_sounds = getSoundListSize();
15308 checked_free(loop_sound_frame);
15309 checked_free(loop_sound_volume);
15311 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15312 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15315 static void PlayLevelSound(int x, int y, int nr)
15317 int sx = SCREENX(x), sy = SCREENY(y);
15318 int volume, stereo_position;
15319 int max_distance = 8;
15320 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15322 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15323 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15326 if (!IN_LEV_FIELD(x, y) ||
15327 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15328 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15331 volume = SOUND_MAX_VOLUME;
15333 if (!IN_SCR_FIELD(sx, sy))
15335 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15336 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15338 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15341 stereo_position = (SOUND_MAX_LEFT +
15342 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15343 (SCR_FIELDX + 2 * max_distance));
15345 if (IS_LOOP_SOUND(nr))
15347 /* This assures that quieter loop sounds do not overwrite louder ones,
15348 while restarting sound volume comparison with each new game frame. */
15350 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15353 loop_sound_volume[nr] = volume;
15354 loop_sound_frame[nr] = FrameCounter;
15357 PlaySoundExt(nr, volume, stereo_position, type);
15360 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15362 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15363 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15364 y < LEVELY(BY1) ? LEVELY(BY1) :
15365 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15369 static void PlayLevelSoundAction(int x, int y, int action)
15371 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15374 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15376 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15378 if (sound_effect != SND_UNDEFINED)
15379 PlayLevelSound(x, y, sound_effect);
15382 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15385 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15387 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15388 PlayLevelSound(x, y, sound_effect);
15391 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15393 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15395 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15396 PlayLevelSound(x, y, sound_effect);
15399 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15401 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15403 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15404 StopSound(sound_effect);
15407 static void PlayLevelMusic()
15409 if (levelset.music[level_nr] != MUS_UNDEFINED)
15410 PlayMusic(levelset.music[level_nr]); /* from config file */
15412 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15415 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15417 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15418 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15419 int x = xx - 1 - offset;
15420 int y = yy - 1 - offset;
15425 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15429 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15433 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15437 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15441 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15445 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15449 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15452 case SAMPLE_android_clone:
15453 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15456 case SAMPLE_android_move:
15457 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15460 case SAMPLE_spring:
15461 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15465 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15469 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15472 case SAMPLE_eater_eat:
15473 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15477 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15480 case SAMPLE_collect:
15481 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15484 case SAMPLE_diamond:
15485 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15488 case SAMPLE_squash:
15489 /* !!! CHECK THIS !!! */
15491 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15493 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15497 case SAMPLE_wonderfall:
15498 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15502 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15506 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15510 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15514 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15518 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15522 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15525 case SAMPLE_wonder:
15526 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15530 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15533 case SAMPLE_exit_open:
15534 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15537 case SAMPLE_exit_leave:
15538 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15541 case SAMPLE_dynamite:
15542 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15546 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15550 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15554 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15558 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15562 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15566 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15570 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15576 void ChangeTime(int value)
15578 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15582 /* EMC game engine uses value from time counter of RND game engine */
15583 level.native_em_level->lev->time = *time;
15585 DrawGameValue_Time(*time);
15588 void RaiseScore(int value)
15590 /* EMC game engine and RND game engine have separate score counters */
15591 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15592 &level.native_em_level->lev->score : &local_player->score);
15596 DrawGameValue_Score(*score);
15600 void RaiseScore(int value)
15602 local_player->score += value;
15605 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15607 DisplayGameControlValues();
15609 DrawGameValue_Score(local_player->score);
15613 void RaiseScoreElement(int element)
15618 case EL_BD_DIAMOND:
15619 case EL_EMERALD_YELLOW:
15620 case EL_EMERALD_RED:
15621 case EL_EMERALD_PURPLE:
15622 case EL_SP_INFOTRON:
15623 RaiseScore(level.score[SC_EMERALD]);
15626 RaiseScore(level.score[SC_DIAMOND]);
15629 RaiseScore(level.score[SC_CRYSTAL]);
15632 RaiseScore(level.score[SC_PEARL]);
15635 case EL_BD_BUTTERFLY:
15636 case EL_SP_ELECTRON:
15637 RaiseScore(level.score[SC_BUG]);
15640 case EL_BD_FIREFLY:
15641 case EL_SP_SNIKSNAK:
15642 RaiseScore(level.score[SC_SPACESHIP]);
15645 case EL_DARK_YAMYAM:
15646 RaiseScore(level.score[SC_YAMYAM]);
15649 RaiseScore(level.score[SC_ROBOT]);
15652 RaiseScore(level.score[SC_PACMAN]);
15655 RaiseScore(level.score[SC_NUT]);
15658 case EL_EM_DYNAMITE:
15659 case EL_SP_DISK_RED:
15660 case EL_DYNABOMB_INCREASE_NUMBER:
15661 case EL_DYNABOMB_INCREASE_SIZE:
15662 case EL_DYNABOMB_INCREASE_POWER:
15663 RaiseScore(level.score[SC_DYNAMITE]);
15665 case EL_SHIELD_NORMAL:
15666 case EL_SHIELD_DEADLY:
15667 RaiseScore(level.score[SC_SHIELD]);
15669 case EL_EXTRA_TIME:
15670 RaiseScore(level.extra_time_score);
15684 case EL_DC_KEY_WHITE:
15685 RaiseScore(level.score[SC_KEY]);
15688 RaiseScore(element_info[element].collect_score);
15693 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15695 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15697 #if defined(NETWORK_AVALIABLE)
15698 if (options.network)
15699 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15708 FadeSkipNextFadeIn();
15710 fading = fading_none;
15714 OpenDoor(DOOR_CLOSE_1);
15717 game_status = GAME_MODE_MAIN;
15720 DrawAndFadeInMainMenu(REDRAW_FIELD);
15728 FadeOut(REDRAW_FIELD);
15731 game_status = GAME_MODE_MAIN;
15733 DrawAndFadeInMainMenu(REDRAW_FIELD);
15737 else /* continue playing the game */
15739 if (tape.playing && tape.deactivate_display)
15740 TapeDeactivateDisplayOff(TRUE);
15742 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15744 if (tape.playing && tape.deactivate_display)
15745 TapeDeactivateDisplayOn();
15749 void RequestQuitGame(boolean ask_if_really_quit)
15751 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15752 boolean skip_request = AllPlayersGone || quick_quit;
15754 RequestQuitGameExt(skip_request, quick_quit,
15755 "Do you really want to quit the game ?");
15759 /* ------------------------------------------------------------------------- */
15760 /* random generator functions */
15761 /* ------------------------------------------------------------------------- */
15763 unsigned int InitEngineRandom_RND(long seed)
15765 game.num_random_calls = 0;
15768 unsigned int rnd_seed = InitEngineRandom(seed);
15770 printf("::: START RND: %d\n", rnd_seed);
15775 return InitEngineRandom(seed);
15781 unsigned int RND(int max)
15785 game.num_random_calls++;
15787 return GetEngineRandom(max);
15794 /* ------------------------------------------------------------------------- */
15795 /* game engine snapshot handling functions */
15796 /* ------------------------------------------------------------------------- */
15798 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15800 struct EngineSnapshotInfo
15802 /* runtime values for custom element collect score */
15803 int collect_score[NUM_CUSTOM_ELEMENTS];
15805 /* runtime values for group element choice position */
15806 int choice_pos[NUM_GROUP_ELEMENTS];
15808 /* runtime values for belt position animations */
15809 int belt_graphic[4 * NUM_BELT_PARTS];
15810 int belt_anim_mode[4 * NUM_BELT_PARTS];
15813 struct EngineSnapshotNodeInfo
15820 static struct EngineSnapshotInfo engine_snapshot_rnd;
15821 static ListNode *engine_snapshot_list = NULL;
15822 static char *snapshot_level_identifier = NULL;
15823 static int snapshot_level_nr = -1;
15825 void FreeEngineSnapshot()
15827 while (engine_snapshot_list != NULL)
15828 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15831 setString(&snapshot_level_identifier, NULL);
15832 snapshot_level_nr = -1;
15835 static void SaveEngineSnapshotValues_RND()
15837 static int belt_base_active_element[4] =
15839 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15840 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15841 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15842 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15846 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15848 int element = EL_CUSTOM_START + i;
15850 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15853 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15855 int element = EL_GROUP_START + i;
15857 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15860 for (i = 0; i < 4; i++)
15862 for (j = 0; j < NUM_BELT_PARTS; j++)
15864 int element = belt_base_active_element[i] + j;
15865 int graphic = el2img(element);
15866 int anim_mode = graphic_info[graphic].anim_mode;
15868 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15869 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15874 static void LoadEngineSnapshotValues_RND()
15876 unsigned long num_random_calls = game.num_random_calls;
15879 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15881 int element = EL_CUSTOM_START + i;
15883 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15886 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15888 int element = EL_GROUP_START + i;
15890 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15893 for (i = 0; i < 4; i++)
15895 for (j = 0; j < NUM_BELT_PARTS; j++)
15897 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15898 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15900 graphic_info[graphic].anim_mode = anim_mode;
15904 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15906 InitRND(tape.random_seed);
15907 for (i = 0; i < num_random_calls; i++)
15911 if (game.num_random_calls != num_random_calls)
15913 Error(ERR_INFO, "number of random calls out of sync");
15914 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15915 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15916 Error(ERR_EXIT, "this should not happen -- please debug");
15920 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15922 struct EngineSnapshotNodeInfo *bi =
15923 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15925 bi->buffer_orig = buffer;
15926 bi->buffer_copy = checked_malloc(size);
15929 memcpy(bi->buffer_copy, buffer, size);
15931 addNodeToList(&engine_snapshot_list, NULL, bi);
15934 void SaveEngineSnapshot()
15936 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15938 if (level_editor_test_game) /* do not save snapshots from editor */
15941 /* copy some special values to a structure better suited for the snapshot */
15943 SaveEngineSnapshotValues_RND();
15944 SaveEngineSnapshotValues_EM();
15946 /* save values stored in special snapshot structure */
15948 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15949 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15951 /* save further RND engine values */
15953 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15954 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15955 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15957 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15958 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15959 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15960 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15962 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15963 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15964 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15965 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15966 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15968 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15969 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15970 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15972 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15974 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15976 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15977 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15979 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15980 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15981 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15982 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15983 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15984 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15985 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15986 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15987 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15988 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15989 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15990 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15991 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15992 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15993 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15994 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15995 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15996 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15998 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15999 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16001 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16002 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16003 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16005 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16006 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16008 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16009 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16010 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16011 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16012 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16014 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16015 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16017 /* save level identification information */
16019 setString(&snapshot_level_identifier, leveldir_current->identifier);
16020 snapshot_level_nr = level_nr;
16023 ListNode *node = engine_snapshot_list;
16026 while (node != NULL)
16028 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16033 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16037 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16039 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16042 void LoadEngineSnapshot()
16044 ListNode *node = engine_snapshot_list;
16046 if (engine_snapshot_list == NULL)
16049 while (node != NULL)
16051 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16056 /* restore special values from snapshot structure */
16058 LoadEngineSnapshotValues_RND();
16059 LoadEngineSnapshotValues_EM();
16062 boolean CheckEngineSnapshot()
16064 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16065 snapshot_level_nr == level_nr);
16069 /* ---------- new game button stuff ---------------------------------------- */
16071 /* graphic position values for game buttons */
16072 #define GAME_BUTTON_XSIZE 30
16073 #define GAME_BUTTON_YSIZE 30
16074 #define GAME_BUTTON_XPOS 5
16075 #define GAME_BUTTON_YPOS 215
16076 #define SOUND_BUTTON_XPOS 5
16077 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16079 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16080 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16081 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16082 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16083 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16084 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16092 } gamebutton_info[NUM_GAME_BUTTONS] =
16096 &game.button.stop.x, &game.button.stop.y,
16097 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16102 &game.button.pause.x, &game.button.pause.y,
16103 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16104 GAME_CTRL_ID_PAUSE,
16108 &game.button.play.x, &game.button.play.y,
16109 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16114 &game.button.sound_music.x, &game.button.sound_music.y,
16115 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16116 SOUND_CTRL_ID_MUSIC,
16117 "background music on/off"
16120 &game.button.sound_loops.x, &game.button.sound_loops.y,
16121 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16122 SOUND_CTRL_ID_LOOPS,
16123 "sound loops on/off"
16126 &game.button.sound_simple.x,&game.button.sound_simple.y,
16127 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16128 SOUND_CTRL_ID_SIMPLE,
16129 "normal sounds on/off"
16133 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16138 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16139 GAME_CTRL_ID_PAUSE,
16143 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16148 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16149 SOUND_CTRL_ID_MUSIC,
16150 "background music on/off"
16153 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16154 SOUND_CTRL_ID_LOOPS,
16155 "sound loops on/off"
16158 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16159 SOUND_CTRL_ID_SIMPLE,
16160 "normal sounds on/off"
16165 void CreateGameButtons()
16169 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16171 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16172 struct GadgetInfo *gi;
16175 unsigned long event_mask;
16177 int gd_xoffset, gd_yoffset;
16178 int gd_x1, gd_x2, gd_y1, gd_y2;
16181 x = DX + *gamebutton_info[i].x;
16182 y = DY + *gamebutton_info[i].y;
16183 gd_xoffset = gamebutton_info[i].gd_x;
16184 gd_yoffset = gamebutton_info[i].gd_y;
16185 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16186 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16188 if (id == GAME_CTRL_ID_STOP ||
16189 id == GAME_CTRL_ID_PAUSE ||
16190 id == GAME_CTRL_ID_PLAY)
16192 button_type = GD_TYPE_NORMAL_BUTTON;
16194 event_mask = GD_EVENT_RELEASED;
16195 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16196 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16200 button_type = GD_TYPE_CHECK_BUTTON;
16202 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16203 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16204 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16205 event_mask = GD_EVENT_PRESSED;
16206 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16207 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16210 gi = CreateGadget(GDI_CUSTOM_ID, id,
16211 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16216 GDI_X, DX + gd_xoffset,
16217 GDI_Y, DY + gd_yoffset,
16219 GDI_WIDTH, GAME_BUTTON_XSIZE,
16220 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16221 GDI_TYPE, button_type,
16222 GDI_STATE, GD_BUTTON_UNPRESSED,
16223 GDI_CHECKED, checked,
16224 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16225 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16226 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16227 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16228 GDI_DIRECT_DRAW, FALSE,
16229 GDI_EVENT_MASK, event_mask,
16230 GDI_CALLBACK_ACTION, HandleGameButtons,
16234 Error(ERR_EXIT, "cannot create gadget");
16236 game_gadget[id] = gi;
16240 void FreeGameButtons()
16244 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16245 FreeGadget(game_gadget[i]);
16248 static void MapGameButtons()
16252 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16253 MapGadget(game_gadget[i]);
16256 void UnmapGameButtons()
16260 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16261 UnmapGadget(game_gadget[i]);
16264 void RedrawGameButtons()
16268 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16269 RedrawGadget(game_gadget[i]);
16272 static void HandleGameButtons(struct GadgetInfo *gi)
16274 int id = gi->custom_id;
16276 if (game_status != GAME_MODE_PLAYING)
16281 case GAME_CTRL_ID_STOP:
16285 RequestQuitGame(TRUE);
16288 case GAME_CTRL_ID_PAUSE:
16289 if (options.network)
16291 #if defined(NETWORK_AVALIABLE)
16293 SendToServer_ContinuePlaying();
16295 SendToServer_PausePlaying();
16299 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16302 case GAME_CTRL_ID_PLAY:
16305 #if defined(NETWORK_AVALIABLE)
16306 if (options.network)
16307 SendToServer_ContinuePlaying();
16311 tape.pausing = FALSE;
16312 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16317 case SOUND_CTRL_ID_MUSIC:
16318 if (setup.sound_music)
16320 setup.sound_music = FALSE;
16323 else if (audio.music_available)
16325 setup.sound = setup.sound_music = TRUE;
16327 SetAudioMode(setup.sound);
16333 case SOUND_CTRL_ID_LOOPS:
16334 if (setup.sound_loops)
16335 setup.sound_loops = FALSE;
16336 else if (audio.loops_available)
16338 setup.sound = setup.sound_loops = TRUE;
16339 SetAudioMode(setup.sound);
16343 case SOUND_CTRL_ID_SIMPLE:
16344 if (setup.sound_simple)
16345 setup.sound_simple = FALSE;
16346 else if (audio.sound_available)
16348 setup.sound = setup.sound_simple = TRUE;
16349 SetAudioMode(setup.sound);