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)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
85 DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
87 DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_SHIELD_NORMAL 35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
199 #define GAME_PANEL_SHIELD_DEADLY 37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
201 #define GAME_PANEL_EXIT 39
202 #define GAME_PANEL_EMC_MAGIC_BALL 40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
204 #define GAME_PANEL_LIGHT_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
206 #define GAME_PANEL_TIMEGATE_SWITCH 44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
208 #define GAME_PANEL_SWITCHGATE_SWITCH 46
209 #define GAME_PANEL_EMC_LENSES 47
210 #define GAME_PANEL_EMC_LENSES_TIME 48
211 #define GAME_PANEL_EMC_MAGNIFIER 49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
213 #define GAME_PANEL_BALLOON_SWITCH 51
214 #define GAME_PANEL_DYNABOMB_NUMBER 52
215 #define GAME_PANEL_DYNABOMB_SIZE 53
216 #define GAME_PANEL_DYNABOMB_POWER 54
217 #define GAME_PANEL_PENGUINS 55
218 #define GAME_PANEL_SOKOBAN_OBJECTS 56
219 #define GAME_PANEL_SOKOBAN_FIELDS 57
220 #define GAME_PANEL_ROBOT_WHEEL 58
221 #define GAME_PANEL_CONVEYOR_BELT_1 59
222 #define GAME_PANEL_CONVEYOR_BELT_2 60
223 #define GAME_PANEL_CONVEYOR_BELT_3 61
224 #define GAME_PANEL_CONVEYOR_BELT_4 62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
229 #define GAME_PANEL_MAGIC_WALL 67
230 #define GAME_PANEL_MAGIC_WALL_TIME 68
231 #define GAME_PANEL_GRAVITY_STATE 69
232 #define GAME_PANEL_GRAPHIC_1 70
233 #define GAME_PANEL_GRAPHIC_2 71
234 #define GAME_PANEL_GRAPHIC_3 72
235 #define GAME_PANEL_GRAPHIC_4 73
236 #define GAME_PANEL_GRAPHIC_5 74
237 #define GAME_PANEL_GRAPHIC_6 75
238 #define GAME_PANEL_GRAPHIC_7 76
239 #define GAME_PANEL_GRAPHIC_8 77
240 #define GAME_PANEL_ELEMENT_1 78
241 #define GAME_PANEL_ELEMENT_2 79
242 #define GAME_PANEL_ELEMENT_3 80
243 #define GAME_PANEL_ELEMENT_4 81
244 #define GAME_PANEL_ELEMENT_5 82
245 #define GAME_PANEL_ELEMENT_6 83
246 #define GAME_PANEL_ELEMENT_7 84
247 #define GAME_PANEL_ELEMENT_8 85
248 #define GAME_PANEL_ELEMENT_COUNT_1 86
249 #define GAME_PANEL_ELEMENT_COUNT_2 87
250 #define GAME_PANEL_ELEMENT_COUNT_3 88
251 #define GAME_PANEL_ELEMENT_COUNT_4 89
252 #define GAME_PANEL_ELEMENT_COUNT_5 90
253 #define GAME_PANEL_ELEMENT_COUNT_6 91
254 #define GAME_PANEL_ELEMENT_COUNT_7 92
255 #define GAME_PANEL_ELEMENT_COUNT_8 93
256 #define GAME_PANEL_CE_SCORE_1 94
257 #define GAME_PANEL_CE_SCORE_2 95
258 #define GAME_PANEL_CE_SCORE_3 96
259 #define GAME_PANEL_CE_SCORE_4 97
260 #define GAME_PANEL_CE_SCORE_5 98
261 #define GAME_PANEL_CE_SCORE_6 99
262 #define GAME_PANEL_CE_SCORE_7 100
263 #define GAME_PANEL_CE_SCORE_8 101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
272 #define GAME_PANEL_PLAYER_NAME 110
273 #define GAME_PANEL_LEVEL_NAME 111
274 #define GAME_PANEL_LEVEL_AUTHOR 112
276 #define NUM_GAME_PANEL_CONTROLS 113
278 struct GamePanelOrderInfo
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286 struct GamePanelControlInfo
290 struct TextPosInfo *pos;
293 int value, last_value;
294 int frame, last_frame;
299 static struct GamePanelControlInfo game_panel_controls[] =
302 GAME_PANEL_LEVEL_NUMBER,
303 &game.panel.level_number,
312 GAME_PANEL_INVENTORY_COUNT,
313 &game.panel.inventory_count,
317 GAME_PANEL_INVENTORY_FIRST_1,
318 &game.panel.inventory_first[0],
322 GAME_PANEL_INVENTORY_FIRST_2,
323 &game.panel.inventory_first[1],
327 GAME_PANEL_INVENTORY_FIRST_3,
328 &game.panel.inventory_first[2],
332 GAME_PANEL_INVENTORY_FIRST_4,
333 &game.panel.inventory_first[3],
337 GAME_PANEL_INVENTORY_FIRST_5,
338 &game.panel.inventory_first[4],
342 GAME_PANEL_INVENTORY_FIRST_6,
343 &game.panel.inventory_first[5],
347 GAME_PANEL_INVENTORY_FIRST_7,
348 &game.panel.inventory_first[6],
352 GAME_PANEL_INVENTORY_FIRST_8,
353 &game.panel.inventory_first[7],
357 GAME_PANEL_INVENTORY_LAST_1,
358 &game.panel.inventory_last[0],
362 GAME_PANEL_INVENTORY_LAST_2,
363 &game.panel.inventory_last[1],
367 GAME_PANEL_INVENTORY_LAST_3,
368 &game.panel.inventory_last[2],
372 GAME_PANEL_INVENTORY_LAST_4,
373 &game.panel.inventory_last[3],
377 GAME_PANEL_INVENTORY_LAST_5,
378 &game.panel.inventory_last[4],
382 GAME_PANEL_INVENTORY_LAST_6,
383 &game.panel.inventory_last[5],
387 GAME_PANEL_INVENTORY_LAST_7,
388 &game.panel.inventory_last[6],
392 GAME_PANEL_INVENTORY_LAST_8,
393 &game.panel.inventory_last[7],
437 GAME_PANEL_KEY_WHITE,
438 &game.panel.key_white,
442 GAME_PANEL_KEY_WHITE_COUNT,
443 &game.panel.key_white_count,
452 GAME_PANEL_HIGHSCORE,
453 &game.panel.highscore,
477 GAME_PANEL_SHIELD_NORMAL,
478 &game.panel.shield_normal,
482 GAME_PANEL_SHIELD_NORMAL_TIME,
483 &game.panel.shield_normal_time,
487 GAME_PANEL_SHIELD_DEADLY,
488 &game.panel.shield_deadly,
492 GAME_PANEL_SHIELD_DEADLY_TIME,
493 &game.panel.shield_deadly_time,
502 GAME_PANEL_EMC_MAGIC_BALL,
503 &game.panel.emc_magic_ball,
507 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508 &game.panel.emc_magic_ball_switch,
512 GAME_PANEL_LIGHT_SWITCH,
513 &game.panel.light_switch,
517 GAME_PANEL_LIGHT_SWITCH_TIME,
518 &game.panel.light_switch_time,
522 GAME_PANEL_TIMEGATE_SWITCH,
523 &game.panel.timegate_switch,
527 GAME_PANEL_TIMEGATE_SWITCH_TIME,
528 &game.panel.timegate_switch_time,
532 GAME_PANEL_SWITCHGATE_SWITCH,
533 &game.panel.switchgate_switch,
537 GAME_PANEL_EMC_LENSES,
538 &game.panel.emc_lenses,
542 GAME_PANEL_EMC_LENSES_TIME,
543 &game.panel.emc_lenses_time,
547 GAME_PANEL_EMC_MAGNIFIER,
548 &game.panel.emc_magnifier,
552 GAME_PANEL_EMC_MAGNIFIER_TIME,
553 &game.panel.emc_magnifier_time,
557 GAME_PANEL_BALLOON_SWITCH,
558 &game.panel.balloon_switch,
562 GAME_PANEL_DYNABOMB_NUMBER,
563 &game.panel.dynabomb_number,
567 GAME_PANEL_DYNABOMB_SIZE,
568 &game.panel.dynabomb_size,
572 GAME_PANEL_DYNABOMB_POWER,
573 &game.panel.dynabomb_power,
578 &game.panel.penguins,
582 GAME_PANEL_SOKOBAN_OBJECTS,
583 &game.panel.sokoban_objects,
587 GAME_PANEL_SOKOBAN_FIELDS,
588 &game.panel.sokoban_fields,
592 GAME_PANEL_ROBOT_WHEEL,
593 &game.panel.robot_wheel,
597 GAME_PANEL_CONVEYOR_BELT_1,
598 &game.panel.conveyor_belt[0],
602 GAME_PANEL_CONVEYOR_BELT_2,
603 &game.panel.conveyor_belt[1],
607 GAME_PANEL_CONVEYOR_BELT_3,
608 &game.panel.conveyor_belt[2],
612 GAME_PANEL_CONVEYOR_BELT_4,
613 &game.panel.conveyor_belt[3],
617 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618 &game.panel.conveyor_belt_switch[0],
622 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623 &game.panel.conveyor_belt_switch[1],
627 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628 &game.panel.conveyor_belt_switch[2],
632 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633 &game.panel.conveyor_belt_switch[3],
637 GAME_PANEL_MAGIC_WALL,
638 &game.panel.magic_wall,
642 GAME_PANEL_MAGIC_WALL_TIME,
643 &game.panel.magic_wall_time,
647 GAME_PANEL_GRAVITY_STATE,
648 &game.panel.gravity_state,
652 GAME_PANEL_GRAPHIC_1,
653 &game.panel.graphic[0],
657 GAME_PANEL_GRAPHIC_2,
658 &game.panel.graphic[1],
662 GAME_PANEL_GRAPHIC_3,
663 &game.panel.graphic[2],
667 GAME_PANEL_GRAPHIC_4,
668 &game.panel.graphic[3],
672 GAME_PANEL_GRAPHIC_5,
673 &game.panel.graphic[4],
677 GAME_PANEL_GRAPHIC_6,
678 &game.panel.graphic[5],
682 GAME_PANEL_GRAPHIC_7,
683 &game.panel.graphic[6],
687 GAME_PANEL_GRAPHIC_8,
688 &game.panel.graphic[7],
692 GAME_PANEL_ELEMENT_1,
693 &game.panel.element[0],
697 GAME_PANEL_ELEMENT_2,
698 &game.panel.element[1],
702 GAME_PANEL_ELEMENT_3,
703 &game.panel.element[2],
707 GAME_PANEL_ELEMENT_4,
708 &game.panel.element[3],
712 GAME_PANEL_ELEMENT_5,
713 &game.panel.element[4],
717 GAME_PANEL_ELEMENT_6,
718 &game.panel.element[5],
722 GAME_PANEL_ELEMENT_7,
723 &game.panel.element[6],
727 GAME_PANEL_ELEMENT_8,
728 &game.panel.element[7],
732 GAME_PANEL_ELEMENT_COUNT_1,
733 &game.panel.element_count[0],
737 GAME_PANEL_ELEMENT_COUNT_2,
738 &game.panel.element_count[1],
742 GAME_PANEL_ELEMENT_COUNT_3,
743 &game.panel.element_count[2],
747 GAME_PANEL_ELEMENT_COUNT_4,
748 &game.panel.element_count[3],
752 GAME_PANEL_ELEMENT_COUNT_5,
753 &game.panel.element_count[4],
757 GAME_PANEL_ELEMENT_COUNT_6,
758 &game.panel.element_count[5],
762 GAME_PANEL_ELEMENT_COUNT_7,
763 &game.panel.element_count[6],
767 GAME_PANEL_ELEMENT_COUNT_8,
768 &game.panel.element_count[7],
772 GAME_PANEL_CE_SCORE_1,
773 &game.panel.ce_score[0],
777 GAME_PANEL_CE_SCORE_2,
778 &game.panel.ce_score[1],
782 GAME_PANEL_CE_SCORE_3,
783 &game.panel.ce_score[2],
787 GAME_PANEL_CE_SCORE_4,
788 &game.panel.ce_score[3],
792 GAME_PANEL_CE_SCORE_5,
793 &game.panel.ce_score[4],
797 GAME_PANEL_CE_SCORE_6,
798 &game.panel.ce_score[5],
802 GAME_PANEL_CE_SCORE_7,
803 &game.panel.ce_score[6],
807 GAME_PANEL_CE_SCORE_8,
808 &game.panel.ce_score[7],
812 GAME_PANEL_CE_SCORE_1_ELEMENT,
813 &game.panel.ce_score_element[0],
817 GAME_PANEL_CE_SCORE_2_ELEMENT,
818 &game.panel.ce_score_element[1],
822 GAME_PANEL_CE_SCORE_3_ELEMENT,
823 &game.panel.ce_score_element[2],
827 GAME_PANEL_CE_SCORE_4_ELEMENT,
828 &game.panel.ce_score_element[3],
832 GAME_PANEL_CE_SCORE_5_ELEMENT,
833 &game.panel.ce_score_element[4],
837 GAME_PANEL_CE_SCORE_6_ELEMENT,
838 &game.panel.ce_score_element[5],
842 GAME_PANEL_CE_SCORE_7_ELEMENT,
843 &game.panel.ce_score_element[6],
847 GAME_PANEL_CE_SCORE_8_ELEMENT,
848 &game.panel.ce_score_element[7],
852 GAME_PANEL_PLAYER_NAME,
853 &game.panel.player_name,
857 GAME_PANEL_LEVEL_NAME,
858 &game.panel.level_name,
862 GAME_PANEL_LEVEL_AUTHOR,
863 &game.panel.level_author,
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING 3
878 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION 2
880 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF -1
884 #define INITIAL_MOVE_DELAY_ON 0
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED 32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED 4
890 #define MOVE_DELAY_MAX_SPEED 1
892 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
895 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN (1)
901 #define MOVE_STEPSIZE_MAX (TILEX)
903 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
906 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
908 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
909 RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
911 RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
913 RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
915 (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
917 RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
920 RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
922 RND((c)->delay_random))
925 #define GET_VALID_RUNTIME_ELEMENT(e) \
926 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
928 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
929 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
930 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
931 (be) + (e) - EL_SELF)
933 #define GET_PLAYER_FROM_BITS(p) \
934 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
937 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
938 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
939 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
940 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
941 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
942 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
943 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
944 RESOLVED_REFERENCE_ELEMENT(be, e) : \
947 #define CAN_GROW_INTO(e) \
948 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
951 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
955 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
956 (CAN_MOVE_INTO_ACID(e) && \
957 Feld[x][y] == EL_ACID) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
969 (CAN_MOVE_INTO_ACID(e) && \
970 Feld[x][y] == EL_ACID) || \
971 (DONT_COLLIDE_WITH(e) && \
973 !PLAYER_ENEMY_PROTECTED(x, y))))
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
978 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
984 #define ANDROID_CAN_CLONE_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
989 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
992 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1000 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005 Feld[x][y] == EL_EM_EXIT_OPEN || \
1006 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008 IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1013 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1019 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1020 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1022 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1024 #define CE_ENTER_FIELD_COND(e, x, y) \
1025 (!IS_PLAYER(x, y) && \
1026 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1029 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1034 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP 0
1041 #define GAME_CTRL_ID_PAUSE 1
1042 #define GAME_CTRL_ID_PLAY 2
1043 #define SOUND_CTRL_ID_MUSIC 3
1044 #define SOUND_CTRL_ID_LOOPS 4
1045 #define SOUND_CTRL_ID_SIMPLE 5
1047 #define NUM_GAME_BUTTONS 6
1050 /* forward declaration for internal use */
1052 static void CreateField(int, int, int);
1054 static void ResetGfxAnimation(int, int);
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev) \
1089 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1091 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1093 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev) \
1099 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1101 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1103 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1149 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1151 if (recursion_loop_detected) \
1154 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1156 recursion_loop_detected = TRUE; \
1157 recursion_loop_element = (e); \
1160 recursion_loop_depth++; \
1163 #define RECURSION_LOOP_DETECTION_END() \
1165 recursion_loop_depth--; \
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1172 static int map_player_action[MAX_PLAYERS];
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after */
1177 /* a specified time, eventually calling a function when changing */
1178 /* ------------------------------------------------------------------------- */
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1198 struct ChangingElementInfo
1203 void (*pre_change_function)(int x, int y);
1204 void (*change_function)(int x, int y);
1205 void (*post_change_function)(int x, int y);
1208 static struct ChangingElementInfo change_delay_list[] =
1243 EL_STEEL_EXIT_OPENING,
1251 EL_STEEL_EXIT_CLOSING,
1252 EL_STEEL_EXIT_CLOSED,
1279 EL_EM_STEEL_EXIT_OPENING,
1280 EL_EM_STEEL_EXIT_OPEN,
1287 EL_EM_STEEL_EXIT_CLOSING,
1291 EL_EM_STEEL_EXIT_CLOSED,
1315 EL_SWITCHGATE_OPENING,
1323 EL_SWITCHGATE_CLOSING,
1324 EL_SWITCHGATE_CLOSED,
1331 EL_TIMEGATE_OPENING,
1339 EL_TIMEGATE_CLOSING,
1348 EL_ACID_SPLASH_LEFT,
1356 EL_ACID_SPLASH_RIGHT,
1365 EL_SP_BUGGY_BASE_ACTIVATING,
1372 EL_SP_BUGGY_BASE_ACTIVATING,
1373 EL_SP_BUGGY_BASE_ACTIVE,
1380 EL_SP_BUGGY_BASE_ACTIVE,
1404 EL_ROBOT_WHEEL_ACTIVE,
1412 EL_TIMEGATE_SWITCH_ACTIVE,
1420 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421 EL_DC_TIMEGATE_SWITCH,
1428 EL_EMC_MAGIC_BALL_ACTIVE,
1429 EL_EMC_MAGIC_BALL_ACTIVE,
1436 EL_EMC_SPRING_BUMPER_ACTIVE,
1437 EL_EMC_SPRING_BUMPER,
1444 EL_DIAGONAL_SHRINKING,
1452 EL_DIAGONAL_GROWING,
1473 int push_delay_fixed, push_delay_random;
1477 { EL_SPRING, 0, 0 },
1478 { EL_BALLOON, 0, 0 },
1480 { EL_SOKOBAN_OBJECT, 2, 0 },
1481 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1482 { EL_SATELLITE, 2, 0 },
1483 { EL_SP_DISK_YELLOW, 2, 0 },
1485 { EL_UNDEFINED, 0, 0 },
1493 move_stepsize_list[] =
1495 { EL_AMOEBA_DROP, 2 },
1496 { EL_AMOEBA_DROPPING, 2 },
1497 { EL_QUICKSAND_FILLING, 1 },
1498 { EL_QUICKSAND_EMPTYING, 1 },
1499 { EL_QUICKSAND_FAST_FILLING, 2 },
1500 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501 { EL_MAGIC_WALL_FILLING, 2 },
1502 { EL_MAGIC_WALL_EMPTYING, 2 },
1503 { EL_BD_MAGIC_WALL_FILLING, 2 },
1504 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1505 { EL_DC_MAGIC_WALL_FILLING, 2 },
1506 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1508 { EL_UNDEFINED, 0 },
1516 collect_count_list[] =
1519 { EL_BD_DIAMOND, 1 },
1520 { EL_EMERALD_YELLOW, 1 },
1521 { EL_EMERALD_RED, 1 },
1522 { EL_EMERALD_PURPLE, 1 },
1524 { EL_SP_INFOTRON, 1 },
1528 { EL_UNDEFINED, 0 },
1536 access_direction_list[] =
1538 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1540 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1541 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1542 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1543 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1544 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1545 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1546 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1547 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1548 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1550 { EL_SP_PORT_LEFT, MV_RIGHT },
1551 { EL_SP_PORT_RIGHT, MV_LEFT },
1552 { EL_SP_PORT_UP, MV_DOWN },
1553 { EL_SP_PORT_DOWN, MV_UP },
1554 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1555 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1556 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1558 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1559 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1560 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1561 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1562 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1563 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1564 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1565 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1566 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1567 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1568 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1570 { EL_UNDEFINED, MV_NONE }
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1575 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1578 IS_JUST_CHANGING(x, y))
1580 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1588 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1589 (y) >= 0 && (y) <= lev_fieldy - 1; \
1590 (y) += playfield_scan_delta_y) \
1591 for ((x) = playfield_scan_start_x; \
1592 (x) >= 0 && (x) <= lev_fieldx - 1; \
1593 (x) += playfield_scan_delta_x)
1596 void DEBUG_SetMaximumDynamite()
1600 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602 local_player->inventory_element[local_player->inventory_size++] =
1607 static void InitPlayfieldScanModeVars()
1609 if (game.use_reverse_scan_direction)
1611 playfield_scan_start_x = lev_fieldx - 1;
1612 playfield_scan_start_y = lev_fieldy - 1;
1614 playfield_scan_delta_x = -1;
1615 playfield_scan_delta_y = -1;
1619 playfield_scan_start_x = 0;
1620 playfield_scan_start_y = 0;
1622 playfield_scan_delta_x = 1;
1623 playfield_scan_delta_y = 1;
1627 static void InitPlayfieldScanMode(int mode)
1629 game.use_reverse_scan_direction =
1630 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1632 InitPlayfieldScanModeVars();
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1638 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1640 /* make sure that stepsize value is always a power of 2 */
1641 move_stepsize = (1 << log_2(move_stepsize));
1643 return TILEX / move_stepsize;
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1649 int player_nr = player->index_nr;
1650 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1653 /* do no immediately change move delay -- the player might just be moving */
1654 player->move_delay_value_next = move_delay;
1656 /* information if player can move must be set separately */
1657 player->cannot_move = cannot_move;
1661 player->move_delay = game.initial_move_delay[player_nr];
1662 player->move_delay_value = game.initial_move_delay_value[player_nr];
1664 player->move_delay_value_next = -1;
1666 player->move_delay_reset_counter = 0;
1670 void GetPlayerConfig()
1672 GameFrameDelay = setup.game_frame_delay;
1674 if (!audio.sound_available)
1675 setup.sound_simple = FALSE;
1677 if (!audio.loops_available)
1678 setup.sound_loops = FALSE;
1680 if (!audio.music_available)
1681 setup.sound_music = FALSE;
1683 if (!video.fullscreen_available)
1684 setup.fullscreen = FALSE;
1686 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1688 SetAudioMode(setup.sound);
1692 int GetElementFromGroupElement(int element)
1694 if (IS_GROUP_ELEMENT(element))
1696 struct ElementGroupInfo *group = element_info[element].group;
1697 int last_anim_random_frame = gfx.anim_random_frame;
1700 if (group->choice_mode == ANIM_RANDOM)
1701 gfx.anim_random_frame = RND(group->num_elements_resolved);
1703 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704 group->choice_mode, 0,
1707 if (group->choice_mode == ANIM_RANDOM)
1708 gfx.anim_random_frame = last_anim_random_frame;
1710 group->choice_pos++;
1712 element = group->element_resolved[element_pos];
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1720 if (element == EL_SP_MURPHY)
1724 if (stored_player[0].present)
1726 Feld[x][y] = EL_SP_MURPHY_CLONE;
1732 stored_player[0].initial_element = element;
1733 stored_player[0].use_murphy = TRUE;
1735 if (!level.use_artwork_element[0])
1736 stored_player[0].artwork_element = EL_SP_MURPHY;
1739 Feld[x][y] = EL_PLAYER_1;
1745 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746 int jx = player->jx, jy = player->jy;
1748 player->present = TRUE;
1750 player->block_last_field = (element == EL_SP_MURPHY ?
1751 level.sp_block_last_field :
1752 level.block_last_field);
1754 /* ---------- initialize player's last field block delay --------------- */
1756 /* always start with reliable default value (no adjustment needed) */
1757 player->block_delay_adjustment = 0;
1759 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760 if (player->block_last_field && element == EL_SP_MURPHY)
1761 player->block_delay_adjustment = 1;
1763 /* special case 2: in game engines before 3.1.1, blocking was different */
1764 if (game.use_block_last_field_bug)
1765 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1767 if (!options.network || player->connected)
1769 player->active = TRUE;
1771 /* remove potentially duplicate players */
1772 if (StorePlayer[jx][jy] == Feld[x][y])
1773 StorePlayer[jx][jy] = 0;
1775 StorePlayer[x][y] = Feld[x][y];
1779 printf("Player %d activated.\n", player->element_nr);
1780 printf("[Local player is %d and currently %s.]\n",
1781 local_player->element_nr,
1782 local_player->active ? "active" : "not active");
1786 Feld[x][y] = EL_EMPTY;
1788 player->jx = player->last_jx = x;
1789 player->jy = player->last_jy = y;
1792 #if USE_PLAYER_REANIMATION
1795 int player_nr = GET_PLAYER_NR(element);
1796 struct PlayerInfo *player = &stored_player[player_nr];
1798 if (player->active && player->killed)
1799 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1804 static void InitField(int x, int y, boolean init_game)
1806 int element = Feld[x][y];
1815 InitPlayerField(x, y, element, init_game);
1818 case EL_SOKOBAN_FIELD_PLAYER:
1819 element = Feld[x][y] = EL_PLAYER_1;
1820 InitField(x, y, init_game);
1822 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823 InitField(x, y, init_game);
1826 case EL_SOKOBAN_FIELD_EMPTY:
1827 local_player->sokobanfields_still_needed++;
1831 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1849 case EL_SPACESHIP_RIGHT:
1850 case EL_SPACESHIP_UP:
1851 case EL_SPACESHIP_LEFT:
1852 case EL_SPACESHIP_DOWN:
1853 case EL_BD_BUTTERFLY:
1854 case EL_BD_BUTTERFLY_RIGHT:
1855 case EL_BD_BUTTERFLY_UP:
1856 case EL_BD_BUTTERFLY_LEFT:
1857 case EL_BD_BUTTERFLY_DOWN:
1859 case EL_BD_FIREFLY_RIGHT:
1860 case EL_BD_FIREFLY_UP:
1861 case EL_BD_FIREFLY_LEFT:
1862 case EL_BD_FIREFLY_DOWN:
1863 case EL_PACMAN_RIGHT:
1865 case EL_PACMAN_LEFT:
1866 case EL_PACMAN_DOWN:
1868 case EL_YAMYAM_LEFT:
1869 case EL_YAMYAM_RIGHT:
1871 case EL_YAMYAM_DOWN:
1872 case EL_DARK_YAMYAM:
1875 case EL_SP_SNIKSNAK:
1876 case EL_SP_ELECTRON:
1885 case EL_AMOEBA_FULL:
1890 case EL_AMOEBA_DROP:
1891 if (y == lev_fieldy - 1)
1893 Feld[x][y] = EL_AMOEBA_GROWING;
1894 Store[x][y] = EL_AMOEBA_WET;
1898 case EL_DYNAMITE_ACTIVE:
1899 case EL_SP_DISK_RED_ACTIVE:
1900 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904 MovDelay[x][y] = 96;
1907 case EL_EM_DYNAMITE_ACTIVE:
1908 MovDelay[x][y] = 32;
1912 local_player->lights_still_needed++;
1916 local_player->friends_still_needed++;
1921 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1924 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1938 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1942 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1944 game.belt_dir[belt_nr] = belt_dir;
1945 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1947 else /* more than one switch -- set it like the first switch */
1949 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1957 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1960 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1962 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1966 case EL_LIGHT_SWITCH_ACTIVE:
1968 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1971 case EL_INVISIBLE_STEELWALL:
1972 case EL_INVISIBLE_WALL:
1973 case EL_INVISIBLE_SAND:
1974 if (game.light_time_left > 0 ||
1975 game.lenses_time_left > 0)
1976 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1979 case EL_EMC_MAGIC_BALL:
1980 if (game.ball_state)
1981 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1984 case EL_EMC_MAGIC_BALL_SWITCH:
1985 if (game.ball_state)
1986 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1989 case EL_TRIGGER_PLAYER:
1990 case EL_TRIGGER_ELEMENT:
1991 case EL_TRIGGER_CE_VALUE:
1992 case EL_TRIGGER_CE_SCORE:
1994 case EL_ANY_ELEMENT:
1995 case EL_CURRENT_CE_VALUE:
1996 case EL_CURRENT_CE_SCORE:
2013 /* reference elements should not be used on the playfield */
2014 Feld[x][y] = EL_EMPTY;
2018 if (IS_CUSTOM_ELEMENT(element))
2020 if (CAN_MOVE(element))
2023 #if USE_NEW_CUSTOM_VALUE
2024 if (!element_info[element].use_last_ce_value || init_game)
2025 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2028 else if (IS_GROUP_ELEMENT(element))
2030 Feld[x][y] = GetElementFromGroupElement(element);
2032 InitField(x, y, init_game);
2039 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2044 InitField(x, y, init_game);
2046 /* not needed to call InitMovDir() -- already done by InitField()! */
2047 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048 CAN_MOVE(Feld[x][y]))
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2054 int old_element = Feld[x][y];
2056 InitField(x, y, init_game);
2058 /* not needed to call InitMovDir() -- already done by InitField()! */
2059 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060 CAN_MOVE(old_element) &&
2061 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2064 /* this case is in fact a combination of not less than three bugs:
2065 first, it calls InitMovDir() for elements that can move, although this is
2066 already done by InitField(); then, it checks the element that was at this
2067 field _before_ the call to InitField() (which can change it); lastly, it
2068 was not called for "mole with direction" elements, which were treated as
2069 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075 static int get_key_element_from_nr(int key_nr)
2077 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079 EL_EM_KEY_1 : EL_KEY_1);
2081 return key_base_element + key_nr;
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2086 return (player->inventory_size > 0 ?
2087 player->inventory_element[player->inventory_size - 1] :
2088 player->inventory_infinite_element != EL_UNDEFINED ?
2089 player->inventory_infinite_element :
2090 player->dynabombs_left > 0 ?
2091 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2097 /* pos >= 0: get element from bottom of the stack;
2098 pos < 0: get element from top of the stack */
2102 int min_inventory_size = -pos;
2103 int inventory_pos = player->inventory_size - min_inventory_size;
2104 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2106 return (player->inventory_size >= min_inventory_size ?
2107 player->inventory_element[inventory_pos] :
2108 player->inventory_infinite_element != EL_UNDEFINED ?
2109 player->inventory_infinite_element :
2110 player->dynabombs_left >= min_dynabombs_left ?
2111 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116 int min_dynabombs_left = pos + 1;
2117 int min_inventory_size = pos + 1 - player->dynabombs_left;
2118 int inventory_pos = pos - player->dynabombs_left;
2120 return (player->inventory_infinite_element != EL_UNDEFINED ?
2121 player->inventory_infinite_element :
2122 player->dynabombs_left >= min_dynabombs_left ?
2123 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124 player->inventory_size >= min_inventory_size ?
2125 player->inventory_element[inventory_pos] :
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2132 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2136 if (gpo1->sort_priority != gpo2->sort_priority)
2137 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2139 compare_result = gpo1->nr - gpo2->nr;
2141 return compare_result;
2144 void InitGameControlValues()
2148 for (i = 0; game_panel_controls[i].nr != -1; i++)
2150 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152 struct TextPosInfo *pos = gpc->pos;
2154 int type = gpc->type;
2158 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159 Error(ERR_EXIT, "this should not happen -- please debug");
2162 /* force update of game controls after initialization */
2163 gpc->value = gpc->last_value = -1;
2164 gpc->frame = gpc->last_frame = -1;
2165 gpc->gfx_frame = -1;
2167 /* determine panel value width for later calculation of alignment */
2168 if (type == TYPE_INTEGER || type == TYPE_STRING)
2170 pos->width = pos->size * getFontWidth(pos->font);
2171 pos->height = getFontHeight(pos->font);
2173 else if (type == TYPE_ELEMENT)
2175 pos->width = pos->size;
2176 pos->height = pos->size;
2179 /* fill structure for game panel draw order */
2181 gpo->sort_priority = pos->sort_priority;
2184 /* sort game panel controls according to sort_priority and control number */
2185 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2189 void UpdatePlayfieldElementCount()
2191 boolean use_element_count = FALSE;
2194 /* first check if it is needed at all to calculate playfield element count */
2195 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197 use_element_count = TRUE;
2199 if (!use_element_count)
2202 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203 element_info[i].element_count = 0;
2205 SCAN_PLAYFIELD(x, y)
2207 element_info[Feld[x][y]].element_count++;
2210 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212 if (IS_IN_GROUP(j, i))
2213 element_info[EL_GROUP_START + i].element_count +=
2214 element_info[j].element_count;
2217 void UpdateGameControlValues()
2220 int time = (local_player->LevelSolved ?
2221 local_player->LevelSolved_CountingTime :
2222 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223 level.native_em_level->lev->time :
2224 level.time == 0 ? TimePlayed : TimeLeft);
2225 int score = (local_player->LevelSolved ?
2226 local_player->LevelSolved_CountingScore :
2227 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228 level.native_em_level->lev->score :
2229 local_player->score);
2230 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231 level.native_em_level->lev->required :
2232 local_player->gems_still_needed);
2233 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234 level.native_em_level->lev->required > 0 :
2235 local_player->gems_still_needed > 0 ||
2236 local_player->sokobanfields_still_needed > 0 ||
2237 local_player->lights_still_needed > 0);
2239 UpdatePlayfieldElementCount();
2241 /* update game panel control values */
2243 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2246 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247 for (i = 0; i < MAX_NUM_KEYS; i++)
2248 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2252 if (game.centered_player_nr == -1)
2254 for (i = 0; i < MAX_PLAYERS; i++)
2256 for (k = 0; k < MAX_NUM_KEYS; k++)
2258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2260 if (level.native_em_level->ply[i]->keys & (1 << k))
2261 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262 get_key_element_from_nr(k);
2264 else if (stored_player[i].key[k])
2265 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266 get_key_element_from_nr(k);
2269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271 level.native_em_level->ply[i]->dynamite;
2273 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274 stored_player[i].inventory_size;
2276 if (stored_player[i].num_white_keys > 0)
2277 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2280 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281 stored_player[i].num_white_keys;
2286 int player_nr = game.centered_player_nr;
2288 for (k = 0; k < MAX_NUM_KEYS; k++)
2290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2292 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294 get_key_element_from_nr(k);
2296 else if (stored_player[player_nr].key[k])
2297 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298 get_key_element_from_nr(k);
2301 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303 level.native_em_level->ply[player_nr]->dynamite;
2305 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306 stored_player[player_nr].inventory_size;
2308 if (stored_player[player_nr].num_white_keys > 0)
2309 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2311 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312 stored_player[player_nr].num_white_keys;
2315 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2317 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318 get_inventory_element_from_pos(local_player, i);
2319 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320 get_inventory_element_from_pos(local_player, -i - 1);
2323 game_panel_controls[GAME_PANEL_SCORE].value = score;
2324 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2326 game_panel_controls[GAME_PANEL_TIME].value = time;
2328 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2332 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2335 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336 local_player->shield_normal_time_left;
2337 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2340 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341 local_player->shield_deadly_time_left;
2343 game_panel_controls[GAME_PANEL_EXIT].value =
2344 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2346 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350 EL_EMC_MAGIC_BALL_SWITCH);
2352 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355 game.light_time_left;
2357 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360 game.timegate_time_left;
2362 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2365 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368 game.lenses_time_left;
2370 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373 game.magnify_time_left;
2375 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2377 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2379 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2380 EL_BALLOON_SWITCH_NONE);
2382 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383 local_player->dynabomb_count;
2384 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385 local_player->dynabomb_size;
2386 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2389 game_panel_controls[GAME_PANEL_PENGUINS].value =
2390 local_player->friends_still_needed;
2392 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393 local_player->sokobanfields_still_needed;
2394 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395 local_player->sokobanfields_still_needed;
2397 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2400 for (i = 0; i < NUM_BELTS; i++)
2402 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2409 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412 game.magic_wall_time_left;
2414 #if USE_PLAYER_GRAVITY
2415 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416 local_player->gravity;
2418 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2421 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2424 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427 game.panel.element[i].id : EL_UNDEFINED);
2429 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432 element_info[game.panel.element_count[i].id].element_count : 0);
2434 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437 element_info[game.panel.ce_score[i].id].collect_score : 0);
2439 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442 element_info[game.panel.ce_score_element[i].id].collect_score :
2445 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2449 /* update game panel control frames */
2451 for (i = 0; game_panel_controls[i].nr != -1; i++)
2453 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2455 if (gpc->type == TYPE_ELEMENT)
2457 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2459 int last_anim_random_frame = gfx.anim_random_frame;
2460 int element = gpc->value;
2461 int graphic = el2panelimg(element);
2463 if (gpc->value != gpc->last_value)
2466 gpc->gfx_random = INIT_GFX_RANDOM();
2472 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474 gpc->gfx_random = INIT_GFX_RANDOM();
2477 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478 gfx.anim_random_frame = gpc->gfx_random;
2480 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481 gpc->gfx_frame = element_info[element].collect_score;
2483 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2486 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487 gfx.anim_random_frame = last_anim_random_frame;
2493 void DisplayGameControlValues()
2495 boolean redraw_panel = FALSE;
2498 for (i = 0; game_panel_controls[i].nr != -1; i++)
2500 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2502 if (PANEL_DEACTIVATED(gpc->pos))
2505 if (gpc->value == gpc->last_value &&
2506 gpc->frame == gpc->last_frame)
2509 redraw_panel = TRUE;
2515 /* copy default game door content to main double buffer */
2516 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2519 /* redraw game control buttons */
2521 RedrawGameButtons();
2527 game_status = GAME_MODE_PSEUDO_PANEL;
2530 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2532 for (i = 0; game_panel_controls[i].nr != -1; i++)
2536 int nr = game_panel_order[i].nr;
2537 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2539 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2542 struct TextPosInfo *pos = gpc->pos;
2543 int type = gpc->type;
2544 int value = gpc->value;
2545 int frame = gpc->frame;
2547 int last_value = gpc->last_value;
2548 int last_frame = gpc->last_frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2559 if (value == last_value && frame == last_frame)
2563 gpc->last_value = value;
2564 gpc->last_frame = frame;
2567 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2570 if (type == TYPE_INTEGER)
2572 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573 nr == GAME_PANEL_TIME)
2575 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2577 if (use_dynamic_size) /* use dynamic number of digits */
2579 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581 int size2 = size1 + 1;
2582 int font1 = pos->font;
2583 int font2 = pos->font_alt;
2585 size = (value < value_change ? size1 : size2);
2586 font = (value < value_change ? font1 : font2);
2589 /* clear background if value just changed its size (dynamic digits) */
2590 if ((last_value < value_change) != (value < value_change))
2592 int width1 = size1 * getFontWidth(font1);
2593 int width2 = size2 * getFontWidth(font2);
2594 int max_width = MAX(width1, width2);
2595 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2597 pos->width = max_width;
2599 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600 max_width, max_height);
2607 /* correct text size if "digits" is zero or less */
2609 size = strlen(int2str(value, size));
2611 /* dynamically correct text alignment */
2612 pos->width = size * getFontWidth(font);
2615 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616 int2str(value, size), font, mask_mode);
2618 else if (type == TYPE_ELEMENT)
2620 int element, graphic;
2624 int dst_x = PANEL_XPOS(pos);
2625 int dst_y = PANEL_YPOS(pos);
2628 if (value != EL_UNDEFINED && value != EL_EMPTY)
2631 graphic = el2panelimg(value);
2633 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2636 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2640 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2643 width = graphic_info[graphic].width * size / TILESIZE;
2644 height = graphic_info[graphic].height * size / TILESIZE;
2648 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649 dst_x - src_x, dst_y - src_y);
2650 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2655 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2660 if (value == EL_UNDEFINED || value == EL_EMPTY)
2662 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663 graphic = el2panelimg(element);
2665 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2672 graphic = el2panelimg(value);
2674 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2677 width = graphic_info[graphic].width * size / TILESIZE;
2678 height = graphic_info[graphic].height * size / TILESIZE;
2680 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2683 else if (type == TYPE_STRING)
2685 boolean active = (value != 0);
2686 char *state_normal = "off";
2687 char *state_active = "on";
2688 char *state = (active ? state_active : state_normal);
2689 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2691 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2692 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2694 if (nr == GAME_PANEL_GRAVITY_STATE)
2696 int font1 = pos->font; /* (used for normal state) */
2697 int font2 = pos->font_alt; /* (used for active state) */
2699 int size1 = strlen(state_normal);
2700 int size2 = strlen(state_active);
2701 int width1 = size1 * getFontWidth(font1);
2702 int width2 = size2 * getFontWidth(font2);
2703 int max_width = MAX(width1, width2);
2704 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2706 pos->width = max_width;
2708 /* clear background for values that may have changed its size */
2709 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710 max_width, max_height);
2713 font = (active ? font2 : font1);
2723 /* don't truncate output if "chars" is zero or less */
2726 /* dynamically correct text alignment */
2727 pos->width = size * getFontWidth(font);
2731 s_cut = getStringCopyN(s, size);
2733 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734 s_cut, font, mask_mode);
2740 redraw_mask |= REDRAW_DOOR_1;
2743 game_status = GAME_MODE_PLAYING;
2746 void UpdateAndDisplayGameControlValues()
2748 if (tape.warp_forward)
2751 UpdateGameControlValues();
2752 DisplayGameControlValues();
2755 void DrawGameValue_Emeralds(int value)
2757 struct TextPosInfo *pos = &game.panel.gems;
2759 int font_nr = pos->font;
2761 int font_nr = FONT_TEXT_2;
2763 int font_width = getFontWidth(font_nr);
2764 int chars = pos->size;
2767 return; /* !!! USE NEW STUFF !!! */
2770 if (PANEL_DEACTIVATED(pos))
2773 pos->width = chars * font_width;
2775 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2778 void DrawGameValue_Dynamite(int value)
2780 struct TextPosInfo *pos = &game.panel.inventory_count;
2782 int font_nr = pos->font;
2784 int font_nr = FONT_TEXT_2;
2786 int font_width = getFontWidth(font_nr);
2787 int chars = pos->size;
2790 return; /* !!! USE NEW STUFF !!! */
2793 if (PANEL_DEACTIVATED(pos))
2796 pos->width = chars * font_width;
2798 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 void DrawGameValue_Score(int value)
2803 struct TextPosInfo *pos = &game.panel.score;
2805 int font_nr = pos->font;
2807 int font_nr = FONT_TEXT_2;
2809 int font_width = getFontWidth(font_nr);
2810 int chars = pos->size;
2813 return; /* !!! USE NEW STUFF !!! */
2816 if (PANEL_DEACTIVATED(pos))
2819 pos->width = chars * font_width;
2821 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2824 void DrawGameValue_Time(int value)
2826 struct TextPosInfo *pos = &game.panel.time;
2827 static int last_value = -1;
2830 int chars = pos->size;
2832 int font1_nr = pos->font;
2833 int font2_nr = pos->font_alt;
2835 int font1_nr = FONT_TEXT_2;
2836 int font2_nr = FONT_TEXT_1;
2838 int font_nr = font1_nr;
2839 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2842 return; /* !!! USE NEW STUFF !!! */
2845 if (PANEL_DEACTIVATED(pos))
2848 if (use_dynamic_chars) /* use dynamic number of chars */
2850 chars = (value < 1000 ? chars1 : chars2);
2851 font_nr = (value < 1000 ? font1_nr : font2_nr);
2854 /* clear background if value just changed its size (dynamic chars only) */
2855 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2857 int width1 = chars1 * getFontWidth(font1_nr);
2858 int width2 = chars2 * getFontWidth(font2_nr);
2859 int max_width = MAX(width1, width2);
2860 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2862 pos->width = max_width;
2864 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865 max_width, max_height);
2868 pos->width = chars * getFontWidth(font_nr);
2870 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2875 void DrawGameValue_Level(int value)
2877 struct TextPosInfo *pos = &game.panel.level_number;
2880 int chars = pos->size;
2882 int font1_nr = pos->font;
2883 int font2_nr = pos->font_alt;
2885 int font1_nr = FONT_TEXT_2;
2886 int font2_nr = FONT_TEXT_1;
2888 int font_nr = font1_nr;
2889 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2892 return; /* !!! USE NEW STUFF !!! */
2895 if (PANEL_DEACTIVATED(pos))
2898 if (use_dynamic_chars) /* use dynamic number of chars */
2900 chars = (level_nr < 100 ? chars1 : chars2);
2901 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2904 pos->width = chars * getFontWidth(font_nr);
2906 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2912 struct TextPosInfo *pos = &game.panel.keys;
2915 int base_key_graphic = EL_KEY_1;
2920 return; /* !!! USE NEW STUFF !!! */
2924 if (PANEL_DEACTIVATED(pos))
2929 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930 base_key_graphic = EL_EM_KEY_1;
2934 pos->width = 4 * MINI_TILEX;
2938 for (i = 0; i < MAX_NUM_KEYS; i++)
2940 /* currently only 4 of 8 possible keys are displayed */
2941 for (i = 0; i < STD_NUM_KEYS; i++)
2945 struct TextPosInfo *pos = &game.panel.key[i];
2947 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948 int src_y = DOOR_GFX_PAGEY1 + 123;
2950 int dst_x = PANEL_XPOS(pos);
2951 int dst_y = PANEL_YPOS(pos);
2953 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954 int dst_y = PANEL_YPOS(pos);
2958 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2961 int graphic = el2edimg(element);
2965 if (PANEL_DEACTIVATED(pos))
2970 /* masked blit with tiles from half-size scaled bitmap does not work yet
2971 (no mask bitmap created for these sizes after loading and scaling) --
2972 solution: load without creating mask, scale, then create final mask */
2974 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2980 int graphic = el2edimg(base_key_graphic + i);
2985 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2987 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988 dst_x - src_x, dst_y - src_y);
2989 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2995 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2997 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3001 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3003 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3012 void DrawGameValue_Emeralds(int value)
3014 int font_nr = FONT_TEXT_2;
3015 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3017 if (PANEL_DEACTIVATED(game.panel.gems))
3020 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3023 void DrawGameValue_Dynamite(int value)
3025 int font_nr = FONT_TEXT_2;
3026 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3028 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3031 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3034 void DrawGameValue_Score(int value)
3036 int font_nr = FONT_TEXT_2;
3037 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3039 if (PANEL_DEACTIVATED(game.panel.score))
3042 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3045 void DrawGameValue_Time(int value)
3047 int font1_nr = FONT_TEXT_2;
3049 int font2_nr = FONT_TEXT_1;
3051 int font2_nr = FONT_LEVEL_NUMBER;
3053 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3056 if (PANEL_DEACTIVATED(game.panel.time))
3059 /* clear background if value just changed its size */
3060 if (value == 999 || value == 1000)
3061 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3064 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3066 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3069 void DrawGameValue_Level(int value)
3071 int font1_nr = FONT_TEXT_2;
3073 int font2_nr = FONT_TEXT_1;
3075 int font2_nr = FONT_LEVEL_NUMBER;
3078 if (PANEL_DEACTIVATED(game.panel.level))
3082 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3084 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3089 int base_key_graphic = EL_KEY_1;
3092 if (PANEL_DEACTIVATED(game.panel.keys))
3095 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096 base_key_graphic = EL_EM_KEY_1;
3098 /* currently only 4 of 8 possible keys are displayed */
3099 for (i = 0; i < STD_NUM_KEYS; i++)
3101 int x = XX_KEYS + i * MINI_TILEX;
3105 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3107 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3117 int key[MAX_NUM_KEYS];
3120 /* prevent EM engine from updating time/score values parallel to GameWon() */
3121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122 local_player->LevelSolved)
3125 for (i = 0; i < MAX_NUM_KEYS; i++)
3126 key[i] = key_bits & (1 << i);
3128 DrawGameValue_Level(level_nr);
3130 DrawGameValue_Emeralds(emeralds);
3131 DrawGameValue_Dynamite(dynamite);
3132 DrawGameValue_Score(score);
3133 DrawGameValue_Time(time);
3135 DrawGameValue_Keys(key);
3138 void UpdateGameDoorValues()
3140 UpdateGameControlValues();
3143 void DrawGameDoorValues()
3145 DisplayGameControlValues();
3148 void DrawGameDoorValues_OLD()
3150 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151 int dynamite_value = 0;
3152 int score_value = (local_player->LevelSolved ? local_player->score_final :
3153 local_player->score);
3154 int gems_value = local_player->gems_still_needed;
3158 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3160 DrawGameDoorValues_EM();
3165 if (game.centered_player_nr == -1)
3167 for (i = 0; i < MAX_PLAYERS; i++)
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 if (stored_player[i].key[j])
3171 key_bits |= (1 << j);
3173 dynamite_value += stored_player[i].inventory_size;
3178 int player_nr = game.centered_player_nr;
3180 for (i = 0; i < MAX_NUM_KEYS; i++)
3181 if (stored_player[player_nr].key[i])
3182 key_bits |= (1 << i);
3184 dynamite_value = stored_player[player_nr].inventory_size;
3187 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3193 =============================================================================
3195 -----------------------------------------------------------------------------
3196 initialize game engine due to level / tape version number
3197 =============================================================================
3200 static void InitGameEngine()
3202 int i, j, k, l, x, y;
3204 /* set game engine from tape file when re-playing, else from level file */
3205 game.engine_version = (tape.playing ? tape.engine_version :
3206 level.game_version);
3208 /* ---------------------------------------------------------------------- */
3209 /* set flags for bugs and changes according to active game engine version */
3210 /* ---------------------------------------------------------------------- */
3213 Summary of bugfix/change:
3214 Fixed handling for custom elements that change when pushed by the player.
3216 Fixed/changed in version:
3220 Before 3.1.0, custom elements that "change when pushing" changed directly
3221 after the player started pushing them (until then handled in "DigField()").
3222 Since 3.1.0, these custom elements are not changed until the "pushing"
3223 move of the element is finished (now handled in "ContinueMoving()").
3225 Affected levels/tapes:
3226 The first condition is generally needed for all levels/tapes before version
3227 3.1.0, which might use the old behaviour before it was changed; known tapes
3228 that are affected are some tapes from the level set "Walpurgis Gardens" by
3230 The second condition is an exception from the above case and is needed for
3231 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232 above (including some development versions of 3.1.0), but before it was
3233 known that this change would break tapes like the above and was fixed in
3234 3.1.1, so that the changed behaviour was active although the engine version
3235 while recording maybe was before 3.1.0. There is at least one tape that is
3236 affected by this exception, which is the tape for the one-level set "Bug
3237 Machine" by Juergen Bonhagen.
3240 game.use_change_when_pushing_bug =
3241 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3243 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244 tape.game_version < VERSION_IDENT(3,1,1,0)));
3247 Summary of bugfix/change:
3248 Fixed handling for blocking the field the player leaves when moving.
3250 Fixed/changed in version:
3254 Before 3.1.1, when "block last field when moving" was enabled, the field
3255 the player is leaving when moving was blocked for the time of the move,
3256 and was directly unblocked afterwards. This resulted in the last field
3257 being blocked for exactly one less than the number of frames of one player
3258 move. Additionally, even when blocking was disabled, the last field was
3259 blocked for exactly one frame.
3260 Since 3.1.1, due to changes in player movement handling, the last field
3261 is not blocked at all when blocking is disabled. When blocking is enabled,
3262 the last field is blocked for exactly the number of frames of one player
3263 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264 last field is blocked for exactly one more than the number of frames of
3267 Affected levels/tapes:
3268 (!!! yet to be determined -- probably many !!!)
3271 game.use_block_last_field_bug =
3272 (game.engine_version < VERSION_IDENT(3,1,1,0));
3275 Summary of bugfix/change:
3276 Changed behaviour of CE changes with multiple changes per single frame.
3278 Fixed/changed in version:
3282 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283 This resulted in race conditions where CEs seem to behave strange in some
3284 situations (where triggered CE changes were just skipped because there was
3285 already a CE change on that tile in the playfield in that engine frame).
3286 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287 (The number of changes per frame must be limited in any case, because else
3288 it is easily possible to define CE changes that would result in an infinite
3289 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290 should be set large enough so that it would only be reached in cases where
3291 the corresponding CE change conditions run into a loop. Therefore, it seems
3292 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293 maximal number of change pages for custom elements.)
3295 Affected levels/tapes:
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300 game.max_num_changes_per_frame = 1;
3302 game.max_num_changes_per_frame =
3303 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3306 /* ---------------------------------------------------------------------- */
3308 /* default scan direction: scan playfield from top/left to bottom/right */
3309 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3311 /* dynamically adjust element properties according to game engine version */
3312 InitElementPropertiesEngine(game.engine_version);
3315 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316 printf(" tape version == %06d [%s] [file: %06d]\n",
3317 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3319 printf(" => game.engine_version == %06d\n", game.engine_version);
3322 /* ---------- initialize player's initial move delay --------------------- */
3324 /* dynamically adjust player properties according to level information */
3325 for (i = 0; i < MAX_PLAYERS; i++)
3326 game.initial_move_delay_value[i] =
3327 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3329 /* dynamically adjust player properties according to game engine version */
3330 for (i = 0; i < MAX_PLAYERS; i++)
3331 game.initial_move_delay[i] =
3332 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333 game.initial_move_delay_value[i] : 0);
3335 /* ---------- initialize player's initial push delay --------------------- */
3337 /* dynamically adjust player properties according to game engine version */
3338 game.initial_push_delay_value =
3339 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3341 /* ---------- initialize changing elements ------------------------------- */
3343 /* initialize changing elements information */
3344 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346 struct ElementInfo *ei = &element_info[i];
3348 /* this pointer might have been changed in the level editor */
3349 ei->change = &ei->change_page[0];
3351 if (!IS_CUSTOM_ELEMENT(i))
3353 ei->change->target_element = EL_EMPTY_SPACE;
3354 ei->change->delay_fixed = 0;
3355 ei->change->delay_random = 0;
3356 ei->change->delay_frames = 1;
3359 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3361 ei->has_change_event[j] = FALSE;
3363 ei->event_page_nr[j] = 0;
3364 ei->event_page[j] = &ei->change_page[0];
3368 /* add changing elements from pre-defined list */
3369 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3371 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372 struct ElementInfo *ei = &element_info[ch_delay->element];
3374 ei->change->target_element = ch_delay->target_element;
3375 ei->change->delay_fixed = ch_delay->change_delay;
3377 ei->change->pre_change_function = ch_delay->pre_change_function;
3378 ei->change->change_function = ch_delay->change_function;
3379 ei->change->post_change_function = ch_delay->post_change_function;
3381 ei->change->can_change = TRUE;
3382 ei->change->can_change_or_has_action = TRUE;
3384 ei->has_change_event[CE_DELAY] = TRUE;
3386 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3390 /* ---------- initialize internal run-time variables --------------------- */
3392 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3394 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3396 for (j = 0; j < ei->num_change_pages; j++)
3398 ei->change_page[j].can_change_or_has_action =
3399 (ei->change_page[j].can_change |
3400 ei->change_page[j].has_action);
3404 /* add change events from custom element configuration */
3405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3407 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3409 for (j = 0; j < ei->num_change_pages; j++)
3411 if (!ei->change_page[j].can_change_or_has_action)
3414 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3416 /* only add event page for the first page found with this event */
3417 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3419 ei->has_change_event[k] = TRUE;
3421 ei->event_page_nr[k] = j;
3422 ei->event_page[k] = &ei->change_page[j];
3429 /* ---------- initialize reference elements in change conditions --------- */
3431 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3433 int element = EL_CUSTOM_START + i;
3434 struct ElementInfo *ei = &element_info[element];
3436 for (j = 0; j < ei->num_change_pages; j++)
3438 int trigger_element = ei->change_page[j].initial_trigger_element;
3440 if (trigger_element >= EL_PREV_CE_8 &&
3441 trigger_element <= EL_NEXT_CE_8)
3442 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3444 ei->change_page[j].trigger_element = trigger_element;
3449 /* ---------- initialize run-time trigger player and element ------------- */
3451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3453 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3455 for (j = 0; j < ei->num_change_pages; j++)
3457 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461 ei->change_page[j].actual_trigger_ce_value = 0;
3462 ei->change_page[j].actual_trigger_ce_score = 0;
3466 /* ---------- initialize trigger events ---------------------------------- */
3468 /* initialize trigger events information */
3469 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471 trigger_events[i][j] = FALSE;
3473 /* add trigger events from element change event properties */
3474 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3476 struct ElementInfo *ei = &element_info[i];
3478 for (j = 0; j < ei->num_change_pages; j++)
3480 if (!ei->change_page[j].can_change_or_has_action)
3483 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3485 int trigger_element = ei->change_page[j].trigger_element;
3487 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3489 if (ei->change_page[j].has_event[k])
3491 if (IS_GROUP_ELEMENT(trigger_element))
3493 struct ElementGroupInfo *group =
3494 element_info[trigger_element].group;
3496 for (l = 0; l < group->num_elements_resolved; l++)
3497 trigger_events[group->element_resolved[l]][k] = TRUE;
3499 else if (trigger_element == EL_ANY_ELEMENT)
3500 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501 trigger_events[l][k] = TRUE;
3503 trigger_events[trigger_element][k] = TRUE;
3510 /* ---------- initialize push delay -------------------------------------- */
3512 /* initialize push delay values to default */
3513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3515 if (!IS_CUSTOM_ELEMENT(i))
3517 /* set default push delay values (corrected since version 3.0.7-1) */
3518 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3520 element_info[i].push_delay_fixed = 2;
3521 element_info[i].push_delay_random = 8;
3525 element_info[i].push_delay_fixed = 8;
3526 element_info[i].push_delay_random = 8;
3531 /* set push delay value for certain elements from pre-defined list */
3532 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3534 int e = push_delay_list[i].element;
3536 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3537 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3540 /* set push delay value for Supaplex elements for newer engine versions */
3541 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3543 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3545 if (IS_SP_ELEMENT(i))
3547 /* set SP push delay to just enough to push under a falling zonk */
3548 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3550 element_info[i].push_delay_fixed = delay;
3551 element_info[i].push_delay_random = 0;
3556 /* ---------- initialize move stepsize ----------------------------------- */
3558 /* initialize move stepsize values to default */
3559 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560 if (!IS_CUSTOM_ELEMENT(i))
3561 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3563 /* set move stepsize value for certain elements from pre-defined list */
3564 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3566 int e = move_stepsize_list[i].element;
3568 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3571 /* ---------- initialize collect score ----------------------------------- */
3573 /* initialize collect score values for custom elements from initial value */
3574 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575 if (IS_CUSTOM_ELEMENT(i))
3576 element_info[i].collect_score = element_info[i].collect_score_initial;
3578 /* ---------- initialize collect count ----------------------------------- */
3580 /* initialize collect count values for non-custom elements */
3581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582 if (!IS_CUSTOM_ELEMENT(i))
3583 element_info[i].collect_count_initial = 0;
3585 /* add collect count values for all elements from pre-defined list */
3586 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587 element_info[collect_count_list[i].element].collect_count_initial =
3588 collect_count_list[i].count;
3590 /* ---------- initialize access direction -------------------------------- */
3592 /* initialize access direction values to default (access from every side) */
3593 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594 if (!IS_CUSTOM_ELEMENT(i))
3595 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3597 /* set access direction value for certain elements from pre-defined list */
3598 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599 element_info[access_direction_list[i].element].access_direction =
3600 access_direction_list[i].direction;
3602 /* ---------- initialize explosion content ------------------------------- */
3603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3605 if (IS_CUSTOM_ELEMENT(i))
3608 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3610 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3612 element_info[i].content.e[x][y] =
3613 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615 i == EL_PLAYER_3 ? EL_EMERALD :
3616 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617 i == EL_MOLE ? EL_EMERALD_RED :
3618 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623 i == EL_WALL_EMERALD ? EL_EMERALD :
3624 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629 i == EL_WALL_PEARL ? EL_PEARL :
3630 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3635 /* ---------- initialize recursion detection ------------------------------ */
3636 recursion_loop_depth = 0;
3637 recursion_loop_detected = FALSE;
3638 recursion_loop_element = EL_UNDEFINED;
3640 /* ---------- initialize graphics engine ---------------------------------- */
3641 game.scroll_delay_value =
3642 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643 setup.scroll_delay ? setup.scroll_delay_value : 0);
3644 game.scroll_delay_value =
3645 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3648 int get_num_special_action(int element, int action_first, int action_last)
3650 int num_special_action = 0;
3653 for (i = action_first; i <= action_last; i++)
3655 boolean found = FALSE;
3657 for (j = 0; j < NUM_DIRECTIONS; j++)
3658 if (el_act_dir2img(element, i, j) !=
3659 el_act_dir2img(element, ACTION_DEFAULT, j))
3663 num_special_action++;
3668 return num_special_action;
3673 =============================================================================
3675 -----------------------------------------------------------------------------
3676 initialize and start new game
3677 =============================================================================
3682 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3683 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3684 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3686 boolean do_fading = (game_status == GAME_MODE_MAIN);
3689 int initial_move_dir = MV_DOWN;
3691 int initial_move_dir = MV_NONE;
3695 game_status = GAME_MODE_PLAYING;
3698 InitGameControlValues();
3700 /* don't play tapes over network */
3701 network_playing = (options.network && !tape.playing);
3703 for (i = 0; i < MAX_PLAYERS; i++)
3705 struct PlayerInfo *player = &stored_player[i];
3707 player->index_nr = i;
3708 player->index_bit = (1 << i);
3709 player->element_nr = EL_PLAYER_1 + i;
3711 player->present = FALSE;
3712 player->active = FALSE;
3713 player->mapped = FALSE;
3715 player->killed = FALSE;
3716 player->reanimated = FALSE;
3719 player->effective_action = 0;
3720 player->programmed_action = 0;
3723 player->score_final = 0;
3725 player->gems_still_needed = level.gems_needed;
3726 player->sokobanfields_still_needed = 0;
3727 player->lights_still_needed = 0;
3728 player->friends_still_needed = 0;
3730 for (j = 0; j < MAX_NUM_KEYS; j++)
3731 player->key[j] = FALSE;
3733 player->num_white_keys = 0;
3735 player->dynabomb_count = 0;
3736 player->dynabomb_size = 1;
3737 player->dynabombs_left = 0;
3738 player->dynabomb_xl = FALSE;
3740 player->MovDir = initial_move_dir;
3743 player->GfxDir = initial_move_dir;
3744 player->GfxAction = ACTION_DEFAULT;
3746 player->StepFrame = 0;
3748 player->initial_element = player->element_nr;
3749 player->artwork_element =
3750 (level.use_artwork_element[i] ? level.artwork_element[i] :
3751 player->element_nr);
3752 player->use_murphy = FALSE;
3754 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3755 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3757 player->gravity = level.initial_player_gravity[i];
3759 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3761 player->actual_frame_counter = 0;
3763 player->step_counter = 0;
3765 player->last_move_dir = initial_move_dir;
3767 player->is_active = FALSE;
3769 player->is_waiting = FALSE;
3770 player->is_moving = FALSE;
3771 player->is_auto_moving = FALSE;
3772 player->is_digging = FALSE;
3773 player->is_snapping = FALSE;
3774 player->is_collecting = FALSE;
3775 player->is_pushing = FALSE;
3776 player->is_switching = FALSE;
3777 player->is_dropping = FALSE;
3778 player->is_dropping_pressed = FALSE;
3780 player->is_bored = FALSE;
3781 player->is_sleeping = FALSE;
3783 player->frame_counter_bored = -1;
3784 player->frame_counter_sleeping = -1;
3786 player->anim_delay_counter = 0;
3787 player->post_delay_counter = 0;
3789 player->dir_waiting = initial_move_dir;
3790 player->action_waiting = ACTION_DEFAULT;
3791 player->last_action_waiting = ACTION_DEFAULT;
3792 player->special_action_bored = ACTION_DEFAULT;
3793 player->special_action_sleeping = ACTION_DEFAULT;
3795 player->switch_x = -1;
3796 player->switch_y = -1;
3798 player->drop_x = -1;
3799 player->drop_y = -1;
3801 player->show_envelope = 0;
3803 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3805 player->push_delay = -1; /* initialized when pushing starts */
3806 player->push_delay_value = game.initial_push_delay_value;
3808 player->drop_delay = 0;
3809 player->drop_pressed_delay = 0;
3811 player->last_jx = -1;
3812 player->last_jy = -1;
3816 player->shield_normal_time_left = 0;
3817 player->shield_deadly_time_left = 0;
3819 player->inventory_infinite_element = EL_UNDEFINED;
3820 player->inventory_size = 0;
3822 if (level.use_initial_inventory[i])
3824 for (j = 0; j < level.initial_inventory_size[i]; j++)
3826 int element = level.initial_inventory_content[i][j];
3827 int collect_count = element_info[element].collect_count_initial;
3830 if (!IS_CUSTOM_ELEMENT(element))
3833 if (collect_count == 0)
3834 player->inventory_infinite_element = element;
3836 for (k = 0; k < collect_count; k++)
3837 if (player->inventory_size < MAX_INVENTORY_SIZE)
3838 player->inventory_element[player->inventory_size++] = element;
3842 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843 SnapField(player, 0, 0);
3845 player->LevelSolved = FALSE;
3846 player->GameOver = FALSE;
3848 player->LevelSolved_GameWon = FALSE;
3849 player->LevelSolved_GameEnd = FALSE;
3850 player->LevelSolved_PanelOff = FALSE;
3851 player->LevelSolved_SaveTape = FALSE;
3852 player->LevelSolved_SaveScore = FALSE;
3853 player->LevelSolved_CountingTime = 0;
3854 player->LevelSolved_CountingScore = 0;
3856 map_player_action[i] = i;
3859 network_player_action_received = FALSE;
3861 #if defined(NETWORK_AVALIABLE)
3862 /* initial null action */
3863 if (network_playing)
3864 SendToServer_MovePlayer(MV_NONE);
3873 TimeLeft = level.time;
3876 ScreenMovDir = MV_NONE;
3880 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3882 AllPlayersGone = FALSE;
3884 game.yamyam_content_nr = 0;
3885 game.robot_wheel_active = FALSE;
3886 game.magic_wall_active = FALSE;
3887 game.magic_wall_time_left = 0;
3888 game.light_time_left = 0;
3889 game.timegate_time_left = 0;
3890 game.switchgate_pos = 0;
3891 game.wind_direction = level.wind_direction_initial;
3893 #if !USE_PLAYER_GRAVITY
3894 game.gravity = FALSE;
3895 game.explosions_delayed = TRUE;
3898 game.lenses_time_left = 0;
3899 game.magnify_time_left = 0;
3901 game.ball_state = level.ball_state_initial;
3902 game.ball_content_nr = 0;
3904 game.envelope_active = FALSE;
3906 /* set focus to local player for network games, else to all players */
3907 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908 game.centered_player_nr_next = game.centered_player_nr;
3909 game.set_centered_player = FALSE;
3911 if (network_playing && tape.recording)
3913 /* store client dependent player focus when recording network games */
3914 tape.centered_player_nr_next = game.centered_player_nr_next;
3915 tape.set_centered_player = TRUE;
3918 for (i = 0; i < NUM_BELTS; i++)
3920 game.belt_dir[i] = MV_NONE;
3921 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3924 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3927 SCAN_PLAYFIELD(x, y)
3929 Feld[x][y] = level.field[x][y];
3930 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931 ChangeDelay[x][y] = 0;
3932 ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934 CustomValue[x][y] = 0; /* initialized in InitField() */
3936 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3938 WasJustMoving[x][y] = 0;
3939 WasJustFalling[x][y] = 0;
3940 CheckCollision[x][y] = 0;
3941 CheckImpact[x][y] = 0;
3943 Pushed[x][y] = FALSE;
3945 ChangeCount[x][y] = 0;
3946 ChangeEvent[x][y] = -1;
3948 ExplodePhase[x][y] = 0;
3949 ExplodeDelay[x][y] = 0;
3950 ExplodeField[x][y] = EX_TYPE_NONE;
3952 RunnerVisit[x][y] = 0;
3953 PlayerVisit[x][y] = 0;
3956 GfxRandom[x][y] = INIT_GFX_RANDOM();
3957 GfxElement[x][y] = EL_UNDEFINED;
3958 GfxAction[x][y] = ACTION_DEFAULT;
3959 GfxDir[x][y] = MV_NONE;
3960 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3963 SCAN_PLAYFIELD(x, y)
3965 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3967 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3969 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3972 InitField(x, y, TRUE);
3974 ResetGfxAnimation(x, y);
3979 for (i = 0; i < MAX_PLAYERS; i++)
3981 struct PlayerInfo *player = &stored_player[i];
3983 /* set number of special actions for bored and sleeping animation */
3984 player->num_special_action_bored =
3985 get_num_special_action(player->artwork_element,
3986 ACTION_BORING_1, ACTION_BORING_LAST);
3987 player->num_special_action_sleeping =
3988 get_num_special_action(player->artwork_element,
3989 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3992 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993 emulate_sb ? EMU_SOKOBAN :
3994 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3996 #if USE_NEW_ALL_SLIPPERY
3997 /* initialize type of slippery elements */
3998 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4000 if (!IS_CUSTOM_ELEMENT(i))
4002 /* default: elements slip down either to the left or right randomly */
4003 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4005 /* SP style elements prefer to slip down on the left side */
4006 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4009 /* BD style elements prefer to slip down on the left side */
4010 if (game.emulation == EMU_BOULDERDASH)
4011 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4016 /* initialize explosion and ignition delay */
4017 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4019 if (!IS_CUSTOM_ELEMENT(i))
4022 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025 int last_phase = (num_phase + 1) * delay;
4026 int half_phase = (num_phase / 2) * delay;
4028 element_info[i].explosion_delay = last_phase - 1;
4029 element_info[i].ignition_delay = half_phase;
4031 if (i == EL_BLACK_ORB)
4032 element_info[i].ignition_delay = 1;
4036 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4037 element_info[i].explosion_delay = 1;
4039 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4040 element_info[i].ignition_delay = 1;
4044 /* correct non-moving belts to start moving left */
4045 for (i = 0; i < NUM_BELTS; i++)
4046 if (game.belt_dir[i] == MV_NONE)
4047 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051 /* choose default local player */
4052 local_player = &stored_player[0];
4054 for (i = 0; i < MAX_PLAYERS; i++)
4055 stored_player[i].connected = FALSE;
4057 local_player->connected = TRUE;
4058 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4062 /* try to guess locally connected team mode players (needed for correct
4063 assignment of player figures from level to locally playing players) */
4065 for (i = 0; i < MAX_PLAYERS; i++)
4066 if (tape.player_participates[i])
4067 stored_player[i].connected = TRUE;
4069 else if (setup.team_mode && !options.network)
4071 /* try to guess locally connected team mode players (needed for correct
4072 assignment of player figures from level to locally playing players) */
4074 for (i = 0; i < MAX_PLAYERS; i++)
4075 if (setup.input[i].use_joystick ||
4076 setup.input[i].key.left != KSYM_UNDEFINED)
4077 stored_player[i].connected = TRUE;
4081 for (i = 0; i < MAX_PLAYERS; i++)
4082 printf("::: player %d: %s\n", i,
4083 (stored_player[i].connected ? "connected" : "not connected"));
4085 for (i = 0; i < MAX_PLAYERS; i++)
4086 printf("::: player %d: %s\n", i,
4087 (stored_player[i].present ? "present" : "not present"));
4090 /* check if any connected player was not found in playfield */
4091 for (i = 0; i < MAX_PLAYERS; i++)
4093 struct PlayerInfo *player = &stored_player[i];
4095 if (player->connected && !player->present)
4097 struct PlayerInfo *field_player = NULL;
4100 printf("::: looking for field player for player %d ...\n", i);
4103 /* assign first free player found that is present in the playfield */
4105 /* first try: look for unmapped playfield player that is not connected */
4106 if (field_player == NULL)
4107 for (j = 0; j < MAX_PLAYERS; j++)
4108 if (stored_player[j].present &&
4109 !stored_player[j].mapped &&
4110 !stored_player[j].connected)
4111 field_player = &stored_player[j];
4113 /* second try: look for *any* unmapped playfield player */
4114 if (field_player == NULL)
4115 for (j = 0; j < MAX_PLAYERS; j++)
4116 if (stored_player[j].present &&
4117 !stored_player[j].mapped)
4118 field_player = &stored_player[j];
4120 if (field_player != NULL)
4122 int jx = field_player->jx, jy = field_player->jy;
4125 printf("::: found player figure %d\n", field_player->index_nr);
4128 player->present = FALSE;
4129 player->active = FALSE;
4131 field_player->present = TRUE;
4132 field_player->active = TRUE;
4135 player->initial_element = field_player->initial_element;
4136 player->artwork_element = field_player->artwork_element;
4138 player->block_last_field = field_player->block_last_field;
4139 player->block_delay_adjustment = field_player->block_delay_adjustment;
4142 StorePlayer[jx][jy] = field_player->element_nr;
4144 field_player->jx = field_player->last_jx = jx;
4145 field_player->jy = field_player->last_jy = jy;
4147 if (local_player == player)
4148 local_player = field_player;
4150 map_player_action[field_player->index_nr] = i;
4152 field_player->mapped = TRUE;
4155 printf("::: map_player_action[%d] == %d\n",
4156 field_player->index_nr, i);
4161 if (player->connected && player->present)
4162 player->mapped = TRUE;
4167 /* check if any connected player was not found in playfield */
4168 for (i = 0; i < MAX_PLAYERS; i++)
4170 struct PlayerInfo *player = &stored_player[i];
4172 if (player->connected && !player->present)
4174 for (j = 0; j < MAX_PLAYERS; j++)
4176 struct PlayerInfo *field_player = &stored_player[j];
4177 int jx = field_player->jx, jy = field_player->jy;
4179 /* assign first free player found that is present in the playfield */
4180 if (field_player->present && !field_player->connected)
4182 player->present = TRUE;
4183 player->active = TRUE;
4185 field_player->present = FALSE;
4186 field_player->active = FALSE;
4188 player->initial_element = field_player->initial_element;
4189 player->artwork_element = field_player->artwork_element;
4191 player->block_last_field = field_player->block_last_field;
4192 player->block_delay_adjustment = field_player->block_delay_adjustment;
4194 StorePlayer[jx][jy] = player->element_nr;
4196 player->jx = player->last_jx = jx;
4197 player->jy = player->last_jy = jy;
4207 printf("::: local_player->present == %d\n", local_player->present);
4212 /* when playing a tape, eliminate all players who do not participate */
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215 for (i = 0; i < MAX_PLAYERS; i++)
4217 if (stored_player[i].active &&
4218 !tape.player_participates[map_player_action[i]])
4220 struct PlayerInfo *player = &stored_player[i];
4221 int jx = player->jx, jy = player->jy;
4223 player->active = FALSE;
4224 StorePlayer[jx][jy] = 0;
4225 Feld[jx][jy] = EL_EMPTY;
4229 for (i = 0; i < MAX_PLAYERS; i++)
4231 if (stored_player[i].active &&
4232 !tape.player_participates[i])
4234 struct PlayerInfo *player = &stored_player[i];
4235 int jx = player->jx, jy = player->jy;
4237 player->active = FALSE;
4238 StorePlayer[jx][jy] = 0;
4239 Feld[jx][jy] = EL_EMPTY;
4244 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4246 /* when in single player mode, eliminate all but the first active player */
4248 for (i = 0; i < MAX_PLAYERS; i++)
4250 if (stored_player[i].active)
4252 for (j = i + 1; j < MAX_PLAYERS; j++)
4254 if (stored_player[j].active)
4256 struct PlayerInfo *player = &stored_player[j];
4257 int jx = player->jx, jy = player->jy;
4259 player->active = FALSE;
4260 player->present = FALSE;
4262 StorePlayer[jx][jy] = 0;
4263 Feld[jx][jy] = EL_EMPTY;
4270 /* when recording the game, store which players take part in the game */
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274 for (i = 0; i < MAX_PLAYERS; i++)
4275 if (stored_player[i].connected)
4276 tape.player_participates[i] = TRUE;
4278 for (i = 0; i < MAX_PLAYERS; i++)
4279 if (stored_player[i].active)
4280 tape.player_participates[i] = TRUE;
4286 for (i = 0; i < MAX_PLAYERS; i++)
4288 struct PlayerInfo *player = &stored_player[i];
4290 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4295 if (local_player == player)
4296 printf("Player %d is local player.\n", i+1);
4300 if (BorderElement == EL_EMPTY)
4303 SBX_Right = lev_fieldx - SCR_FIELDX;
4305 SBY_Lower = lev_fieldy - SCR_FIELDY;
4310 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4312 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4315 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4318 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4321 /* if local player not found, look for custom element that might create
4322 the player (make some assumptions about the right custom element) */
4323 if (!local_player->present)
4325 int start_x = 0, start_y = 0;
4326 int found_rating = 0;
4327 int found_element = EL_UNDEFINED;
4328 int player_nr = local_player->index_nr;
4330 SCAN_PLAYFIELD(x, y)
4332 int element = Feld[x][y];
4337 if (level.use_start_element[player_nr] &&
4338 level.start_element[player_nr] == element &&
4345 found_element = element;
4348 if (!IS_CUSTOM_ELEMENT(element))
4351 if (CAN_CHANGE(element))
4353 for (i = 0; i < element_info[element].num_change_pages; i++)
4355 /* check for player created from custom element as single target */
4356 content = element_info[element].change_page[i].target_element;
4357 is_player = ELEM_IS_PLAYER(content);
4359 if (is_player && (found_rating < 3 ||
4360 (found_rating == 3 && element < found_element)))
4366 found_element = element;
4371 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4373 /* check for player created from custom element as explosion content */
4374 content = element_info[element].content.e[xx][yy];
4375 is_player = ELEM_IS_PLAYER(content);
4377 if (is_player && (found_rating < 2 ||
4378 (found_rating == 2 && element < found_element)))
4380 start_x = x + xx - 1;
4381 start_y = y + yy - 1;
4384 found_element = element;
4387 if (!CAN_CHANGE(element))
4390 for (i = 0; i < element_info[element].num_change_pages; i++)
4392 /* check for player created from custom element as extended target */
4394 element_info[element].change_page[i].target_content.e[xx][yy];
4396 is_player = ELEM_IS_PLAYER(content);
4398 if (is_player && (found_rating < 1 ||
4399 (found_rating == 1 && element < found_element)))
4401 start_x = x + xx - 1;
4402 start_y = y + yy - 1;
4405 found_element = element;
4411 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4412 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4415 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4421 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4422 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423 local_player->jx - MIDPOSX);
4425 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427 local_player->jy - MIDPOSY);
4431 /* do not use PLAYING mask for fading out from main screen */
4432 game_status = GAME_MODE_MAIN;
4437 if (!game.restart_level)
4438 CloseDoor(DOOR_CLOSE_1);
4441 if (level_editor_test_game)
4442 FadeSkipNextFadeIn();
4444 FadeSetEnterScreen();
4446 if (level_editor_test_game)
4447 fading = fading_none;
4449 fading = menu.destination;
4453 FadeOut(REDRAW_FIELD);
4456 FadeOut(REDRAW_FIELD);
4460 game_status = GAME_MODE_PLAYING;
4463 /* !!! FIX THIS (START) !!! */
4464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4466 InitGameEngine_EM();
4468 /* blit playfield from scroll buffer to normal back buffer for fading in */
4469 BlitScreenToBitmap_EM(backbuffer);
4471 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4473 InitGameEngine_SP();
4475 /* blit playfield from scroll buffer to normal back buffer for fading in */
4476 BlitScreenToBitmap_SP(backbuffer);
4483 /* after drawing the level, correct some elements */
4484 if (game.timegate_time_left == 0)
4485 CloseAllOpenTimegates();
4487 /* blit playfield from scroll buffer to normal back buffer for fading in */
4488 if (setup.soft_scrolling)
4489 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4491 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4493 /* !!! FIX THIS (END) !!! */
4496 FadeIn(REDRAW_FIELD);
4499 FadeIn(REDRAW_FIELD);
4504 if (!game.restart_level)
4506 /* copy default game door content to main double buffer */
4507 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4508 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4511 SetPanelBackground();
4512 SetDrawBackgroundMask(REDRAW_DOOR_1);
4515 UpdateAndDisplayGameControlValues();
4517 UpdateGameDoorValues();
4518 DrawGameDoorValues();
4521 if (!game.restart_level)
4525 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4526 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4527 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4531 /* copy actual game door content to door double buffer for OpenDoor() */
4532 BlitBitmap(drawto, bitmap_db_door,
4533 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4535 OpenDoor(DOOR_OPEN_ALL);
4537 PlaySound(SND_GAME_STARTING);
4539 if (setup.sound_music)
4542 KeyboardAutoRepeatOffUnlessAutoplay();
4546 for (i = 0; i < MAX_PLAYERS; i++)
4547 printf("Player %d %sactive.\n",
4548 i + 1, (stored_player[i].active ? "" : "not "));
4559 game.restart_level = FALSE;
4562 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4564 /* this is used for non-R'n'D game engines to update certain engine values */
4566 /* needed to determine if sounds are played within the visible screen area */
4567 scroll_x = actual_scroll_x;
4568 scroll_y = actual_scroll_y;
4571 void InitMovDir(int x, int y)
4573 int i, element = Feld[x][y];
4574 static int xy[4][2] =
4581 static int direction[3][4] =
4583 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4584 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4585 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4594 Feld[x][y] = EL_BUG;
4595 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4598 case EL_SPACESHIP_RIGHT:
4599 case EL_SPACESHIP_UP:
4600 case EL_SPACESHIP_LEFT:
4601 case EL_SPACESHIP_DOWN:
4602 Feld[x][y] = EL_SPACESHIP;
4603 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4606 case EL_BD_BUTTERFLY_RIGHT:
4607 case EL_BD_BUTTERFLY_UP:
4608 case EL_BD_BUTTERFLY_LEFT:
4609 case EL_BD_BUTTERFLY_DOWN:
4610 Feld[x][y] = EL_BD_BUTTERFLY;
4611 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4614 case EL_BD_FIREFLY_RIGHT:
4615 case EL_BD_FIREFLY_UP:
4616 case EL_BD_FIREFLY_LEFT:
4617 case EL_BD_FIREFLY_DOWN:
4618 Feld[x][y] = EL_BD_FIREFLY;
4619 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4622 case EL_PACMAN_RIGHT:
4624 case EL_PACMAN_LEFT:
4625 case EL_PACMAN_DOWN:
4626 Feld[x][y] = EL_PACMAN;
4627 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4630 case EL_YAMYAM_LEFT:
4631 case EL_YAMYAM_RIGHT:
4633 case EL_YAMYAM_DOWN:
4634 Feld[x][y] = EL_YAMYAM;
4635 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4638 case EL_SP_SNIKSNAK:
4639 MovDir[x][y] = MV_UP;
4642 case EL_SP_ELECTRON:
4643 MovDir[x][y] = MV_LEFT;
4650 Feld[x][y] = EL_MOLE;
4651 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4655 if (IS_CUSTOM_ELEMENT(element))
4657 struct ElementInfo *ei = &element_info[element];
4658 int move_direction_initial = ei->move_direction_initial;
4659 int move_pattern = ei->move_pattern;
4661 if (move_direction_initial == MV_START_PREVIOUS)
4663 if (MovDir[x][y] != MV_NONE)
4666 move_direction_initial = MV_START_AUTOMATIC;
4669 if (move_direction_initial == MV_START_RANDOM)
4670 MovDir[x][y] = 1 << RND(4);
4671 else if (move_direction_initial & MV_ANY_DIRECTION)
4672 MovDir[x][y] = move_direction_initial;
4673 else if (move_pattern == MV_ALL_DIRECTIONS ||
4674 move_pattern == MV_TURNING_LEFT ||
4675 move_pattern == MV_TURNING_RIGHT ||
4676 move_pattern == MV_TURNING_LEFT_RIGHT ||
4677 move_pattern == MV_TURNING_RIGHT_LEFT ||
4678 move_pattern == MV_TURNING_RANDOM)
4679 MovDir[x][y] = 1 << RND(4);
4680 else if (move_pattern == MV_HORIZONTAL)
4681 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4682 else if (move_pattern == MV_VERTICAL)
4683 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4684 else if (move_pattern & MV_ANY_DIRECTION)
4685 MovDir[x][y] = element_info[element].move_pattern;
4686 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4687 move_pattern == MV_ALONG_RIGHT_SIDE)
4689 /* use random direction as default start direction */
4690 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4691 MovDir[x][y] = 1 << RND(4);
4693 for (i = 0; i < NUM_DIRECTIONS; i++)
4695 int x1 = x + xy[i][0];
4696 int y1 = y + xy[i][1];
4698 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4700 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4701 MovDir[x][y] = direction[0][i];
4703 MovDir[x][y] = direction[1][i];
4712 MovDir[x][y] = 1 << RND(4);
4714 if (element != EL_BUG &&
4715 element != EL_SPACESHIP &&
4716 element != EL_BD_BUTTERFLY &&
4717 element != EL_BD_FIREFLY)
4720 for (i = 0; i < NUM_DIRECTIONS; i++)
4722 int x1 = x + xy[i][0];
4723 int y1 = y + xy[i][1];
4725 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4727 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4729 MovDir[x][y] = direction[0][i];
4732 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4735 MovDir[x][y] = direction[1][i];
4744 GfxDir[x][y] = MovDir[x][y];
4747 void InitAmoebaNr(int x, int y)
4750 int group_nr = AmoebeNachbarNr(x, y);
4754 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4756 if (AmoebaCnt[i] == 0)
4764 AmoebaNr[x][y] = group_nr;
4765 AmoebaCnt[group_nr]++;
4766 AmoebaCnt2[group_nr]++;
4769 static void PlayerWins(struct PlayerInfo *player)
4771 player->LevelSolved = TRUE;
4772 player->GameOver = TRUE;
4774 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4775 level.native_em_level->lev->score : player->score);
4777 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4778 player->LevelSolved_CountingScore = player->score_final;
4783 static int time, time_final;
4784 static int score, score_final;
4785 static int game_over_delay_1 = 0;
4786 static int game_over_delay_2 = 0;
4787 int game_over_delay_value_1 = 50;
4788 int game_over_delay_value_2 = 50;
4790 if (!local_player->LevelSolved_GameWon)
4794 /* do not start end game actions before the player stops moving (to exit) */
4795 if (local_player->MovPos)
4798 local_player->LevelSolved_GameWon = TRUE;
4799 local_player->LevelSolved_SaveTape = tape.recording;
4800 local_player->LevelSolved_SaveScore = !tape.playing;
4802 if (tape.auto_play) /* tape might already be stopped here */
4803 tape.auto_play_level_solved = TRUE;
4809 game_over_delay_1 = game_over_delay_value_1;
4810 game_over_delay_2 = game_over_delay_value_2;
4812 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4813 score = score_final = local_player->score_final;
4818 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4820 else if (level.time == 0 && TimePlayed < 999)
4823 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4826 local_player->score_final = score_final;
4828 if (level_editor_test_game)
4831 score = score_final;
4834 local_player->LevelSolved_CountingTime = time;
4835 local_player->LevelSolved_CountingScore = score;
4837 game_panel_controls[GAME_PANEL_TIME].value = time;
4838 game_panel_controls[GAME_PANEL_SCORE].value = score;
4840 DisplayGameControlValues();
4842 DrawGameValue_Time(time);
4843 DrawGameValue_Score(score);
4847 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4849 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4851 /* close exit door after last player */
4852 if ((AllPlayersGone &&
4853 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4854 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4855 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4856 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4857 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4859 int element = Feld[ExitX][ExitY];
4862 if (element == EL_EM_EXIT_OPEN ||
4863 element == EL_EM_STEEL_EXIT_OPEN)
4870 Feld[ExitX][ExitY] =
4871 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4872 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4873 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4874 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4875 EL_EM_STEEL_EXIT_CLOSING);
4877 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4881 /* player disappears */
4882 DrawLevelField(ExitX, ExitY);
4885 for (i = 0; i < MAX_PLAYERS; i++)
4887 struct PlayerInfo *player = &stored_player[i];
4889 if (player->present)
4891 RemovePlayer(player);
4893 /* player disappears */
4894 DrawLevelField(player->jx, player->jy);
4899 PlaySound(SND_GAME_WINNING);
4902 if (game_over_delay_1 > 0)
4904 game_over_delay_1--;
4909 if (time != time_final)
4911 int time_to_go = ABS(time_final - time);
4912 int time_count_dir = (time < time_final ? +1 : -1);
4913 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4915 time += time_count_steps * time_count_dir;
4916 score += time_count_steps * level.score[SC_TIME_BONUS];
4919 local_player->LevelSolved_CountingTime = time;
4920 local_player->LevelSolved_CountingScore = score;
4922 game_panel_controls[GAME_PANEL_TIME].value = time;
4923 game_panel_controls[GAME_PANEL_SCORE].value = score;
4925 DisplayGameControlValues();
4927 DrawGameValue_Time(time);
4928 DrawGameValue_Score(score);
4931 if (time == time_final)
4932 StopSound(SND_GAME_LEVELTIME_BONUS);
4933 else if (setup.sound_loops)
4934 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4936 PlaySound(SND_GAME_LEVELTIME_BONUS);
4941 local_player->LevelSolved_PanelOff = TRUE;
4943 if (game_over_delay_2 > 0)
4945 game_over_delay_2--;
4958 boolean raise_level = FALSE;
4960 local_player->LevelSolved_GameEnd = TRUE;
4962 CloseDoor(DOOR_CLOSE_1);
4964 if (local_player->LevelSolved_SaveTape)
4971 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4973 SaveTape(tape.level_nr); /* ask to save tape */
4977 if (level_editor_test_game)
4979 game_status = GAME_MODE_MAIN;
4982 DrawAndFadeInMainMenu(REDRAW_FIELD);
4990 if (!local_player->LevelSolved_SaveScore)
4993 FadeOut(REDRAW_FIELD);
4996 game_status = GAME_MODE_MAIN;
4998 DrawAndFadeInMainMenu(REDRAW_FIELD);
5003 if (level_nr == leveldir_current->handicap_level)
5005 leveldir_current->handicap_level++;
5006 SaveLevelSetup_SeriesInfo();
5009 if (level_nr < leveldir_current->last_level)
5010 raise_level = TRUE; /* advance to next level */
5012 if ((hi_pos = NewHiScore()) >= 0)
5014 game_status = GAME_MODE_SCORES;
5016 DrawHallOfFame(hi_pos);
5027 FadeOut(REDRAW_FIELD);
5030 game_status = GAME_MODE_MAIN;
5038 DrawAndFadeInMainMenu(REDRAW_FIELD);
5047 LoadScore(level_nr);
5049 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5053 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5055 if (local_player->score_final > highscore[k].Score)
5057 /* player has made it to the hall of fame */
5059 if (k < MAX_SCORE_ENTRIES - 1)
5061 int m = MAX_SCORE_ENTRIES - 1;
5064 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5065 if (strEqual(setup.player_name, highscore[l].Name))
5067 if (m == k) /* player's new highscore overwrites his old one */
5071 for (l = m; l > k; l--)
5073 strcpy(highscore[l].Name, highscore[l - 1].Name);
5074 highscore[l].Score = highscore[l - 1].Score;
5081 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5082 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5083 highscore[k].Score = local_player->score_final;
5089 else if (!strncmp(setup.player_name, highscore[k].Name,
5090 MAX_PLAYER_NAME_LEN))
5091 break; /* player already there with a higher score */
5097 SaveScore(level_nr);
5102 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5104 int element = Feld[x][y];
5105 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5106 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5107 int horiz_move = (dx != 0);
5108 int sign = (horiz_move ? dx : dy);
5109 int step = sign * element_info[element].move_stepsize;
5111 /* special values for move stepsize for spring and things on conveyor belt */
5114 if (CAN_FALL(element) &&
5115 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5116 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5117 else if (element == EL_SPRING)
5118 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5124 inline static int getElementMoveStepsize(int x, int y)
5126 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5129 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5131 if (player->GfxAction != action || player->GfxDir != dir)
5134 printf("Player frame reset! (%d => %d, %d => %d)\n",
5135 player->GfxAction, action, player->GfxDir, dir);
5138 player->GfxAction = action;
5139 player->GfxDir = dir;
5141 player->StepFrame = 0;
5145 #if USE_GFX_RESET_GFX_ANIMATION
5146 static void ResetGfxFrame(int x, int y, boolean redraw)
5148 int element = Feld[x][y];
5149 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5150 int last_gfx_frame = GfxFrame[x][y];
5152 if (graphic_info[graphic].anim_global_sync)
5153 GfxFrame[x][y] = FrameCounter;
5154 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5155 GfxFrame[x][y] = CustomValue[x][y];
5156 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5157 GfxFrame[x][y] = element_info[element].collect_score;
5158 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5159 GfxFrame[x][y] = ChangeDelay[x][y];
5161 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5162 DrawLevelGraphicAnimation(x, y, graphic);
5166 static void ResetGfxAnimation(int x, int y)
5168 GfxAction[x][y] = ACTION_DEFAULT;
5169 GfxDir[x][y] = MovDir[x][y];
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173 ResetGfxFrame(x, y, FALSE);
5177 static void ResetRandomAnimationValue(int x, int y)
5179 GfxRandom[x][y] = INIT_GFX_RANDOM();
5182 void InitMovingField(int x, int y, int direction)
5184 int element = Feld[x][y];
5185 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5189 boolean is_moving_before, is_moving_after;
5191 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5194 /* check if element was/is moving or being moved before/after mode change */
5197 is_moving_before = (WasJustMoving[x][y] != 0);
5199 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5200 is_moving_before = WasJustMoving[x][y];
5203 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5205 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5207 /* reset animation only for moving elements which change direction of moving
5208 or which just started or stopped moving
5209 (else CEs with property "can move" / "not moving" are reset each frame) */
5210 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5212 if (is_moving_before != is_moving_after ||
5213 direction != MovDir[x][y])
5214 ResetGfxAnimation(x, y);
5216 if ((is_moving_before || is_moving_after) && !continues_moving)
5217 ResetGfxAnimation(x, y);
5220 if (!continues_moving)
5221 ResetGfxAnimation(x, y);
5224 MovDir[x][y] = direction;
5225 GfxDir[x][y] = direction;
5227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5228 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5229 direction == MV_DOWN && CAN_FALL(element) ?
5230 ACTION_FALLING : ACTION_MOVING);
5232 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5233 ACTION_FALLING : ACTION_MOVING);
5236 /* this is needed for CEs with property "can move" / "not moving" */
5238 if (is_moving_after)
5240 if (Feld[newx][newy] == EL_EMPTY)
5241 Feld[newx][newy] = EL_BLOCKED;
5243 MovDir[newx][newy] = MovDir[x][y];
5245 #if USE_NEW_CUSTOM_VALUE
5246 CustomValue[newx][newy] = CustomValue[x][y];
5249 GfxFrame[newx][newy] = GfxFrame[x][y];
5250 GfxRandom[newx][newy] = GfxRandom[x][y];
5251 GfxAction[newx][newy] = GfxAction[x][y];
5252 GfxDir[newx][newy] = GfxDir[x][y];
5256 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5258 int direction = MovDir[x][y];
5259 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5260 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5266 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5268 int oldx = x, oldy = y;
5269 int direction = MovDir[x][y];
5271 if (direction == MV_LEFT)
5273 else if (direction == MV_RIGHT)
5275 else if (direction == MV_UP)
5277 else if (direction == MV_DOWN)
5280 *comes_from_x = oldx;
5281 *comes_from_y = oldy;
5284 int MovingOrBlocked2Element(int x, int y)
5286 int element = Feld[x][y];
5288 if (element == EL_BLOCKED)
5292 Blocked2Moving(x, y, &oldx, &oldy);
5293 return Feld[oldx][oldy];
5299 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5301 /* like MovingOrBlocked2Element(), but if element is moving
5302 and (x,y) is the field the moving element is just leaving,
5303 return EL_BLOCKED instead of the element value */
5304 int element = Feld[x][y];
5306 if (IS_MOVING(x, y))
5308 if (element == EL_BLOCKED)
5312 Blocked2Moving(x, y, &oldx, &oldy);
5313 return Feld[oldx][oldy];
5322 static void RemoveField(int x, int y)
5324 Feld[x][y] = EL_EMPTY;
5330 #if USE_NEW_CUSTOM_VALUE
5331 CustomValue[x][y] = 0;
5335 ChangeDelay[x][y] = 0;
5336 ChangePage[x][y] = -1;
5337 Pushed[x][y] = FALSE;
5340 ExplodeField[x][y] = EX_TYPE_NONE;
5343 GfxElement[x][y] = EL_UNDEFINED;
5344 GfxAction[x][y] = ACTION_DEFAULT;
5345 GfxDir[x][y] = MV_NONE;
5347 /* !!! this would prevent the removed tile from being redrawn !!! */
5348 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5352 void RemoveMovingField(int x, int y)
5354 int oldx = x, oldy = y, newx = x, newy = y;
5355 int element = Feld[x][y];
5356 int next_element = EL_UNDEFINED;
5358 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5361 if (IS_MOVING(x, y))
5363 Moving2Blocked(x, y, &newx, &newy);
5365 if (Feld[newx][newy] != EL_BLOCKED)
5367 /* element is moving, but target field is not free (blocked), but
5368 already occupied by something different (example: acid pool);
5369 in this case, only remove the moving field, but not the target */
5371 RemoveField(oldx, oldy);
5373 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5375 TEST_DrawLevelField(oldx, oldy);
5380 else if (element == EL_BLOCKED)
5382 Blocked2Moving(x, y, &oldx, &oldy);
5383 if (!IS_MOVING(oldx, oldy))
5387 if (element == EL_BLOCKED &&
5388 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5389 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5390 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5391 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5392 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5393 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5394 next_element = get_next_element(Feld[oldx][oldy]);
5396 RemoveField(oldx, oldy);
5397 RemoveField(newx, newy);
5399 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5401 if (next_element != EL_UNDEFINED)
5402 Feld[oldx][oldy] = next_element;
5404 TEST_DrawLevelField(oldx, oldy);
5405 TEST_DrawLevelField(newx, newy);
5408 void DrawDynamite(int x, int y)
5410 int sx = SCREENX(x), sy = SCREENY(y);
5411 int graphic = el2img(Feld[x][y]);
5414 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5417 if (IS_WALKABLE_INSIDE(Back[x][y]))
5421 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5422 else if (Store[x][y])
5423 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5425 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5427 if (Back[x][y] || Store[x][y])
5428 DrawGraphicThruMask(sx, sy, graphic, frame);
5430 DrawGraphic(sx, sy, graphic, frame);
5433 void CheckDynamite(int x, int y)
5435 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5439 if (MovDelay[x][y] != 0)
5442 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5448 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5453 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5455 boolean num_checked_players = 0;
5458 for (i = 0; i < MAX_PLAYERS; i++)
5460 if (stored_player[i].active)
5462 int sx = stored_player[i].jx;
5463 int sy = stored_player[i].jy;
5465 if (num_checked_players == 0)
5472 *sx1 = MIN(*sx1, sx);
5473 *sy1 = MIN(*sy1, sy);
5474 *sx2 = MAX(*sx2, sx);
5475 *sy2 = MAX(*sy2, sy);
5478 num_checked_players++;
5483 static boolean checkIfAllPlayersFitToScreen_RND()
5485 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5487 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5489 return (sx2 - sx1 < SCR_FIELDX &&
5490 sy2 - sy1 < SCR_FIELDY);
5493 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5495 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5497 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5499 *sx = (sx1 + sx2) / 2;
5500 *sy = (sy1 + sy2) / 2;
5503 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5504 boolean center_screen, boolean quick_relocation)
5506 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5507 boolean no_delay = (tape.warp_forward);
5508 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5509 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5511 if (quick_relocation)
5513 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5515 if (!level.shifted_relocation || center_screen)
5517 /* quick relocation (without scrolling), with centering of screen */
5519 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5520 x > SBX_Right + MIDPOSX ? SBX_Right :
5523 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5524 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5529 /* quick relocation (without scrolling), but do not center screen */
5531 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5532 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5535 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5536 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5539 int offset_x = x + (scroll_x - center_scroll_x);
5540 int offset_y = y + (scroll_y - center_scroll_y);
5542 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5543 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5544 offset_x - MIDPOSX);
5546 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5547 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5548 offset_y - MIDPOSY);
5554 if (!level.shifted_relocation || center_screen)
5556 /* quick relocation (without scrolling), with centering of screen */
5558 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5559 x > SBX_Right + MIDPOSX ? SBX_Right :
5562 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5568 /* quick relocation (without scrolling), but do not center screen */
5570 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5571 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5574 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5575 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5578 int offset_x = x + (scroll_x - center_scroll_x);
5579 int offset_y = y + (scroll_y - center_scroll_y);
5581 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5582 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5583 offset_x - MIDPOSX);
5585 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5586 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587 offset_y - MIDPOSY);
5590 /* quick relocation (without scrolling), inside visible screen area */
5592 int offset = game.scroll_delay_value;
5594 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5595 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5596 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5598 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5599 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5600 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5602 /* don't scroll over playfield boundaries */
5603 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5604 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5606 /* don't scroll over playfield boundaries */
5607 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5608 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5612 RedrawPlayfield(TRUE, 0,0,0,0);
5617 int scroll_xx, scroll_yy;
5619 if (!level.shifted_relocation || center_screen)
5621 /* visible relocation (with scrolling), with centering of screen */
5623 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5624 x > SBX_Right + MIDPOSX ? SBX_Right :
5627 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5628 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5633 /* visible relocation (with scrolling), but do not center screen */
5635 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5636 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5639 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5640 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643 int offset_x = x + (scroll_x - center_scroll_x);
5644 int offset_y = y + (scroll_y - center_scroll_y);
5646 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5647 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5648 offset_x - MIDPOSX);
5650 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5651 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5652 offset_y - MIDPOSY);
5657 /* visible relocation (with scrolling), with centering of screen */
5659 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5660 x > SBX_Right + MIDPOSX ? SBX_Right :
5663 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5668 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5670 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5673 int fx = FX, fy = FY;
5675 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5676 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5678 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5684 fx += dx * TILEX / 2;
5685 fy += dy * TILEY / 2;
5687 ScrollLevel(dx, dy);
5690 /* scroll in two steps of half tile size to make things smoother */
5691 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5693 Delay(wait_delay_value);
5695 /* scroll second step to align at full tile size */
5697 Delay(wait_delay_value);
5702 Delay(wait_delay_value);
5706 void RelocatePlayer(int jx, int jy, int el_player_raw)
5708 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5709 int player_nr = GET_PLAYER_NR(el_player);
5710 struct PlayerInfo *player = &stored_player[player_nr];
5711 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5712 boolean no_delay = (tape.warp_forward);
5713 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5714 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5715 int old_jx = player->jx;
5716 int old_jy = player->jy;
5717 int old_element = Feld[old_jx][old_jy];
5718 int element = Feld[jx][jy];
5719 boolean player_relocated = (old_jx != jx || old_jy != jy);
5721 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5722 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5723 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5724 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5725 int leave_side_horiz = move_dir_horiz;
5726 int leave_side_vert = move_dir_vert;
5727 int enter_side = enter_side_horiz | enter_side_vert;
5728 int leave_side = leave_side_horiz | leave_side_vert;
5730 if (player->GameOver) /* do not reanimate dead player */
5733 if (!player_relocated) /* no need to relocate the player */
5736 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5738 RemoveField(jx, jy); /* temporarily remove newly placed player */
5739 DrawLevelField(jx, jy);
5742 if (player->present)
5744 while (player->MovPos)
5746 ScrollPlayer(player, SCROLL_GO_ON);
5747 ScrollScreen(NULL, SCROLL_GO_ON);
5749 AdvanceFrameAndPlayerCounters(player->index_nr);
5754 Delay(wait_delay_value);
5757 DrawPlayer(player); /* needed here only to cleanup last field */
5758 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5760 player->is_moving = FALSE;
5763 if (IS_CUSTOM_ELEMENT(old_element))
5764 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5766 player->index_bit, leave_side);
5768 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5770 player->index_bit, leave_side);
5772 Feld[jx][jy] = el_player;
5773 InitPlayerField(jx, jy, el_player, TRUE);
5775 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5777 Feld[jx][jy] = element;
5778 InitField(jx, jy, FALSE);
5781 /* only visually relocate centered player */
5782 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5783 FALSE, level.instant_relocation);
5785 TestIfPlayerTouchesBadThing(jx, jy);
5786 TestIfPlayerTouchesCustomElement(jx, jy);
5788 if (IS_CUSTOM_ELEMENT(element))
5789 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5790 player->index_bit, enter_side);
5792 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5793 player->index_bit, enter_side);
5796 if (player->is_switching)
5798 /* ensure that relocation while still switching an element does not cause
5799 a new element to be treated as also switched directly after relocation
5800 (this is important for teleporter switches that teleport the player to
5801 a place where another teleporter switch is in the same direction, which
5802 would then incorrectly be treated as immediately switched before the
5803 direction key that caused the switch was released) */
5805 player->switch_x += jx - old_jx;
5806 player->switch_y += jy - old_jy;
5811 void Explode(int ex, int ey, int phase, int mode)
5817 /* !!! eliminate this variable !!! */
5818 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5820 if (game.explosions_delayed)
5822 ExplodeField[ex][ey] = mode;
5826 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5828 int center_element = Feld[ex][ey];
5829 int artwork_element, explosion_element; /* set these values later */
5832 /* --- This is only really needed (and now handled) in "Impact()". --- */
5833 /* do not explode moving elements that left the explode field in time */
5834 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5835 center_element == EL_EMPTY &&
5836 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5841 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5842 if (mode == EX_TYPE_NORMAL ||
5843 mode == EX_TYPE_CENTER ||
5844 mode == EX_TYPE_CROSS)
5845 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5848 /* remove things displayed in background while burning dynamite */
5849 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5852 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5854 /* put moving element to center field (and let it explode there) */
5855 center_element = MovingOrBlocked2Element(ex, ey);
5856 RemoveMovingField(ex, ey);
5857 Feld[ex][ey] = center_element;
5860 /* now "center_element" is finally determined -- set related values now */
5861 artwork_element = center_element; /* for custom player artwork */
5862 explosion_element = center_element; /* for custom player artwork */
5864 if (IS_PLAYER(ex, ey))
5866 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5868 artwork_element = stored_player[player_nr].artwork_element;
5870 if (level.use_explosion_element[player_nr])
5872 explosion_element = level.explosion_element[player_nr];
5873 artwork_element = explosion_element;
5878 if (mode == EX_TYPE_NORMAL ||
5879 mode == EX_TYPE_CENTER ||
5880 mode == EX_TYPE_CROSS)
5881 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5884 last_phase = element_info[explosion_element].explosion_delay + 1;
5886 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5888 int xx = x - ex + 1;
5889 int yy = y - ey + 1;
5892 if (!IN_LEV_FIELD(x, y) ||
5893 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5894 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5897 element = Feld[x][y];
5899 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5901 element = MovingOrBlocked2Element(x, y);
5903 if (!IS_EXPLOSION_PROOF(element))
5904 RemoveMovingField(x, y);
5907 /* indestructible elements can only explode in center (but not flames) */
5908 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5909 mode == EX_TYPE_BORDER)) ||
5910 element == EL_FLAMES)
5913 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5914 behaviour, for example when touching a yamyam that explodes to rocks
5915 with active deadly shield, a rock is created under the player !!! */
5916 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5918 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5919 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5920 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5922 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5925 if (IS_ACTIVE_BOMB(element))
5927 /* re-activate things under the bomb like gate or penguin */
5928 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5935 /* save walkable background elements while explosion on same tile */
5936 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5937 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5938 Back[x][y] = element;
5940 /* ignite explodable elements reached by other explosion */
5941 if (element == EL_EXPLOSION)
5942 element = Store2[x][y];
5944 if (AmoebaNr[x][y] &&
5945 (element == EL_AMOEBA_FULL ||
5946 element == EL_BD_AMOEBA ||
5947 element == EL_AMOEBA_GROWING))
5949 AmoebaCnt[AmoebaNr[x][y]]--;
5950 AmoebaCnt2[AmoebaNr[x][y]]--;
5955 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5957 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5959 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5961 if (PLAYERINFO(ex, ey)->use_murphy)
5962 Store[x][y] = EL_EMPTY;
5965 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5966 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5967 else if (ELEM_IS_PLAYER(center_element))
5968 Store[x][y] = EL_EMPTY;
5969 else if (center_element == EL_YAMYAM)
5970 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5971 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5972 Store[x][y] = element_info[center_element].content.e[xx][yy];
5974 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5975 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5976 otherwise) -- FIX THIS !!! */
5977 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5978 Store[x][y] = element_info[element].content.e[1][1];
5980 else if (!CAN_EXPLODE(element))
5981 Store[x][y] = element_info[element].content.e[1][1];
5984 Store[x][y] = EL_EMPTY;
5986 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5987 center_element == EL_AMOEBA_TO_DIAMOND)
5988 Store2[x][y] = element;
5990 Feld[x][y] = EL_EXPLOSION;
5991 GfxElement[x][y] = artwork_element;
5993 ExplodePhase[x][y] = 1;
5994 ExplodeDelay[x][y] = last_phase;
5999 if (center_element == EL_YAMYAM)
6000 game.yamyam_content_nr =
6001 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6013 GfxFrame[x][y] = 0; /* restart explosion animation */
6015 last_phase = ExplodeDelay[x][y];
6017 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6021 /* activate this even in non-DEBUG version until cause for crash in
6022 getGraphicAnimationFrame() (see below) is found and eliminated */
6028 /* this can happen if the player leaves an explosion just in time */
6029 if (GfxElement[x][y] == EL_UNDEFINED)
6030 GfxElement[x][y] = EL_EMPTY;
6032 if (GfxElement[x][y] == EL_UNDEFINED)
6035 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6036 printf("Explode(): This should never happen!\n");
6039 GfxElement[x][y] = EL_EMPTY;
6045 border_element = Store2[x][y];
6046 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047 border_element = StorePlayer[x][y];
6049 if (phase == element_info[border_element].ignition_delay ||
6050 phase == last_phase)
6052 boolean border_explosion = FALSE;
6054 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055 !PLAYER_EXPLOSION_PROTECTED(x, y))
6057 KillPlayerUnlessExplosionProtected(x, y);
6058 border_explosion = TRUE;
6060 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6062 Feld[x][y] = Store2[x][y];
6065 border_explosion = TRUE;
6067 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6069 AmoebeUmwandeln(x, y);
6071 border_explosion = TRUE;
6074 /* if an element just explodes due to another explosion (chain-reaction),
6075 do not immediately end the new explosion when it was the last frame of
6076 the explosion (as it would be done in the following "if"-statement!) */
6077 if (border_explosion && phase == last_phase)
6081 if (phase == last_phase)
6085 element = Feld[x][y] = Store[x][y];
6086 Store[x][y] = Store2[x][y] = 0;
6087 GfxElement[x][y] = EL_UNDEFINED;
6089 /* player can escape from explosions and might therefore be still alive */
6090 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6091 element <= EL_PLAYER_IS_EXPLODING_4)
6093 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6094 int explosion_element = EL_PLAYER_1 + player_nr;
6095 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6096 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6098 if (level.use_explosion_element[player_nr])
6099 explosion_element = level.explosion_element[player_nr];
6101 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6102 element_info[explosion_element].content.e[xx][yy]);
6105 /* restore probably existing indestructible background element */
6106 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6107 element = Feld[x][y] = Back[x][y];
6110 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6111 GfxDir[x][y] = MV_NONE;
6112 ChangeDelay[x][y] = 0;
6113 ChangePage[x][y] = -1;
6115 #if USE_NEW_CUSTOM_VALUE
6116 CustomValue[x][y] = 0;
6119 InitField_WithBug2(x, y, FALSE);
6121 TEST_DrawLevelField(x, y);
6123 TestIfElementTouchesCustomElement(x, y);
6125 if (GFX_CRUMBLED(element))
6126 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6128 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6129 StorePlayer[x][y] = 0;
6131 if (ELEM_IS_PLAYER(element))
6132 RelocatePlayer(x, y, element);
6134 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6136 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6137 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6140 TEST_DrawLevelFieldCrumbledSand(x, y);
6142 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6144 DrawLevelElement(x, y, Back[x][y]);
6145 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6147 else if (IS_WALKABLE_UNDER(Back[x][y]))
6149 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6150 DrawLevelElementThruMask(x, y, Back[x][y]);
6152 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6153 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6157 void DynaExplode(int ex, int ey)
6160 int dynabomb_element = Feld[ex][ey];
6161 int dynabomb_size = 1;
6162 boolean dynabomb_xl = FALSE;
6163 struct PlayerInfo *player;
6164 static int xy[4][2] =
6172 if (IS_ACTIVE_BOMB(dynabomb_element))
6174 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6175 dynabomb_size = player->dynabomb_size;
6176 dynabomb_xl = player->dynabomb_xl;
6177 player->dynabombs_left++;
6180 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6182 for (i = 0; i < NUM_DIRECTIONS; i++)
6184 for (j = 1; j <= dynabomb_size; j++)
6186 int x = ex + j * xy[i][0];
6187 int y = ey + j * xy[i][1];
6190 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6193 element = Feld[x][y];
6195 /* do not restart explosions of fields with active bombs */
6196 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6199 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6201 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6202 !IS_DIGGABLE(element) && !dynabomb_xl)
6208 void Bang(int x, int y)
6210 int element = MovingOrBlocked2Element(x, y);
6211 int explosion_type = EX_TYPE_NORMAL;
6213 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6215 struct PlayerInfo *player = PLAYERINFO(x, y);
6217 #if USE_FIX_CE_ACTION_WITH_PLAYER
6218 element = Feld[x][y] = player->initial_element;
6220 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6221 player->element_nr);
6224 if (level.use_explosion_element[player->index_nr])
6226 int explosion_element = level.explosion_element[player->index_nr];
6228 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6229 explosion_type = EX_TYPE_CROSS;
6230 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6231 explosion_type = EX_TYPE_CENTER;
6239 case EL_BD_BUTTERFLY:
6242 case EL_DARK_YAMYAM:
6246 RaiseScoreElement(element);
6249 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6250 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6251 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6252 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6253 case EL_DYNABOMB_INCREASE_NUMBER:
6254 case EL_DYNABOMB_INCREASE_SIZE:
6255 case EL_DYNABOMB_INCREASE_POWER:
6256 explosion_type = EX_TYPE_DYNA;
6259 case EL_DC_LANDMINE:
6261 case EL_EM_EXIT_OPEN:
6262 case EL_EM_STEEL_EXIT_OPEN:
6264 explosion_type = EX_TYPE_CENTER;
6269 case EL_LAMP_ACTIVE:
6270 case EL_AMOEBA_TO_DIAMOND:
6271 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6272 explosion_type = EX_TYPE_CENTER;
6276 if (element_info[element].explosion_type == EXPLODES_CROSS)
6277 explosion_type = EX_TYPE_CROSS;
6278 else if (element_info[element].explosion_type == EXPLODES_1X1)
6279 explosion_type = EX_TYPE_CENTER;
6283 if (explosion_type == EX_TYPE_DYNA)
6286 Explode(x, y, EX_PHASE_START, explosion_type);
6288 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6291 void SplashAcid(int x, int y)
6293 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6294 (!IN_LEV_FIELD(x - 1, y - 2) ||
6295 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6296 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6298 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6299 (!IN_LEV_FIELD(x + 1, y - 2) ||
6300 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6301 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6303 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6306 static void InitBeltMovement()
6308 static int belt_base_element[4] =
6310 EL_CONVEYOR_BELT_1_LEFT,
6311 EL_CONVEYOR_BELT_2_LEFT,
6312 EL_CONVEYOR_BELT_3_LEFT,
6313 EL_CONVEYOR_BELT_4_LEFT
6315 static int belt_base_active_element[4] =
6317 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6318 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6319 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6320 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6325 /* set frame order for belt animation graphic according to belt direction */
6326 for (i = 0; i < NUM_BELTS; i++)
6330 for (j = 0; j < NUM_BELT_PARTS; j++)
6332 int element = belt_base_active_element[belt_nr] + j;
6333 int graphic_1 = el2img(element);
6334 int graphic_2 = el2panelimg(element);
6336 if (game.belt_dir[i] == MV_LEFT)
6338 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6339 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6343 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6344 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6349 SCAN_PLAYFIELD(x, y)
6351 int element = Feld[x][y];
6353 for (i = 0; i < NUM_BELTS; i++)
6355 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6357 int e_belt_nr = getBeltNrFromBeltElement(element);
6360 if (e_belt_nr == belt_nr)
6362 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6364 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6371 static void ToggleBeltSwitch(int x, int y)
6373 static int belt_base_element[4] =
6375 EL_CONVEYOR_BELT_1_LEFT,
6376 EL_CONVEYOR_BELT_2_LEFT,
6377 EL_CONVEYOR_BELT_3_LEFT,
6378 EL_CONVEYOR_BELT_4_LEFT
6380 static int belt_base_active_element[4] =
6382 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6383 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6384 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6385 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6387 static int belt_base_switch_element[4] =
6389 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6390 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6391 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6392 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6394 static int belt_move_dir[4] =
6402 int element = Feld[x][y];
6403 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6404 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6405 int belt_dir = belt_move_dir[belt_dir_nr];
6408 if (!IS_BELT_SWITCH(element))
6411 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6412 game.belt_dir[belt_nr] = belt_dir;
6414 if (belt_dir_nr == 3)
6417 /* set frame order for belt animation graphic according to belt direction */
6418 for (i = 0; i < NUM_BELT_PARTS; i++)
6420 int element = belt_base_active_element[belt_nr] + i;
6421 int graphic_1 = el2img(element);
6422 int graphic_2 = el2panelimg(element);
6424 if (belt_dir == MV_LEFT)
6426 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6427 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6431 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6432 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6436 SCAN_PLAYFIELD(xx, yy)
6438 int element = Feld[xx][yy];
6440 if (IS_BELT_SWITCH(element))
6442 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6444 if (e_belt_nr == belt_nr)
6446 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6447 TEST_DrawLevelField(xx, yy);
6450 else if (IS_BELT(element) && belt_dir != MV_NONE)
6452 int e_belt_nr = getBeltNrFromBeltElement(element);
6454 if (e_belt_nr == belt_nr)
6456 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6458 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6459 TEST_DrawLevelField(xx, yy);
6462 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6464 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6466 if (e_belt_nr == belt_nr)
6468 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6470 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6471 TEST_DrawLevelField(xx, yy);
6477 static void ToggleSwitchgateSwitch(int x, int y)
6481 game.switchgate_pos = !game.switchgate_pos;
6483 SCAN_PLAYFIELD(xx, yy)
6485 int element = Feld[xx][yy];
6487 #if !USE_BOTH_SWITCHGATE_SWITCHES
6488 if (element == EL_SWITCHGATE_SWITCH_UP ||
6489 element == EL_SWITCHGATE_SWITCH_DOWN)
6491 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6492 TEST_DrawLevelField(xx, yy);
6494 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6495 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6497 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6498 TEST_DrawLevelField(xx, yy);
6501 if (element == EL_SWITCHGATE_SWITCH_UP)
6503 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6504 TEST_DrawLevelField(xx, yy);
6506 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6508 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6509 TEST_DrawLevelField(xx, yy);
6511 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6513 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6514 TEST_DrawLevelField(xx, yy);
6516 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6518 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6519 TEST_DrawLevelField(xx, yy);
6522 else if (element == EL_SWITCHGATE_OPEN ||
6523 element == EL_SWITCHGATE_OPENING)
6525 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6527 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6529 else if (element == EL_SWITCHGATE_CLOSED ||
6530 element == EL_SWITCHGATE_CLOSING)
6532 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6534 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6539 static int getInvisibleActiveFromInvisibleElement(int element)
6541 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6542 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6543 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6547 static int getInvisibleFromInvisibleActiveElement(int element)
6549 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6550 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6551 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6555 static void RedrawAllLightSwitchesAndInvisibleElements()
6559 SCAN_PLAYFIELD(x, y)
6561 int element = Feld[x][y];
6563 if (element == EL_LIGHT_SWITCH &&
6564 game.light_time_left > 0)
6566 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6567 TEST_DrawLevelField(x, y);
6569 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6570 game.light_time_left == 0)
6572 Feld[x][y] = EL_LIGHT_SWITCH;
6573 TEST_DrawLevelField(x, y);
6575 else if (element == EL_EMC_DRIPPER &&
6576 game.light_time_left > 0)
6578 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6579 TEST_DrawLevelField(x, y);
6581 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6582 game.light_time_left == 0)
6584 Feld[x][y] = EL_EMC_DRIPPER;
6585 TEST_DrawLevelField(x, y);
6587 else if (element == EL_INVISIBLE_STEELWALL ||
6588 element == EL_INVISIBLE_WALL ||
6589 element == EL_INVISIBLE_SAND)
6591 if (game.light_time_left > 0)
6592 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6594 TEST_DrawLevelField(x, y);
6596 /* uncrumble neighbour fields, if needed */
6597 if (element == EL_INVISIBLE_SAND)
6598 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6600 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6601 element == EL_INVISIBLE_WALL_ACTIVE ||
6602 element == EL_INVISIBLE_SAND_ACTIVE)
6604 if (game.light_time_left == 0)
6605 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6607 TEST_DrawLevelField(x, y);
6609 /* re-crumble neighbour fields, if needed */
6610 if (element == EL_INVISIBLE_SAND)
6611 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6616 static void RedrawAllInvisibleElementsForLenses()
6620 SCAN_PLAYFIELD(x, y)
6622 int element = Feld[x][y];
6624 if (element == EL_EMC_DRIPPER &&
6625 game.lenses_time_left > 0)
6627 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6628 TEST_DrawLevelField(x, y);
6630 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6631 game.lenses_time_left == 0)
6633 Feld[x][y] = EL_EMC_DRIPPER;
6634 TEST_DrawLevelField(x, y);
6636 else if (element == EL_INVISIBLE_STEELWALL ||
6637 element == EL_INVISIBLE_WALL ||
6638 element == EL_INVISIBLE_SAND)
6640 if (game.lenses_time_left > 0)
6641 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6643 TEST_DrawLevelField(x, y);
6645 /* uncrumble neighbour fields, if needed */
6646 if (element == EL_INVISIBLE_SAND)
6647 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6649 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6650 element == EL_INVISIBLE_WALL_ACTIVE ||
6651 element == EL_INVISIBLE_SAND_ACTIVE)
6653 if (game.lenses_time_left == 0)
6654 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6656 TEST_DrawLevelField(x, y);
6658 /* re-crumble neighbour fields, if needed */
6659 if (element == EL_INVISIBLE_SAND)
6660 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6665 static void RedrawAllInvisibleElementsForMagnifier()
6669 SCAN_PLAYFIELD(x, y)
6671 int element = Feld[x][y];
6673 if (element == EL_EMC_FAKE_GRASS &&
6674 game.magnify_time_left > 0)
6676 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6677 TEST_DrawLevelField(x, y);
6679 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6680 game.magnify_time_left == 0)
6682 Feld[x][y] = EL_EMC_FAKE_GRASS;
6683 TEST_DrawLevelField(x, y);
6685 else if (IS_GATE_GRAY(element) &&
6686 game.magnify_time_left > 0)
6688 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6689 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6690 IS_EM_GATE_GRAY(element) ?
6691 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6692 IS_EMC_GATE_GRAY(element) ?
6693 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6694 IS_DC_GATE_GRAY(element) ?
6695 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6697 TEST_DrawLevelField(x, y);
6699 else if (IS_GATE_GRAY_ACTIVE(element) &&
6700 game.magnify_time_left == 0)
6702 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6703 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6704 IS_EM_GATE_GRAY_ACTIVE(element) ?
6705 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6706 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6707 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6708 IS_DC_GATE_GRAY_ACTIVE(element) ?
6709 EL_DC_GATE_WHITE_GRAY :
6711 TEST_DrawLevelField(x, y);
6716 static void ToggleLightSwitch(int x, int y)
6718 int element = Feld[x][y];
6720 game.light_time_left =
6721 (element == EL_LIGHT_SWITCH ?
6722 level.time_light * FRAMES_PER_SECOND : 0);
6724 RedrawAllLightSwitchesAndInvisibleElements();
6727 static void ActivateTimegateSwitch(int x, int y)
6731 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6733 SCAN_PLAYFIELD(xx, yy)
6735 int element = Feld[xx][yy];
6737 if (element == EL_TIMEGATE_CLOSED ||
6738 element == EL_TIMEGATE_CLOSING)
6740 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6741 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6745 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6747 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6748 TEST_DrawLevelField(xx, yy);
6755 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6756 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6758 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6762 void Impact(int x, int y)
6764 boolean last_line = (y == lev_fieldy - 1);
6765 boolean object_hit = FALSE;
6766 boolean impact = (last_line || object_hit);
6767 int element = Feld[x][y];
6768 int smashed = EL_STEELWALL;
6770 if (!last_line) /* check if element below was hit */
6772 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6775 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6776 MovDir[x][y + 1] != MV_DOWN ||
6777 MovPos[x][y + 1] <= TILEY / 2));
6779 /* do not smash moving elements that left the smashed field in time */
6780 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6781 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6784 #if USE_QUICKSAND_IMPACT_BUGFIX
6785 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6787 RemoveMovingField(x, y + 1);
6788 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6789 Feld[x][y + 2] = EL_ROCK;
6790 TEST_DrawLevelField(x, y + 2);
6795 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6797 RemoveMovingField(x, y + 1);
6798 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6799 Feld[x][y + 2] = EL_ROCK;
6800 TEST_DrawLevelField(x, y + 2);
6807 smashed = MovingOrBlocked2Element(x, y + 1);
6809 impact = (last_line || object_hit);
6812 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6814 SplashAcid(x, y + 1);
6818 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6819 /* only reset graphic animation if graphic really changes after impact */
6821 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6823 ResetGfxAnimation(x, y);
6824 TEST_DrawLevelField(x, y);
6827 if (impact && CAN_EXPLODE_IMPACT(element))
6832 else if (impact && element == EL_PEARL &&
6833 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6835 ResetGfxAnimation(x, y);
6837 Feld[x][y] = EL_PEARL_BREAKING;
6838 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6841 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6843 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6848 if (impact && element == EL_AMOEBA_DROP)
6850 if (object_hit && IS_PLAYER(x, y + 1))
6851 KillPlayerUnlessEnemyProtected(x, y + 1);
6852 else if (object_hit && smashed == EL_PENGUIN)
6856 Feld[x][y] = EL_AMOEBA_GROWING;
6857 Store[x][y] = EL_AMOEBA_WET;
6859 ResetRandomAnimationValue(x, y);
6864 if (object_hit) /* check which object was hit */
6866 if ((CAN_PASS_MAGIC_WALL(element) &&
6867 (smashed == EL_MAGIC_WALL ||
6868 smashed == EL_BD_MAGIC_WALL)) ||
6869 (CAN_PASS_DC_MAGIC_WALL(element) &&
6870 smashed == EL_DC_MAGIC_WALL))
6873 int activated_magic_wall =
6874 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6875 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6876 EL_DC_MAGIC_WALL_ACTIVE);
6878 /* activate magic wall / mill */
6879 SCAN_PLAYFIELD(xx, yy)
6881 if (Feld[xx][yy] == smashed)
6882 Feld[xx][yy] = activated_magic_wall;
6885 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6886 game.magic_wall_active = TRUE;
6888 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6889 SND_MAGIC_WALL_ACTIVATING :
6890 smashed == EL_BD_MAGIC_WALL ?
6891 SND_BD_MAGIC_WALL_ACTIVATING :
6892 SND_DC_MAGIC_WALL_ACTIVATING));
6895 if (IS_PLAYER(x, y + 1))
6897 if (CAN_SMASH_PLAYER(element))
6899 KillPlayerUnlessEnemyProtected(x, y + 1);
6903 else if (smashed == EL_PENGUIN)
6905 if (CAN_SMASH_PLAYER(element))
6911 else if (element == EL_BD_DIAMOND)
6913 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6919 else if (((element == EL_SP_INFOTRON ||
6920 element == EL_SP_ZONK) &&
6921 (smashed == EL_SP_SNIKSNAK ||
6922 smashed == EL_SP_ELECTRON ||
6923 smashed == EL_SP_DISK_ORANGE)) ||
6924 (element == EL_SP_INFOTRON &&
6925 smashed == EL_SP_DISK_YELLOW))
6930 else if (CAN_SMASH_EVERYTHING(element))
6932 if (IS_CLASSIC_ENEMY(smashed) ||
6933 CAN_EXPLODE_SMASHED(smashed))
6938 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6940 if (smashed == EL_LAMP ||
6941 smashed == EL_LAMP_ACTIVE)
6946 else if (smashed == EL_NUT)
6948 Feld[x][y + 1] = EL_NUT_BREAKING;
6949 PlayLevelSound(x, y, SND_NUT_BREAKING);
6950 RaiseScoreElement(EL_NUT);
6953 else if (smashed == EL_PEARL)
6955 ResetGfxAnimation(x, y);
6957 Feld[x][y + 1] = EL_PEARL_BREAKING;
6958 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6961 else if (smashed == EL_DIAMOND)
6963 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6964 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6967 else if (IS_BELT_SWITCH(smashed))
6969 ToggleBeltSwitch(x, y + 1);
6971 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6972 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6973 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6974 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6976 ToggleSwitchgateSwitch(x, y + 1);
6978 else if (smashed == EL_LIGHT_SWITCH ||
6979 smashed == EL_LIGHT_SWITCH_ACTIVE)
6981 ToggleLightSwitch(x, y + 1);
6986 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6989 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6991 CheckElementChangeBySide(x, y + 1, smashed, element,
6992 CE_SWITCHED, CH_SIDE_TOP);
6993 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6999 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7004 /* play sound of magic wall / mill */
7006 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7007 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7008 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7010 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7011 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7012 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7013 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7014 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7015 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7020 /* play sound of object that hits the ground */
7021 if (last_line || object_hit)
7022 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7025 inline static void TurnRoundExt(int x, int y)
7037 { 0, 0 }, { 0, 0 }, { 0, 0 },
7042 int left, right, back;
7046 { MV_DOWN, MV_UP, MV_RIGHT },
7047 { MV_UP, MV_DOWN, MV_LEFT },
7049 { MV_LEFT, MV_RIGHT, MV_DOWN },
7053 { MV_RIGHT, MV_LEFT, MV_UP }
7056 int element = Feld[x][y];
7057 int move_pattern = element_info[element].move_pattern;
7059 int old_move_dir = MovDir[x][y];
7060 int left_dir = turn[old_move_dir].left;
7061 int right_dir = turn[old_move_dir].right;
7062 int back_dir = turn[old_move_dir].back;
7064 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7065 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7066 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7067 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7069 int left_x = x + left_dx, left_y = y + left_dy;
7070 int right_x = x + right_dx, right_y = y + right_dy;
7071 int move_x = x + move_dx, move_y = y + move_dy;
7075 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7077 TestIfBadThingTouchesOtherBadThing(x, y);
7079 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7080 MovDir[x][y] = right_dir;
7081 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7082 MovDir[x][y] = left_dir;
7084 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7086 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7089 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7091 TestIfBadThingTouchesOtherBadThing(x, y);
7093 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7094 MovDir[x][y] = left_dir;
7095 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7096 MovDir[x][y] = right_dir;
7098 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7100 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7103 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7105 TestIfBadThingTouchesOtherBadThing(x, y);
7107 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7108 MovDir[x][y] = left_dir;
7109 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7110 MovDir[x][y] = right_dir;
7112 if (MovDir[x][y] != old_move_dir)
7115 else if (element == EL_YAMYAM)
7117 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7118 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7120 if (can_turn_left && can_turn_right)
7121 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7122 else if (can_turn_left)
7123 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7124 else if (can_turn_right)
7125 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7127 MovDir[x][y] = back_dir;
7129 MovDelay[x][y] = 16 + 16 * RND(3);
7131 else if (element == EL_DARK_YAMYAM)
7133 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7135 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7138 if (can_turn_left && can_turn_right)
7139 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7140 else if (can_turn_left)
7141 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7142 else if (can_turn_right)
7143 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7145 MovDir[x][y] = back_dir;
7147 MovDelay[x][y] = 16 + 16 * RND(3);
7149 else if (element == EL_PACMAN)
7151 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7152 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7154 if (can_turn_left && can_turn_right)
7155 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7156 else if (can_turn_left)
7157 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7158 else if (can_turn_right)
7159 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7161 MovDir[x][y] = back_dir;
7163 MovDelay[x][y] = 6 + RND(40);
7165 else if (element == EL_PIG)
7167 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7168 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7169 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7170 boolean should_turn_left, should_turn_right, should_move_on;
7172 int rnd = RND(rnd_value);
7174 should_turn_left = (can_turn_left &&
7176 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7177 y + back_dy + left_dy)));
7178 should_turn_right = (can_turn_right &&
7180 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7181 y + back_dy + right_dy)));
7182 should_move_on = (can_move_on &&
7185 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7186 y + move_dy + left_dy) ||
7187 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7188 y + move_dy + right_dy)));
7190 if (should_turn_left || should_turn_right || should_move_on)
7192 if (should_turn_left && should_turn_right && should_move_on)
7193 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7194 rnd < 2 * rnd_value / 3 ? right_dir :
7196 else if (should_turn_left && should_turn_right)
7197 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7198 else if (should_turn_left && should_move_on)
7199 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7200 else if (should_turn_right && should_move_on)
7201 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7202 else if (should_turn_left)
7203 MovDir[x][y] = left_dir;
7204 else if (should_turn_right)
7205 MovDir[x][y] = right_dir;
7206 else if (should_move_on)
7207 MovDir[x][y] = old_move_dir;
7209 else if (can_move_on && rnd > rnd_value / 8)
7210 MovDir[x][y] = old_move_dir;
7211 else if (can_turn_left && can_turn_right)
7212 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7213 else if (can_turn_left && rnd > rnd_value / 8)
7214 MovDir[x][y] = left_dir;
7215 else if (can_turn_right && rnd > rnd_value/8)
7216 MovDir[x][y] = right_dir;
7218 MovDir[x][y] = back_dir;
7220 xx = x + move_xy[MovDir[x][y]].dx;
7221 yy = y + move_xy[MovDir[x][y]].dy;
7223 if (!IN_LEV_FIELD(xx, yy) ||
7224 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7225 MovDir[x][y] = old_move_dir;
7229 else if (element == EL_DRAGON)
7231 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7232 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7233 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7235 int rnd = RND(rnd_value);
7237 if (can_move_on && rnd > rnd_value / 8)
7238 MovDir[x][y] = old_move_dir;
7239 else if (can_turn_left && can_turn_right)
7240 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7241 else if (can_turn_left && rnd > rnd_value / 8)
7242 MovDir[x][y] = left_dir;
7243 else if (can_turn_right && rnd > rnd_value / 8)
7244 MovDir[x][y] = right_dir;
7246 MovDir[x][y] = back_dir;
7248 xx = x + move_xy[MovDir[x][y]].dx;
7249 yy = y + move_xy[MovDir[x][y]].dy;
7251 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7252 MovDir[x][y] = old_move_dir;
7256 else if (element == EL_MOLE)
7258 boolean can_move_on =
7259 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7260 IS_AMOEBOID(Feld[move_x][move_y]) ||
7261 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7264 boolean can_turn_left =
7265 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7266 IS_AMOEBOID(Feld[left_x][left_y])));
7268 boolean can_turn_right =
7269 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7270 IS_AMOEBOID(Feld[right_x][right_y])));
7272 if (can_turn_left && can_turn_right)
7273 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7274 else if (can_turn_left)
7275 MovDir[x][y] = left_dir;
7277 MovDir[x][y] = right_dir;
7280 if (MovDir[x][y] != old_move_dir)
7283 else if (element == EL_BALLOON)
7285 MovDir[x][y] = game.wind_direction;
7288 else if (element == EL_SPRING)
7290 #if USE_NEW_SPRING_BUMPER
7291 if (MovDir[x][y] & MV_HORIZONTAL)
7293 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7294 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7296 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7297 ResetGfxAnimation(move_x, move_y);
7298 TEST_DrawLevelField(move_x, move_y);
7300 MovDir[x][y] = back_dir;
7302 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7303 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304 MovDir[x][y] = MV_NONE;
7307 if (MovDir[x][y] & MV_HORIZONTAL &&
7308 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7309 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7310 MovDir[x][y] = MV_NONE;
7315 else if (element == EL_ROBOT ||
7316 element == EL_SATELLITE ||
7317 element == EL_PENGUIN ||
7318 element == EL_EMC_ANDROID)
7320 int attr_x = -1, attr_y = -1;
7331 for (i = 0; i < MAX_PLAYERS; i++)
7333 struct PlayerInfo *player = &stored_player[i];
7334 int jx = player->jx, jy = player->jy;
7336 if (!player->active)
7340 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7348 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7349 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7350 game.engine_version < VERSION_IDENT(3,1,0,0)))
7356 if (element == EL_PENGUIN)
7359 static int xy[4][2] =
7367 for (i = 0; i < NUM_DIRECTIONS; i++)
7369 int ex = x + xy[i][0];
7370 int ey = y + xy[i][1];
7372 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7373 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7374 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7375 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7384 MovDir[x][y] = MV_NONE;
7386 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7387 else if (attr_x > x)
7388 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7390 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7391 else if (attr_y > y)
7392 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7394 if (element == EL_ROBOT)
7398 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7399 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7400 Moving2Blocked(x, y, &newx, &newy);
7402 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7403 MovDelay[x][y] = 8 + 8 * !RND(3);
7405 MovDelay[x][y] = 16;
7407 else if (element == EL_PENGUIN)
7413 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7415 boolean first_horiz = RND(2);
7416 int new_move_dir = MovDir[x][y];
7419 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7420 Moving2Blocked(x, y, &newx, &newy);
7422 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7426 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7427 Moving2Blocked(x, y, &newx, &newy);
7429 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7432 MovDir[x][y] = old_move_dir;
7436 else if (element == EL_SATELLITE)
7442 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7444 boolean first_horiz = RND(2);
7445 int new_move_dir = MovDir[x][y];
7448 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7449 Moving2Blocked(x, y, &newx, &newy);
7451 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7455 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456 Moving2Blocked(x, y, &newx, &newy);
7458 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7461 MovDir[x][y] = old_move_dir;
7465 else if (element == EL_EMC_ANDROID)
7467 static int check_pos[16] =
7469 -1, /* 0 => (invalid) */
7470 7, /* 1 => MV_LEFT */
7471 3, /* 2 => MV_RIGHT */
7472 -1, /* 3 => (invalid) */
7474 0, /* 5 => MV_LEFT | MV_UP */
7475 2, /* 6 => MV_RIGHT | MV_UP */
7476 -1, /* 7 => (invalid) */
7477 5, /* 8 => MV_DOWN */
7478 6, /* 9 => MV_LEFT | MV_DOWN */
7479 4, /* 10 => MV_RIGHT | MV_DOWN */
7480 -1, /* 11 => (invalid) */
7481 -1, /* 12 => (invalid) */
7482 -1, /* 13 => (invalid) */
7483 -1, /* 14 => (invalid) */
7484 -1, /* 15 => (invalid) */
7492 { -1, -1, MV_LEFT | MV_UP },
7494 { +1, -1, MV_RIGHT | MV_UP },
7495 { +1, 0, MV_RIGHT },
7496 { +1, +1, MV_RIGHT | MV_DOWN },
7498 { -1, +1, MV_LEFT | MV_DOWN },
7501 int start_pos, check_order;
7502 boolean can_clone = FALSE;
7505 /* check if there is any free field around current position */
7506 for (i = 0; i < 8; i++)
7508 int newx = x + check_xy[i].dx;
7509 int newy = y + check_xy[i].dy;
7511 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7519 if (can_clone) /* randomly find an element to clone */
7523 start_pos = check_pos[RND(8)];
7524 check_order = (RND(2) ? -1 : +1);
7526 for (i = 0; i < 8; i++)
7528 int pos_raw = start_pos + i * check_order;
7529 int pos = (pos_raw + 8) % 8;
7530 int newx = x + check_xy[pos].dx;
7531 int newy = y + check_xy[pos].dy;
7533 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7535 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7536 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7538 Store[x][y] = Feld[newx][newy];
7547 if (can_clone) /* randomly find a direction to move */
7551 start_pos = check_pos[RND(8)];
7552 check_order = (RND(2) ? -1 : +1);
7554 for (i = 0; i < 8; i++)
7556 int pos_raw = start_pos + i * check_order;
7557 int pos = (pos_raw + 8) % 8;
7558 int newx = x + check_xy[pos].dx;
7559 int newy = y + check_xy[pos].dy;
7560 int new_move_dir = check_xy[pos].dir;
7562 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7564 MovDir[x][y] = new_move_dir;
7565 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7574 if (can_clone) /* cloning and moving successful */
7577 /* cannot clone -- try to move towards player */
7579 start_pos = check_pos[MovDir[x][y] & 0x0f];
7580 check_order = (RND(2) ? -1 : +1);
7582 for (i = 0; i < 3; i++)
7584 /* first check start_pos, then previous/next or (next/previous) pos */
7585 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7586 int pos = (pos_raw + 8) % 8;
7587 int newx = x + check_xy[pos].dx;
7588 int newy = y + check_xy[pos].dy;
7589 int new_move_dir = check_xy[pos].dir;
7591 if (IS_PLAYER(newx, newy))
7594 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7596 MovDir[x][y] = new_move_dir;
7597 MovDelay[x][y] = level.android_move_time * 8 + 1;
7604 else if (move_pattern == MV_TURNING_LEFT ||
7605 move_pattern == MV_TURNING_RIGHT ||
7606 move_pattern == MV_TURNING_LEFT_RIGHT ||
7607 move_pattern == MV_TURNING_RIGHT_LEFT ||
7608 move_pattern == MV_TURNING_RANDOM ||
7609 move_pattern == MV_ALL_DIRECTIONS)
7611 boolean can_turn_left =
7612 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7613 boolean can_turn_right =
7614 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7616 if (element_info[element].move_stepsize == 0) /* "not moving" */
7619 if (move_pattern == MV_TURNING_LEFT)
7620 MovDir[x][y] = left_dir;
7621 else if (move_pattern == MV_TURNING_RIGHT)
7622 MovDir[x][y] = right_dir;
7623 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7624 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7625 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7626 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7627 else if (move_pattern == MV_TURNING_RANDOM)
7628 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7629 can_turn_right && !can_turn_left ? right_dir :
7630 RND(2) ? left_dir : right_dir);
7631 else if (can_turn_left && can_turn_right)
7632 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7633 else if (can_turn_left)
7634 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7635 else if (can_turn_right)
7636 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7638 MovDir[x][y] = back_dir;
7640 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7642 else if (move_pattern == MV_HORIZONTAL ||
7643 move_pattern == MV_VERTICAL)
7645 if (move_pattern & old_move_dir)
7646 MovDir[x][y] = back_dir;
7647 else if (move_pattern == MV_HORIZONTAL)
7648 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7649 else if (move_pattern == MV_VERTICAL)
7650 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7652 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7654 else if (move_pattern & MV_ANY_DIRECTION)
7656 MovDir[x][y] = move_pattern;
7657 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7659 else if (move_pattern & MV_WIND_DIRECTION)
7661 MovDir[x][y] = game.wind_direction;
7662 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7664 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7666 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7667 MovDir[x][y] = left_dir;
7668 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7669 MovDir[x][y] = right_dir;
7671 if (MovDir[x][y] != old_move_dir)
7672 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7674 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7676 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7677 MovDir[x][y] = right_dir;
7678 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7679 MovDir[x][y] = left_dir;
7681 if (MovDir[x][y] != old_move_dir)
7682 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7684 else if (move_pattern == MV_TOWARDS_PLAYER ||
7685 move_pattern == MV_AWAY_FROM_PLAYER)
7687 int attr_x = -1, attr_y = -1;
7689 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7700 for (i = 0; i < MAX_PLAYERS; i++)
7702 struct PlayerInfo *player = &stored_player[i];
7703 int jx = player->jx, jy = player->jy;
7705 if (!player->active)
7709 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7717 MovDir[x][y] = MV_NONE;
7719 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7720 else if (attr_x > x)
7721 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7723 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7724 else if (attr_y > y)
7725 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7727 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7729 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7731 boolean first_horiz = RND(2);
7732 int new_move_dir = MovDir[x][y];
7734 if (element_info[element].move_stepsize == 0) /* "not moving" */
7736 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7737 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7743 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744 Moving2Blocked(x, y, &newx, &newy);
7746 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7750 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7751 Moving2Blocked(x, y, &newx, &newy);
7753 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7756 MovDir[x][y] = old_move_dir;
7759 else if (move_pattern == MV_WHEN_PUSHED ||
7760 move_pattern == MV_WHEN_DROPPED)
7762 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763 MovDir[x][y] = MV_NONE;
7767 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7769 static int test_xy[7][2] =
7779 static int test_dir[7] =
7789 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7790 int move_preference = -1000000; /* start with very low preference */
7791 int new_move_dir = MV_NONE;
7792 int start_test = RND(4);
7795 for (i = 0; i < NUM_DIRECTIONS; i++)
7797 int move_dir = test_dir[start_test + i];
7798 int move_dir_preference;
7800 xx = x + test_xy[start_test + i][0];
7801 yy = y + test_xy[start_test + i][1];
7803 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7804 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7806 new_move_dir = move_dir;
7811 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7814 move_dir_preference = -1 * RunnerVisit[xx][yy];
7815 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7816 move_dir_preference = PlayerVisit[xx][yy];
7818 if (move_dir_preference > move_preference)
7820 /* prefer field that has not been visited for the longest time */
7821 move_preference = move_dir_preference;
7822 new_move_dir = move_dir;
7824 else if (move_dir_preference == move_preference &&
7825 move_dir == old_move_dir)
7827 /* prefer last direction when all directions are preferred equally */
7828 move_preference = move_dir_preference;
7829 new_move_dir = move_dir;
7833 MovDir[x][y] = new_move_dir;
7834 if (old_move_dir != new_move_dir)
7835 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7839 static void TurnRound(int x, int y)
7841 int direction = MovDir[x][y];
7845 GfxDir[x][y] = MovDir[x][y];
7847 if (direction != MovDir[x][y])
7851 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7853 ResetGfxFrame(x, y, FALSE);
7856 static boolean JustBeingPushed(int x, int y)
7860 for (i = 0; i < MAX_PLAYERS; i++)
7862 struct PlayerInfo *player = &stored_player[i];
7864 if (player->active && player->is_pushing && player->MovPos)
7866 int next_jx = player->jx + (player->jx - player->last_jx);
7867 int next_jy = player->jy + (player->jy - player->last_jy);
7869 if (x == next_jx && y == next_jy)
7877 void StartMoving(int x, int y)
7879 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7880 int element = Feld[x][y];
7885 if (MovDelay[x][y] == 0)
7886 GfxAction[x][y] = ACTION_DEFAULT;
7888 if (CAN_FALL(element) && y < lev_fieldy - 1)
7890 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7891 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7892 if (JustBeingPushed(x, y))
7895 if (element == EL_QUICKSAND_FULL)
7897 if (IS_FREE(x, y + 1))
7899 InitMovingField(x, y, MV_DOWN);
7900 started_moving = TRUE;
7902 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7903 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7904 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7905 Store[x][y] = EL_ROCK;
7907 Store[x][y] = EL_ROCK;
7910 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7912 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7914 if (!MovDelay[x][y])
7916 MovDelay[x][y] = TILEY + 1;
7918 ResetGfxAnimation(x, y);
7919 ResetGfxAnimation(x, y + 1);
7924 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7925 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7932 Feld[x][y] = EL_QUICKSAND_EMPTY;
7933 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7934 Store[x][y + 1] = Store[x][y];
7937 PlayLevelSoundAction(x, y, ACTION_FILLING);
7939 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7941 if (!MovDelay[x][y])
7943 MovDelay[x][y] = TILEY + 1;
7945 ResetGfxAnimation(x, y);
7946 ResetGfxAnimation(x, y + 1);
7951 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7952 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7959 Feld[x][y] = EL_QUICKSAND_EMPTY;
7960 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7961 Store[x][y + 1] = Store[x][y];
7964 PlayLevelSoundAction(x, y, ACTION_FILLING);
7967 else if (element == EL_QUICKSAND_FAST_FULL)
7969 if (IS_FREE(x, y + 1))
7971 InitMovingField(x, y, MV_DOWN);
7972 started_moving = TRUE;
7974 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7976 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7977 Store[x][y] = EL_ROCK;
7979 Store[x][y] = EL_ROCK;
7982 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7984 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7986 if (!MovDelay[x][y])
7988 MovDelay[x][y] = TILEY + 1;
7990 ResetGfxAnimation(x, y);
7991 ResetGfxAnimation(x, y + 1);
7996 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7997 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8004 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8005 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8006 Store[x][y + 1] = Store[x][y];
8009 PlayLevelSoundAction(x, y, ACTION_FILLING);
8011 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8013 if (!MovDelay[x][y])
8015 MovDelay[x][y] = TILEY + 1;
8017 ResetGfxAnimation(x, y);
8018 ResetGfxAnimation(x, y + 1);
8023 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8024 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8031 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8032 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8033 Store[x][y + 1] = Store[x][y];
8036 PlayLevelSoundAction(x, y, ACTION_FILLING);
8039 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8040 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8042 InitMovingField(x, y, MV_DOWN);
8043 started_moving = TRUE;
8045 Feld[x][y] = EL_QUICKSAND_FILLING;
8046 Store[x][y] = element;
8048 PlayLevelSoundAction(x, y, ACTION_FILLING);
8050 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8051 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8053 InitMovingField(x, y, MV_DOWN);
8054 started_moving = TRUE;
8056 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8057 Store[x][y] = element;
8059 PlayLevelSoundAction(x, y, ACTION_FILLING);
8061 else if (element == EL_MAGIC_WALL_FULL)
8063 if (IS_FREE(x, y + 1))
8065 InitMovingField(x, y, MV_DOWN);
8066 started_moving = TRUE;
8068 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8069 Store[x][y] = EL_CHANGED(Store[x][y]);
8071 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8073 if (!MovDelay[x][y])
8074 MovDelay[x][y] = TILEY/4 + 1;
8083 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8084 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8085 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8089 else if (element == EL_BD_MAGIC_WALL_FULL)
8091 if (IS_FREE(x, y + 1))
8093 InitMovingField(x, y, MV_DOWN);
8094 started_moving = TRUE;
8096 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8097 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8099 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8101 if (!MovDelay[x][y])
8102 MovDelay[x][y] = TILEY/4 + 1;
8111 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8112 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8113 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8117 else if (element == EL_DC_MAGIC_WALL_FULL)
8119 if (IS_FREE(x, y + 1))
8121 InitMovingField(x, y, MV_DOWN);
8122 started_moving = TRUE;
8124 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8125 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8127 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8129 if (!MovDelay[x][y])
8130 MovDelay[x][y] = TILEY/4 + 1;
8139 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8140 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8141 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8145 else if ((CAN_PASS_MAGIC_WALL(element) &&
8146 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8147 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8148 (CAN_PASS_DC_MAGIC_WALL(element) &&
8149 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8152 InitMovingField(x, y, MV_DOWN);
8153 started_moving = TRUE;
8156 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8157 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8158 EL_DC_MAGIC_WALL_FILLING);
8159 Store[x][y] = element;
8161 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8163 SplashAcid(x, y + 1);
8165 InitMovingField(x, y, MV_DOWN);
8166 started_moving = TRUE;
8168 Store[x][y] = EL_ACID;
8171 #if USE_FIX_IMPACT_COLLISION
8172 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8173 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8175 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8176 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8178 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8179 CAN_FALL(element) && WasJustFalling[x][y] &&
8180 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8182 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8183 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8184 (Feld[x][y + 1] == EL_BLOCKED)))
8186 /* this is needed for a special case not covered by calling "Impact()"
8187 from "ContinueMoving()": if an element moves to a tile directly below
8188 another element which was just falling on that tile (which was empty
8189 in the previous frame), the falling element above would just stop
8190 instead of smashing the element below (in previous version, the above
8191 element was just checked for "moving" instead of "falling", resulting
8192 in incorrect smashes caused by horizontal movement of the above
8193 element; also, the case of the player being the element to smash was
8194 simply not covered here... :-/ ) */
8196 CheckCollision[x][y] = 0;
8197 CheckImpact[x][y] = 0;
8201 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8203 if (MovDir[x][y] == MV_NONE)
8205 InitMovingField(x, y, MV_DOWN);
8206 started_moving = TRUE;
8209 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8211 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8212 MovDir[x][y] = MV_DOWN;
8214 InitMovingField(x, y, MV_DOWN);
8215 started_moving = TRUE;
8217 else if (element == EL_AMOEBA_DROP)
8219 Feld[x][y] = EL_AMOEBA_GROWING;
8220 Store[x][y] = EL_AMOEBA_WET;
8222 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8223 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8224 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8225 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8227 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8228 (IS_FREE(x - 1, y + 1) ||
8229 Feld[x - 1][y + 1] == EL_ACID));
8230 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8231 (IS_FREE(x + 1, y + 1) ||
8232 Feld[x + 1][y + 1] == EL_ACID));
8233 boolean can_fall_any = (can_fall_left || can_fall_right);
8234 boolean can_fall_both = (can_fall_left && can_fall_right);
8235 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8237 #if USE_NEW_ALL_SLIPPERY
8238 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8240 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8241 can_fall_right = FALSE;
8242 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8243 can_fall_left = FALSE;
8244 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8245 can_fall_right = FALSE;
8246 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8247 can_fall_left = FALSE;
8249 can_fall_any = (can_fall_left || can_fall_right);
8250 can_fall_both = FALSE;
8253 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8255 if (slippery_type == SLIPPERY_ONLY_LEFT)
8256 can_fall_right = FALSE;
8257 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8258 can_fall_left = FALSE;
8259 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8260 can_fall_right = FALSE;
8261 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8262 can_fall_left = FALSE;
8264 can_fall_any = (can_fall_left || can_fall_right);
8265 can_fall_both = (can_fall_left && can_fall_right);
8269 #if USE_NEW_ALL_SLIPPERY
8271 #if USE_NEW_SP_SLIPPERY
8272 /* !!! better use the same properties as for custom elements here !!! */
8273 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8274 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8276 can_fall_right = FALSE; /* slip down on left side */
8277 can_fall_both = FALSE;
8282 #if USE_NEW_ALL_SLIPPERY
8285 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8286 can_fall_right = FALSE; /* slip down on left side */
8288 can_fall_left = !(can_fall_right = RND(2));
8290 can_fall_both = FALSE;
8295 if (game.emulation == EMU_BOULDERDASH ||
8296 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8297 can_fall_right = FALSE; /* slip down on left side */
8299 can_fall_left = !(can_fall_right = RND(2));
8301 can_fall_both = FALSE;
8307 /* if not determined otherwise, prefer left side for slipping down */
8308 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8309 started_moving = TRUE;
8313 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8315 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8318 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8319 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8320 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8321 int belt_dir = game.belt_dir[belt_nr];
8323 if ((belt_dir == MV_LEFT && left_is_free) ||
8324 (belt_dir == MV_RIGHT && right_is_free))
8326 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8328 InitMovingField(x, y, belt_dir);
8329 started_moving = TRUE;
8331 Pushed[x][y] = TRUE;
8332 Pushed[nextx][y] = TRUE;
8334 GfxAction[x][y] = ACTION_DEFAULT;
8338 MovDir[x][y] = 0; /* if element was moving, stop it */
8343 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8345 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8347 if (CAN_MOVE(element) && !started_moving)
8350 int move_pattern = element_info[element].move_pattern;
8355 if (MovDir[x][y] == MV_NONE)
8357 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8358 x, y, element, element_info[element].token_name);
8359 printf("StartMoving(): This should never happen!\n");
8364 Moving2Blocked(x, y, &newx, &newy);
8366 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8369 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8370 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8372 WasJustMoving[x][y] = 0;
8373 CheckCollision[x][y] = 0;
8375 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8377 if (Feld[x][y] != element) /* element has changed */
8381 if (!MovDelay[x][y]) /* start new movement phase */
8383 /* all objects that can change their move direction after each step
8384 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8386 if (element != EL_YAMYAM &&
8387 element != EL_DARK_YAMYAM &&
8388 element != EL_PACMAN &&
8389 !(move_pattern & MV_ANY_DIRECTION) &&
8390 move_pattern != MV_TURNING_LEFT &&
8391 move_pattern != MV_TURNING_RIGHT &&
8392 move_pattern != MV_TURNING_LEFT_RIGHT &&
8393 move_pattern != MV_TURNING_RIGHT_LEFT &&
8394 move_pattern != MV_TURNING_RANDOM)
8398 if (MovDelay[x][y] && (element == EL_BUG ||
8399 element == EL_SPACESHIP ||
8400 element == EL_SP_SNIKSNAK ||
8401 element == EL_SP_ELECTRON ||
8402 element == EL_MOLE))
8403 TEST_DrawLevelField(x, y);
8407 if (MovDelay[x][y]) /* wait some time before next movement */
8411 if (element == EL_ROBOT ||
8412 element == EL_YAMYAM ||
8413 element == EL_DARK_YAMYAM)
8415 DrawLevelElementAnimationIfNeeded(x, y, element);
8416 PlayLevelSoundAction(x, y, ACTION_WAITING);
8418 else if (element == EL_SP_ELECTRON)
8419 DrawLevelElementAnimationIfNeeded(x, y, element);
8420 else if (element == EL_DRAGON)
8423 int dir = MovDir[x][y];
8424 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8425 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8426 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8427 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8428 dir == MV_UP ? IMG_FLAMES_1_UP :
8429 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8430 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8432 GfxAction[x][y] = ACTION_ATTACKING;
8434 if (IS_PLAYER(x, y))
8435 DrawPlayerField(x, y);
8437 TEST_DrawLevelField(x, y);
8439 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8441 for (i = 1; i <= 3; i++)
8443 int xx = x + i * dx;
8444 int yy = y + i * dy;
8445 int sx = SCREENX(xx);
8446 int sy = SCREENY(yy);
8447 int flame_graphic = graphic + (i - 1);
8449 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8454 int flamed = MovingOrBlocked2Element(xx, yy);
8458 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8460 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8461 RemoveMovingField(xx, yy);
8463 RemoveField(xx, yy);
8465 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8468 RemoveMovingField(xx, yy);
8471 ChangeDelay[xx][yy] = 0;
8473 Feld[xx][yy] = EL_FLAMES;
8475 if (IN_SCR_FIELD(sx, sy))
8477 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8478 DrawGraphic(sx, sy, flame_graphic, frame);
8483 if (Feld[xx][yy] == EL_FLAMES)
8484 Feld[xx][yy] = EL_EMPTY;
8485 TEST_DrawLevelField(xx, yy);
8490 if (MovDelay[x][y]) /* element still has to wait some time */
8492 PlayLevelSoundAction(x, y, ACTION_WAITING);
8498 /* now make next step */
8500 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8502 if (DONT_COLLIDE_WITH(element) &&
8503 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8504 !PLAYER_ENEMY_PROTECTED(newx, newy))
8506 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8511 else if (CAN_MOVE_INTO_ACID(element) &&
8512 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8513 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8514 (MovDir[x][y] == MV_DOWN ||
8515 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8517 SplashAcid(newx, newy);
8518 Store[x][y] = EL_ACID;
8520 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8522 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8523 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8524 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8525 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8528 TEST_DrawLevelField(x, y);
8530 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8531 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8532 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8534 local_player->friends_still_needed--;
8535 if (!local_player->friends_still_needed &&
8536 !local_player->GameOver && AllPlayersGone)
8537 PlayerWins(local_player);
8541 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8543 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8544 TEST_DrawLevelField(newx, newy);
8546 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8548 else if (!IS_FREE(newx, newy))
8550 GfxAction[x][y] = ACTION_WAITING;
8552 if (IS_PLAYER(x, y))
8553 DrawPlayerField(x, y);
8555 TEST_DrawLevelField(x, y);
8560 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8562 if (IS_FOOD_PIG(Feld[newx][newy]))
8564 if (IS_MOVING(newx, newy))
8565 RemoveMovingField(newx, newy);
8568 Feld[newx][newy] = EL_EMPTY;
8569 TEST_DrawLevelField(newx, newy);
8572 PlayLevelSound(x, y, SND_PIG_DIGGING);
8574 else if (!IS_FREE(newx, newy))
8576 if (IS_PLAYER(x, y))
8577 DrawPlayerField(x, y);
8579 TEST_DrawLevelField(x, y);
8584 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8586 if (Store[x][y] != EL_EMPTY)
8588 boolean can_clone = FALSE;
8591 /* check if element to clone is still there */
8592 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8594 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8602 /* cannot clone or target field not free anymore -- do not clone */
8603 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8604 Store[x][y] = EL_EMPTY;
8607 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8609 if (IS_MV_DIAGONAL(MovDir[x][y]))
8611 int diagonal_move_dir = MovDir[x][y];
8612 int stored = Store[x][y];
8613 int change_delay = 8;
8616 /* android is moving diagonally */
8618 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8620 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8621 GfxElement[x][y] = EL_EMC_ANDROID;
8622 GfxAction[x][y] = ACTION_SHRINKING;
8623 GfxDir[x][y] = diagonal_move_dir;
8624 ChangeDelay[x][y] = change_delay;
8626 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8629 DrawLevelGraphicAnimation(x, y, graphic);
8630 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8632 if (Feld[newx][newy] == EL_ACID)
8634 SplashAcid(newx, newy);
8639 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8641 Store[newx][newy] = EL_EMC_ANDROID;
8642 GfxElement[newx][newy] = EL_EMC_ANDROID;
8643 GfxAction[newx][newy] = ACTION_GROWING;
8644 GfxDir[newx][newy] = diagonal_move_dir;
8645 ChangeDelay[newx][newy] = change_delay;
8647 graphic = el_act_dir2img(GfxElement[newx][newy],
8648 GfxAction[newx][newy], GfxDir[newx][newy]);
8650 DrawLevelGraphicAnimation(newx, newy, graphic);
8651 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8657 Feld[newx][newy] = EL_EMPTY;
8658 TEST_DrawLevelField(newx, newy);
8660 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8663 else if (!IS_FREE(newx, newy))
8666 if (IS_PLAYER(x, y))
8667 DrawPlayerField(x, y);
8669 TEST_DrawLevelField(x, y);
8675 else if (IS_CUSTOM_ELEMENT(element) &&
8676 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8679 if (!DigFieldByCE(newx, newy, element))
8682 int new_element = Feld[newx][newy];
8684 if (!IS_FREE(newx, newy))
8686 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8687 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8690 /* no element can dig solid indestructible elements */
8691 if (IS_INDESTRUCTIBLE(new_element) &&
8692 !IS_DIGGABLE(new_element) &&
8693 !IS_COLLECTIBLE(new_element))
8696 if (AmoebaNr[newx][newy] &&
8697 (new_element == EL_AMOEBA_FULL ||
8698 new_element == EL_BD_AMOEBA ||
8699 new_element == EL_AMOEBA_GROWING))
8701 AmoebaCnt[AmoebaNr[newx][newy]]--;
8702 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8705 if (IS_MOVING(newx, newy))
8706 RemoveMovingField(newx, newy);
8709 RemoveField(newx, newy);
8710 TEST_DrawLevelField(newx, newy);
8713 /* if digged element was about to explode, prevent the explosion */
8714 ExplodeField[newx][newy] = EX_TYPE_NONE;
8716 PlayLevelSoundAction(x, y, action);
8719 Store[newx][newy] = EL_EMPTY;
8722 /* this makes it possible to leave the removed element again */
8723 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8724 Store[newx][newy] = new_element;
8726 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8728 int move_leave_element = element_info[element].move_leave_element;
8730 /* this makes it possible to leave the removed element again */
8731 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8732 new_element : move_leave_element);
8738 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8740 RunnerVisit[x][y] = FrameCounter;
8741 PlayerVisit[x][y] /= 8; /* expire player visit path */
8744 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8746 if (!IS_FREE(newx, newy))
8748 if (IS_PLAYER(x, y))
8749 DrawPlayerField(x, y);
8751 TEST_DrawLevelField(x, y);
8757 boolean wanna_flame = !RND(10);
8758 int dx = newx - x, dy = newy - y;
8759 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8760 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8761 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8762 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8763 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8764 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8767 IS_CLASSIC_ENEMY(element1) ||
8768 IS_CLASSIC_ENEMY(element2)) &&
8769 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8770 element1 != EL_FLAMES && element2 != EL_FLAMES)
8772 ResetGfxAnimation(x, y);
8773 GfxAction[x][y] = ACTION_ATTACKING;
8775 if (IS_PLAYER(x, y))
8776 DrawPlayerField(x, y);
8778 TEST_DrawLevelField(x, y);
8780 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8782 MovDelay[x][y] = 50;
8786 RemoveField(newx, newy);
8788 Feld[newx][newy] = EL_FLAMES;
8789 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8792 RemoveField(newx1, newy1);
8794 Feld[newx1][newy1] = EL_FLAMES;
8796 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8799 RemoveField(newx2, newy2);
8801 Feld[newx2][newy2] = EL_FLAMES;
8808 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8809 Feld[newx][newy] == EL_DIAMOND)
8811 if (IS_MOVING(newx, newy))
8812 RemoveMovingField(newx, newy);
8815 Feld[newx][newy] = EL_EMPTY;
8816 TEST_DrawLevelField(newx, newy);
8819 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8821 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8822 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8824 if (AmoebaNr[newx][newy])
8826 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8827 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8828 Feld[newx][newy] == EL_BD_AMOEBA)
8829 AmoebaCnt[AmoebaNr[newx][newy]]--;
8834 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8836 RemoveMovingField(newx, newy);
8839 if (IS_MOVING(newx, newy))
8841 RemoveMovingField(newx, newy);
8846 Feld[newx][newy] = EL_EMPTY;
8847 TEST_DrawLevelField(newx, newy);
8850 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8852 else if ((element == EL_PACMAN || element == EL_MOLE)
8853 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8855 if (AmoebaNr[newx][newy])
8857 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8858 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8859 Feld[newx][newy] == EL_BD_AMOEBA)
8860 AmoebaCnt[AmoebaNr[newx][newy]]--;
8863 if (element == EL_MOLE)
8865 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8866 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8868 ResetGfxAnimation(x, y);
8869 GfxAction[x][y] = ACTION_DIGGING;
8870 TEST_DrawLevelField(x, y);
8872 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8874 return; /* wait for shrinking amoeba */
8876 else /* element == EL_PACMAN */
8878 Feld[newx][newy] = EL_EMPTY;
8879 TEST_DrawLevelField(newx, newy);
8880 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8883 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8884 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8885 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8887 /* wait for shrinking amoeba to completely disappear */
8890 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8892 /* object was running against a wall */
8897 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8898 if (move_pattern & MV_ANY_DIRECTION &&
8899 move_pattern == MovDir[x][y])
8901 int blocking_element =
8902 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8904 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8907 element = Feld[x][y]; /* element might have changed */
8911 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8912 DrawLevelElementAnimation(x, y, element);
8914 if (DONT_TOUCH(element))
8915 TestIfBadThingTouchesPlayer(x, y);
8920 InitMovingField(x, y, MovDir[x][y]);
8922 PlayLevelSoundAction(x, y, ACTION_MOVING);
8926 ContinueMoving(x, y);
8929 void ContinueMoving(int x, int y)
8931 int element = Feld[x][y];
8932 struct ElementInfo *ei = &element_info[element];
8933 int direction = MovDir[x][y];
8934 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8935 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8936 int newx = x + dx, newy = y + dy;
8937 int stored = Store[x][y];
8938 int stored_new = Store[newx][newy];
8939 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8940 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8941 boolean last_line = (newy == lev_fieldy - 1);
8943 MovPos[x][y] += getElementMoveStepsize(x, y);
8945 if (pushed_by_player) /* special case: moving object pushed by player */
8946 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8948 if (ABS(MovPos[x][y]) < TILEX)
8951 int ee = Feld[x][y];
8952 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8953 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8955 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8956 x, y, ABS(MovPos[x][y]),
8958 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8961 TEST_DrawLevelField(x, y);
8963 return; /* element is still moving */
8966 /* element reached destination field */
8968 Feld[x][y] = EL_EMPTY;
8969 Feld[newx][newy] = element;
8970 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8972 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8974 element = Feld[newx][newy] = EL_ACID;
8976 else if (element == EL_MOLE)
8978 Feld[x][y] = EL_SAND;
8980 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8982 else if (element == EL_QUICKSAND_FILLING)
8984 element = Feld[newx][newy] = get_next_element(element);
8985 Store[newx][newy] = Store[x][y];
8987 else if (element == EL_QUICKSAND_EMPTYING)
8989 Feld[x][y] = get_next_element(element);
8990 element = Feld[newx][newy] = Store[x][y];
8992 else if (element == EL_QUICKSAND_FAST_FILLING)
8994 element = Feld[newx][newy] = get_next_element(element);
8995 Store[newx][newy] = Store[x][y];
8997 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8999 Feld[x][y] = get_next_element(element);
9000 element = Feld[newx][newy] = Store[x][y];
9002 else if (element == EL_MAGIC_WALL_FILLING)
9004 element = Feld[newx][newy] = get_next_element(element);
9005 if (!game.magic_wall_active)
9006 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9007 Store[newx][newy] = Store[x][y];
9009 else if (element == EL_MAGIC_WALL_EMPTYING)
9011 Feld[x][y] = get_next_element(element);
9012 if (!game.magic_wall_active)
9013 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9014 element = Feld[newx][newy] = Store[x][y];
9016 #if USE_NEW_CUSTOM_VALUE
9017 InitField(newx, newy, FALSE);
9020 else if (element == EL_BD_MAGIC_WALL_FILLING)
9022 element = Feld[newx][newy] = get_next_element(element);
9023 if (!game.magic_wall_active)
9024 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9025 Store[newx][newy] = Store[x][y];
9027 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9029 Feld[x][y] = get_next_element(element);
9030 if (!game.magic_wall_active)
9031 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9032 element = Feld[newx][newy] = Store[x][y];
9034 #if USE_NEW_CUSTOM_VALUE
9035 InitField(newx, newy, FALSE);
9038 else if (element == EL_DC_MAGIC_WALL_FILLING)
9040 element = Feld[newx][newy] = get_next_element(element);
9041 if (!game.magic_wall_active)
9042 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9043 Store[newx][newy] = Store[x][y];
9045 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9047 Feld[x][y] = get_next_element(element);
9048 if (!game.magic_wall_active)
9049 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9050 element = Feld[newx][newy] = Store[x][y];
9052 #if USE_NEW_CUSTOM_VALUE
9053 InitField(newx, newy, FALSE);
9056 else if (element == EL_AMOEBA_DROPPING)
9058 Feld[x][y] = get_next_element(element);
9059 element = Feld[newx][newy] = Store[x][y];
9061 else if (element == EL_SOKOBAN_OBJECT)
9064 Feld[x][y] = Back[x][y];
9066 if (Back[newx][newy])
9067 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9069 Back[x][y] = Back[newx][newy] = 0;
9072 Store[x][y] = EL_EMPTY;
9077 MovDelay[newx][newy] = 0;
9079 if (CAN_CHANGE_OR_HAS_ACTION(element))
9081 /* copy element change control values to new field */
9082 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9083 ChangePage[newx][newy] = ChangePage[x][y];
9084 ChangeCount[newx][newy] = ChangeCount[x][y];
9085 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9088 #if USE_NEW_CUSTOM_VALUE
9089 CustomValue[newx][newy] = CustomValue[x][y];
9092 ChangeDelay[x][y] = 0;
9093 ChangePage[x][y] = -1;
9094 ChangeCount[x][y] = 0;
9095 ChangeEvent[x][y] = -1;
9097 #if USE_NEW_CUSTOM_VALUE
9098 CustomValue[x][y] = 0;
9101 /* copy animation control values to new field */
9102 GfxFrame[newx][newy] = GfxFrame[x][y];
9103 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9104 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9105 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9107 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9109 /* some elements can leave other elements behind after moving */
9111 if (ei->move_leave_element != EL_EMPTY &&
9112 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9113 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9115 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9116 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9117 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9120 int move_leave_element = ei->move_leave_element;
9124 /* this makes it possible to leave the removed element again */
9125 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9126 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9128 /* this makes it possible to leave the removed element again */
9129 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9130 move_leave_element = stored;
9133 /* this makes it possible to leave the removed element again */
9134 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9135 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9136 move_leave_element = stored;
9139 Feld[x][y] = move_leave_element;
9141 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9142 MovDir[x][y] = direction;
9144 InitField(x, y, FALSE);
9146 if (GFX_CRUMBLED(Feld[x][y]))
9147 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9149 if (ELEM_IS_PLAYER(move_leave_element))
9150 RelocatePlayer(x, y, move_leave_element);
9153 /* do this after checking for left-behind element */
9154 ResetGfxAnimation(x, y); /* reset animation values for old field */
9156 if (!CAN_MOVE(element) ||
9157 (CAN_FALL(element) && direction == MV_DOWN &&
9158 (element == EL_SPRING ||
9159 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9160 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9161 GfxDir[x][y] = MovDir[newx][newy] = 0;
9163 TEST_DrawLevelField(x, y);
9164 TEST_DrawLevelField(newx, newy);
9166 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9168 /* prevent pushed element from moving on in pushed direction */
9169 if (pushed_by_player && CAN_MOVE(element) &&
9170 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9171 !(element_info[element].move_pattern & direction))
9172 TurnRound(newx, newy);
9174 /* prevent elements on conveyor belt from moving on in last direction */
9175 if (pushed_by_conveyor && CAN_FALL(element) &&
9176 direction & MV_HORIZONTAL)
9177 MovDir[newx][newy] = 0;
9179 if (!pushed_by_player)
9181 int nextx = newx + dx, nexty = newy + dy;
9182 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9184 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9186 if (CAN_FALL(element) && direction == MV_DOWN)
9187 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9189 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9190 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9192 #if USE_FIX_IMPACT_COLLISION
9193 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9194 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9198 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9200 TestIfBadThingTouchesPlayer(newx, newy);
9201 TestIfBadThingTouchesFriend(newx, newy);
9203 if (!IS_CUSTOM_ELEMENT(element))
9204 TestIfBadThingTouchesOtherBadThing(newx, newy);
9206 else if (element == EL_PENGUIN)
9207 TestIfFriendTouchesBadThing(newx, newy);
9209 if (DONT_GET_HIT_BY(element))
9211 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9214 /* give the player one last chance (one more frame) to move away */
9215 if (CAN_FALL(element) && direction == MV_DOWN &&
9216 (last_line || (!IS_FREE(x, newy + 1) &&
9217 (!IS_PLAYER(x, newy + 1) ||
9218 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9221 if (pushed_by_player && !game.use_change_when_pushing_bug)
9223 int push_side = MV_DIR_OPPOSITE(direction);
9224 struct PlayerInfo *player = PLAYERINFO(x, y);
9226 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9227 player->index_bit, push_side);
9228 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9229 player->index_bit, push_side);
9232 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9233 MovDelay[newx][newy] = 1;
9235 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9237 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9240 if (ChangePage[newx][newy] != -1) /* delayed change */
9242 int page = ChangePage[newx][newy];
9243 struct ElementChangeInfo *change = &ei->change_page[page];
9245 ChangePage[newx][newy] = -1;
9247 if (change->can_change)
9249 if (ChangeElement(newx, newy, element, page))
9251 if (change->post_change_function)
9252 change->post_change_function(newx, newy);
9256 if (change->has_action)
9257 ExecuteCustomElementAction(newx, newy, element, page);
9261 TestIfElementHitsCustomElement(newx, newy, direction);
9262 TestIfPlayerTouchesCustomElement(newx, newy);
9263 TestIfElementTouchesCustomElement(newx, newy);
9265 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9266 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9267 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9268 MV_DIR_OPPOSITE(direction));
9271 int AmoebeNachbarNr(int ax, int ay)
9274 int element = Feld[ax][ay];
9276 static int xy[4][2] =
9284 for (i = 0; i < NUM_DIRECTIONS; i++)
9286 int x = ax + xy[i][0];
9287 int y = ay + xy[i][1];
9289 if (!IN_LEV_FIELD(x, y))
9292 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9293 group_nr = AmoebaNr[x][y];
9299 void AmoebenVereinigen(int ax, int ay)
9301 int i, x, y, xx, yy;
9302 int new_group_nr = AmoebaNr[ax][ay];
9303 static int xy[4][2] =
9311 if (new_group_nr == 0)
9314 for (i = 0; i < NUM_DIRECTIONS; i++)
9319 if (!IN_LEV_FIELD(x, y))
9322 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9323 Feld[x][y] == EL_BD_AMOEBA ||
9324 Feld[x][y] == EL_AMOEBA_DEAD) &&
9325 AmoebaNr[x][y] != new_group_nr)
9327 int old_group_nr = AmoebaNr[x][y];
9329 if (old_group_nr == 0)
9332 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9333 AmoebaCnt[old_group_nr] = 0;
9334 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9335 AmoebaCnt2[old_group_nr] = 0;
9337 SCAN_PLAYFIELD(xx, yy)
9339 if (AmoebaNr[xx][yy] == old_group_nr)
9340 AmoebaNr[xx][yy] = new_group_nr;
9346 void AmoebeUmwandeln(int ax, int ay)
9350 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9352 int group_nr = AmoebaNr[ax][ay];
9357 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9358 printf("AmoebeUmwandeln(): This should never happen!\n");
9363 SCAN_PLAYFIELD(x, y)
9365 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9368 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9372 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9373 SND_AMOEBA_TURNING_TO_GEM :
9374 SND_AMOEBA_TURNING_TO_ROCK));
9379 static int xy[4][2] =
9387 for (i = 0; i < NUM_DIRECTIONS; i++)
9392 if (!IN_LEV_FIELD(x, y))
9395 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9397 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9398 SND_AMOEBA_TURNING_TO_GEM :
9399 SND_AMOEBA_TURNING_TO_ROCK));
9406 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9409 int group_nr = AmoebaNr[ax][ay];
9410 boolean done = FALSE;
9415 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9416 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9421 SCAN_PLAYFIELD(x, y)
9423 if (AmoebaNr[x][y] == group_nr &&
9424 (Feld[x][y] == EL_AMOEBA_DEAD ||
9425 Feld[x][y] == EL_BD_AMOEBA ||
9426 Feld[x][y] == EL_AMOEBA_GROWING))
9429 Feld[x][y] = new_element;
9430 InitField(x, y, FALSE);
9431 TEST_DrawLevelField(x, y);
9437 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9438 SND_BD_AMOEBA_TURNING_TO_ROCK :
9439 SND_BD_AMOEBA_TURNING_TO_GEM));
9442 void AmoebeWaechst(int x, int y)
9444 static unsigned long sound_delay = 0;
9445 static unsigned long sound_delay_value = 0;
9447 if (!MovDelay[x][y]) /* start new growing cycle */
9451 if (DelayReached(&sound_delay, sound_delay_value))
9453 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9454 sound_delay_value = 30;
9458 if (MovDelay[x][y]) /* wait some time before growing bigger */
9461 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9463 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9464 6 - MovDelay[x][y]);
9466 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9469 if (!MovDelay[x][y])
9471 Feld[x][y] = Store[x][y];
9473 TEST_DrawLevelField(x, y);
9478 void AmoebaDisappearing(int x, int y)
9480 static unsigned long sound_delay = 0;
9481 static unsigned long sound_delay_value = 0;
9483 if (!MovDelay[x][y]) /* start new shrinking cycle */
9487 if (DelayReached(&sound_delay, sound_delay_value))
9488 sound_delay_value = 30;
9491 if (MovDelay[x][y]) /* wait some time before shrinking */
9494 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9496 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9497 6 - MovDelay[x][y]);
9499 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9502 if (!MovDelay[x][y])
9504 Feld[x][y] = EL_EMPTY;
9505 TEST_DrawLevelField(x, y);
9507 /* don't let mole enter this field in this cycle;
9508 (give priority to objects falling to this field from above) */
9514 void AmoebeAbleger(int ax, int ay)
9517 int element = Feld[ax][ay];
9518 int graphic = el2img(element);
9519 int newax = ax, neway = ay;
9520 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9521 static int xy[4][2] =
9529 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9531 Feld[ax][ay] = EL_AMOEBA_DEAD;
9532 TEST_DrawLevelField(ax, ay);
9536 if (IS_ANIMATED(graphic))
9537 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9539 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9540 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9542 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9545 if (MovDelay[ax][ay])
9549 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9552 int x = ax + xy[start][0];
9553 int y = ay + xy[start][1];
9555 if (!IN_LEV_FIELD(x, y))
9558 if (IS_FREE(x, y) ||
9559 CAN_GROW_INTO(Feld[x][y]) ||
9560 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9561 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9567 if (newax == ax && neway == ay)
9570 else /* normal or "filled" (BD style) amoeba */
9573 boolean waiting_for_player = FALSE;
9575 for (i = 0; i < NUM_DIRECTIONS; i++)
9577 int j = (start + i) % 4;
9578 int x = ax + xy[j][0];
9579 int y = ay + xy[j][1];
9581 if (!IN_LEV_FIELD(x, y))
9584 if (IS_FREE(x, y) ||
9585 CAN_GROW_INTO(Feld[x][y]) ||
9586 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9587 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9593 else if (IS_PLAYER(x, y))
9594 waiting_for_player = TRUE;
9597 if (newax == ax && neway == ay) /* amoeba cannot grow */
9599 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9601 Feld[ax][ay] = EL_AMOEBA_DEAD;
9602 TEST_DrawLevelField(ax, ay);
9603 AmoebaCnt[AmoebaNr[ax][ay]]--;
9605 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9607 if (element == EL_AMOEBA_FULL)
9608 AmoebeUmwandeln(ax, ay);
9609 else if (element == EL_BD_AMOEBA)
9610 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9615 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9617 /* amoeba gets larger by growing in some direction */
9619 int new_group_nr = AmoebaNr[ax][ay];
9622 if (new_group_nr == 0)
9624 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9625 printf("AmoebeAbleger(): This should never happen!\n");
9630 AmoebaNr[newax][neway] = new_group_nr;
9631 AmoebaCnt[new_group_nr]++;
9632 AmoebaCnt2[new_group_nr]++;
9634 /* if amoeba touches other amoeba(s) after growing, unify them */
9635 AmoebenVereinigen(newax, neway);
9637 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9639 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9645 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9646 (neway == lev_fieldy - 1 && newax != ax))
9648 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9649 Store[newax][neway] = element;
9651 else if (neway == ay || element == EL_EMC_DRIPPER)
9653 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9655 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9659 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9660 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9661 Store[ax][ay] = EL_AMOEBA_DROP;
9662 ContinueMoving(ax, ay);
9666 TEST_DrawLevelField(newax, neway);
9669 void Life(int ax, int ay)
9673 int element = Feld[ax][ay];
9674 int graphic = el2img(element);
9675 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9677 boolean changed = FALSE;
9679 if (IS_ANIMATED(graphic))
9680 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9685 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9686 MovDelay[ax][ay] = life_time;
9688 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9691 if (MovDelay[ax][ay])
9695 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9697 int xx = ax+x1, yy = ay+y1;
9700 if (!IN_LEV_FIELD(xx, yy))
9703 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9705 int x = xx+x2, y = yy+y2;
9707 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9710 if (((Feld[x][y] == element ||
9711 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9713 (IS_FREE(x, y) && Stop[x][y]))
9717 if (xx == ax && yy == ay) /* field in the middle */
9719 if (nachbarn < life_parameter[0] ||
9720 nachbarn > life_parameter[1])
9722 Feld[xx][yy] = EL_EMPTY;
9724 TEST_DrawLevelField(xx, yy);
9725 Stop[xx][yy] = TRUE;
9729 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9730 { /* free border field */
9731 if (nachbarn >= life_parameter[2] &&
9732 nachbarn <= life_parameter[3])
9734 Feld[xx][yy] = element;
9735 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9737 TEST_DrawLevelField(xx, yy);
9738 Stop[xx][yy] = TRUE;
9745 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9746 SND_GAME_OF_LIFE_GROWING);
9749 static void InitRobotWheel(int x, int y)
9751 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9754 static void RunRobotWheel(int x, int y)
9756 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9759 static void StopRobotWheel(int x, int y)
9761 if (ZX == x && ZY == y)
9765 game.robot_wheel_active = FALSE;
9769 static void InitTimegateWheel(int x, int y)
9771 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9774 static void RunTimegateWheel(int x, int y)
9776 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9779 static void InitMagicBallDelay(int x, int y)
9782 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9784 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9788 static void ActivateMagicBall(int bx, int by)
9792 if (level.ball_random)
9794 int pos_border = RND(8); /* select one of the eight border elements */
9795 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9796 int xx = pos_content % 3;
9797 int yy = pos_content / 3;
9802 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9803 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9807 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9809 int xx = x - bx + 1;
9810 int yy = y - by + 1;
9812 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9813 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9817 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9820 void CheckExit(int x, int y)
9822 if (local_player->gems_still_needed > 0 ||
9823 local_player->sokobanfields_still_needed > 0 ||
9824 local_player->lights_still_needed > 0)
9826 int element = Feld[x][y];
9827 int graphic = el2img(element);
9829 if (IS_ANIMATED(graphic))
9830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9835 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9838 Feld[x][y] = EL_EXIT_OPENING;
9840 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9843 void CheckExitEM(int x, int y)
9845 if (local_player->gems_still_needed > 0 ||
9846 local_player->sokobanfields_still_needed > 0 ||
9847 local_player->lights_still_needed > 0)
9849 int element = Feld[x][y];
9850 int graphic = el2img(element);
9852 if (IS_ANIMATED(graphic))
9853 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9858 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9861 Feld[x][y] = EL_EM_EXIT_OPENING;
9863 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9866 void CheckExitSteel(int x, int y)
9868 if (local_player->gems_still_needed > 0 ||
9869 local_player->sokobanfields_still_needed > 0 ||
9870 local_player->lights_still_needed > 0)
9872 int element = Feld[x][y];
9873 int graphic = el2img(element);
9875 if (IS_ANIMATED(graphic))
9876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9881 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9884 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9886 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9889 void CheckExitSteelEM(int x, int y)
9891 if (local_player->gems_still_needed > 0 ||
9892 local_player->sokobanfields_still_needed > 0 ||
9893 local_player->lights_still_needed > 0)
9895 int element = Feld[x][y];
9896 int graphic = el2img(element);
9898 if (IS_ANIMATED(graphic))
9899 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9904 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9907 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9909 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9912 void CheckExitSP(int x, int y)
9914 if (local_player->gems_still_needed > 0)
9916 int element = Feld[x][y];
9917 int graphic = el2img(element);
9919 if (IS_ANIMATED(graphic))
9920 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9925 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9928 Feld[x][y] = EL_SP_EXIT_OPENING;
9930 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9933 static void CloseAllOpenTimegates()
9937 SCAN_PLAYFIELD(x, y)
9939 int element = Feld[x][y];
9941 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9943 Feld[x][y] = EL_TIMEGATE_CLOSING;
9945 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9950 void DrawTwinkleOnField(int x, int y)
9952 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9955 if (Feld[x][y] == EL_BD_DIAMOND)
9958 if (MovDelay[x][y] == 0) /* next animation frame */
9959 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9961 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9965 DrawLevelElementAnimation(x, y, Feld[x][y]);
9967 if (MovDelay[x][y] != 0)
9969 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9970 10 - MovDelay[x][y]);
9972 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9977 void MauerWaechst(int x, int y)
9981 if (!MovDelay[x][y]) /* next animation frame */
9982 MovDelay[x][y] = 3 * delay;
9984 if (MovDelay[x][y]) /* wait some time before next frame */
9988 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9990 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9991 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9993 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9996 if (!MovDelay[x][y])
9998 if (MovDir[x][y] == MV_LEFT)
10000 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10001 TEST_DrawLevelField(x - 1, y);
10003 else if (MovDir[x][y] == MV_RIGHT)
10005 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10006 TEST_DrawLevelField(x + 1, y);
10008 else if (MovDir[x][y] == MV_UP)
10010 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10011 TEST_DrawLevelField(x, y - 1);
10015 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10016 TEST_DrawLevelField(x, y + 1);
10019 Feld[x][y] = Store[x][y];
10021 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10022 TEST_DrawLevelField(x, y);
10027 void MauerAbleger(int ax, int ay)
10029 int element = Feld[ax][ay];
10030 int graphic = el2img(element);
10031 boolean oben_frei = FALSE, unten_frei = FALSE;
10032 boolean links_frei = FALSE, rechts_frei = FALSE;
10033 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10034 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10035 boolean new_wall = FALSE;
10037 if (IS_ANIMATED(graphic))
10038 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10040 if (!MovDelay[ax][ay]) /* start building new wall */
10041 MovDelay[ax][ay] = 6;
10043 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10045 MovDelay[ax][ay]--;
10046 if (MovDelay[ax][ay])
10050 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10052 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10054 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10056 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10057 rechts_frei = TRUE;
10059 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10060 element == EL_EXPANDABLE_WALL_ANY)
10064 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10065 Store[ax][ay-1] = element;
10066 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10067 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10068 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10069 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10074 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10075 Store[ax][ay+1] = element;
10076 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10077 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10078 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10079 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10084 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10085 element == EL_EXPANDABLE_WALL_ANY ||
10086 element == EL_EXPANDABLE_WALL ||
10087 element == EL_BD_EXPANDABLE_WALL)
10091 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10092 Store[ax-1][ay] = element;
10093 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10094 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10095 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10096 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10102 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10103 Store[ax+1][ay] = element;
10104 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10105 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10106 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10107 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10112 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10113 TEST_DrawLevelField(ax, ay);
10115 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10116 oben_massiv = TRUE;
10117 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10118 unten_massiv = TRUE;
10119 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10120 links_massiv = TRUE;
10121 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10122 rechts_massiv = TRUE;
10124 if (((oben_massiv && unten_massiv) ||
10125 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126 element == EL_EXPANDABLE_WALL) &&
10127 ((links_massiv && rechts_massiv) ||
10128 element == EL_EXPANDABLE_WALL_VERTICAL))
10129 Feld[ax][ay] = EL_WALL;
10132 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10135 void MauerAblegerStahl(int ax, int ay)
10137 int element = Feld[ax][ay];
10138 int graphic = el2img(element);
10139 boolean oben_frei = FALSE, unten_frei = FALSE;
10140 boolean links_frei = FALSE, rechts_frei = FALSE;
10141 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10142 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10143 boolean new_wall = FALSE;
10145 if (IS_ANIMATED(graphic))
10146 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10148 if (!MovDelay[ax][ay]) /* start building new wall */
10149 MovDelay[ax][ay] = 6;
10151 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10153 MovDelay[ax][ay]--;
10154 if (MovDelay[ax][ay])
10158 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10160 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10162 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10164 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10165 rechts_frei = TRUE;
10167 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10168 element == EL_EXPANDABLE_STEELWALL_ANY)
10172 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10173 Store[ax][ay-1] = element;
10174 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10175 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10176 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10177 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10182 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10183 Store[ax][ay+1] = element;
10184 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10185 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10186 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10187 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10192 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10193 element == EL_EXPANDABLE_STEELWALL_ANY)
10197 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10198 Store[ax-1][ay] = element;
10199 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10200 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10201 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10202 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10208 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10209 Store[ax+1][ay] = element;
10210 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10211 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10212 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10213 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10218 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10219 oben_massiv = TRUE;
10220 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10221 unten_massiv = TRUE;
10222 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10223 links_massiv = TRUE;
10224 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10225 rechts_massiv = TRUE;
10227 if (((oben_massiv && unten_massiv) ||
10228 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10229 ((links_massiv && rechts_massiv) ||
10230 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10231 Feld[ax][ay] = EL_STEELWALL;
10234 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10237 void CheckForDragon(int x, int y)
10240 boolean dragon_found = FALSE;
10241 static int xy[4][2] =
10249 for (i = 0; i < NUM_DIRECTIONS; i++)
10251 for (j = 0; j < 4; j++)
10253 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10255 if (IN_LEV_FIELD(xx, yy) &&
10256 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10258 if (Feld[xx][yy] == EL_DRAGON)
10259 dragon_found = TRUE;
10268 for (i = 0; i < NUM_DIRECTIONS; i++)
10270 for (j = 0; j < 3; j++)
10272 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10274 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10276 Feld[xx][yy] = EL_EMPTY;
10277 TEST_DrawLevelField(xx, yy);
10286 static void InitBuggyBase(int x, int y)
10288 int element = Feld[x][y];
10289 int activating_delay = FRAMES_PER_SECOND / 4;
10291 ChangeDelay[x][y] =
10292 (element == EL_SP_BUGGY_BASE ?
10293 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10294 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10296 element == EL_SP_BUGGY_BASE_ACTIVE ?
10297 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10300 static void WarnBuggyBase(int x, int y)
10303 static int xy[4][2] =
10311 for (i = 0; i < NUM_DIRECTIONS; i++)
10313 int xx = x + xy[i][0];
10314 int yy = y + xy[i][1];
10316 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10318 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10325 static void InitTrap(int x, int y)
10327 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10330 static void ActivateTrap(int x, int y)
10332 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10335 static void ChangeActiveTrap(int x, int y)
10337 int graphic = IMG_TRAP_ACTIVE;
10339 /* if new animation frame was drawn, correct crumbled sand border */
10340 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10341 TEST_DrawLevelFieldCrumbledSand(x, y);
10344 static int getSpecialActionElement(int element, int number, int base_element)
10346 return (element != EL_EMPTY ? element :
10347 number != -1 ? base_element + number - 1 :
10351 static int getModifiedActionNumber(int value_old, int operator, int operand,
10352 int value_min, int value_max)
10354 int value_new = (operator == CA_MODE_SET ? operand :
10355 operator == CA_MODE_ADD ? value_old + operand :
10356 operator == CA_MODE_SUBTRACT ? value_old - operand :
10357 operator == CA_MODE_MULTIPLY ? value_old * operand :
10358 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10359 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10362 return (value_new < value_min ? value_min :
10363 value_new > value_max ? value_max :
10367 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10369 struct ElementInfo *ei = &element_info[element];
10370 struct ElementChangeInfo *change = &ei->change_page[page];
10371 int target_element = change->target_element;
10372 int action_type = change->action_type;
10373 int action_mode = change->action_mode;
10374 int action_arg = change->action_arg;
10375 int action_element = change->action_element;
10378 if (!change->has_action)
10381 /* ---------- determine action paramater values -------------------------- */
10383 int level_time_value =
10384 (level.time > 0 ? TimeLeft :
10387 int action_arg_element_raw =
10388 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10389 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10390 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10391 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10392 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10393 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10394 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10396 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10399 if (action_arg_element_raw == EL_GROUP_START)
10400 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10403 int action_arg_direction =
10404 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10405 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10406 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10407 change->actual_trigger_side :
10408 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10409 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10412 int action_arg_number_min =
10413 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10416 int action_arg_number_max =
10417 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10418 action_type == CA_SET_LEVEL_GEMS ? 999 :
10419 action_type == CA_SET_LEVEL_TIME ? 9999 :
10420 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10421 action_type == CA_SET_CE_VALUE ? 9999 :
10422 action_type == CA_SET_CE_SCORE ? 9999 :
10425 int action_arg_number_reset =
10426 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10427 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10428 action_type == CA_SET_LEVEL_TIME ? level.time :
10429 action_type == CA_SET_LEVEL_SCORE ? 0 :
10430 #if USE_NEW_CUSTOM_VALUE
10431 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10433 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10435 action_type == CA_SET_CE_SCORE ? 0 :
10438 int action_arg_number =
10439 (action_arg <= CA_ARG_MAX ? action_arg :
10440 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10441 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10442 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10443 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10444 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10445 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10446 #if USE_NEW_CUSTOM_VALUE
10447 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10449 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10451 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10452 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10453 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10454 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10455 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10456 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10457 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10458 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10459 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10460 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10461 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10462 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10463 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10464 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10467 int action_arg_number_old =
10468 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10469 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10470 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10471 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10472 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10475 int action_arg_number_new =
10476 getModifiedActionNumber(action_arg_number_old,
10477 action_mode, action_arg_number,
10478 action_arg_number_min, action_arg_number_max);
10481 int trigger_player_bits =
10482 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10483 change->actual_trigger_player_bits : change->trigger_player);
10485 int trigger_player_bits =
10486 (change->actual_trigger_player >= EL_PLAYER_1 &&
10487 change->actual_trigger_player <= EL_PLAYER_4 ?
10488 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10492 int action_arg_player_bits =
10493 (action_arg >= CA_ARG_PLAYER_1 &&
10494 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10495 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10496 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10499 /* ---------- execute action -------------------------------------------- */
10501 switch (action_type)
10508 /* ---------- level actions ------------------------------------------- */
10510 case CA_RESTART_LEVEL:
10512 game.restart_level = TRUE;
10517 case CA_SHOW_ENVELOPE:
10519 int element = getSpecialActionElement(action_arg_element,
10520 action_arg_number, EL_ENVELOPE_1);
10522 if (IS_ENVELOPE(element))
10523 local_player->show_envelope = element;
10528 case CA_SET_LEVEL_TIME:
10530 if (level.time > 0) /* only modify limited time value */
10532 TimeLeft = action_arg_number_new;
10535 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10537 DisplayGameControlValues();
10539 DrawGameValue_Time(TimeLeft);
10542 if (!TimeLeft && setup.time_limit)
10543 for (i = 0; i < MAX_PLAYERS; i++)
10544 KillPlayer(&stored_player[i]);
10550 case CA_SET_LEVEL_SCORE:
10552 local_player->score = action_arg_number_new;
10555 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10557 DisplayGameControlValues();
10559 DrawGameValue_Score(local_player->score);
10565 case CA_SET_LEVEL_GEMS:
10567 local_player->gems_still_needed = action_arg_number_new;
10570 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10572 DisplayGameControlValues();
10574 DrawGameValue_Emeralds(local_player->gems_still_needed);
10580 #if !USE_PLAYER_GRAVITY
10581 case CA_SET_LEVEL_GRAVITY:
10583 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10584 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10585 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10591 case CA_SET_LEVEL_WIND:
10593 game.wind_direction = action_arg_direction;
10598 case CA_SET_LEVEL_RANDOM_SEED:
10601 /* ensure that setting a new random seed while playing is predictable */
10602 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10604 InitRND(action_arg_number_new);
10608 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10616 for (i = 0; i < 9; i++)
10617 printf("%d, ", RND(2));
10625 /* ---------- player actions ------------------------------------------ */
10627 case CA_MOVE_PLAYER:
10629 /* automatically move to the next field in specified direction */
10630 for (i = 0; i < MAX_PLAYERS; i++)
10631 if (trigger_player_bits & (1 << i))
10632 stored_player[i].programmed_action = action_arg_direction;
10637 case CA_EXIT_PLAYER:
10639 for (i = 0; i < MAX_PLAYERS; i++)
10640 if (action_arg_player_bits & (1 << i))
10641 PlayerWins(&stored_player[i]);
10646 case CA_KILL_PLAYER:
10648 for (i = 0; i < MAX_PLAYERS; i++)
10649 if (action_arg_player_bits & (1 << i))
10650 KillPlayer(&stored_player[i]);
10655 case CA_SET_PLAYER_KEYS:
10657 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10658 int element = getSpecialActionElement(action_arg_element,
10659 action_arg_number, EL_KEY_1);
10661 if (IS_KEY(element))
10663 for (i = 0; i < MAX_PLAYERS; i++)
10665 if (trigger_player_bits & (1 << i))
10667 stored_player[i].key[KEY_NR(element)] = key_state;
10669 DrawGameDoorValues();
10677 case CA_SET_PLAYER_SPEED:
10680 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10683 for (i = 0; i < MAX_PLAYERS; i++)
10685 if (trigger_player_bits & (1 << i))
10687 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10689 if (action_arg == CA_ARG_SPEED_FASTER &&
10690 stored_player[i].cannot_move)
10692 action_arg_number = STEPSIZE_VERY_SLOW;
10694 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10695 action_arg == CA_ARG_SPEED_FASTER)
10697 action_arg_number = 2;
10698 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10701 else if (action_arg == CA_ARG_NUMBER_RESET)
10703 action_arg_number = level.initial_player_stepsize[i];
10707 getModifiedActionNumber(move_stepsize,
10710 action_arg_number_min,
10711 action_arg_number_max);
10713 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10720 case CA_SET_PLAYER_SHIELD:
10722 for (i = 0; i < MAX_PLAYERS; i++)
10724 if (trigger_player_bits & (1 << i))
10726 if (action_arg == CA_ARG_SHIELD_OFF)
10728 stored_player[i].shield_normal_time_left = 0;
10729 stored_player[i].shield_deadly_time_left = 0;
10731 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10733 stored_player[i].shield_normal_time_left = 999999;
10735 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10737 stored_player[i].shield_normal_time_left = 999999;
10738 stored_player[i].shield_deadly_time_left = 999999;
10746 #if USE_PLAYER_GRAVITY
10747 case CA_SET_PLAYER_GRAVITY:
10749 for (i = 0; i < MAX_PLAYERS; i++)
10751 if (trigger_player_bits & (1 << i))
10753 stored_player[i].gravity =
10754 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10755 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10756 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10757 stored_player[i].gravity);
10765 case CA_SET_PLAYER_ARTWORK:
10767 for (i = 0; i < MAX_PLAYERS; i++)
10769 if (trigger_player_bits & (1 << i))
10771 int artwork_element = action_arg_element;
10773 if (action_arg == CA_ARG_ELEMENT_RESET)
10775 (level.use_artwork_element[i] ? level.artwork_element[i] :
10776 stored_player[i].element_nr);
10778 #if USE_GFX_RESET_PLAYER_ARTWORK
10779 if (stored_player[i].artwork_element != artwork_element)
10780 stored_player[i].Frame = 0;
10783 stored_player[i].artwork_element = artwork_element;
10785 SetPlayerWaiting(&stored_player[i], FALSE);
10787 /* set number of special actions for bored and sleeping animation */
10788 stored_player[i].num_special_action_bored =
10789 get_num_special_action(artwork_element,
10790 ACTION_BORING_1, ACTION_BORING_LAST);
10791 stored_player[i].num_special_action_sleeping =
10792 get_num_special_action(artwork_element,
10793 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10800 case CA_SET_PLAYER_INVENTORY:
10802 for (i = 0; i < MAX_PLAYERS; i++)
10804 struct PlayerInfo *player = &stored_player[i];
10807 if (trigger_player_bits & (1 << i))
10809 int inventory_element = action_arg_element;
10811 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10812 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10813 action_arg == CA_ARG_ELEMENT_ACTION)
10815 int element = inventory_element;
10816 int collect_count = element_info[element].collect_count_initial;
10818 if (!IS_CUSTOM_ELEMENT(element))
10821 if (collect_count == 0)
10822 player->inventory_infinite_element = element;
10824 for (k = 0; k < collect_count; k++)
10825 if (player->inventory_size < MAX_INVENTORY_SIZE)
10826 player->inventory_element[player->inventory_size++] =
10829 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10830 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10831 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10833 if (player->inventory_infinite_element != EL_UNDEFINED &&
10834 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10835 action_arg_element_raw))
10836 player->inventory_infinite_element = EL_UNDEFINED;
10838 for (k = 0, j = 0; j < player->inventory_size; j++)
10840 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10841 action_arg_element_raw))
10842 player->inventory_element[k++] = player->inventory_element[j];
10845 player->inventory_size = k;
10847 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10849 if (player->inventory_size > 0)
10851 for (j = 0; j < player->inventory_size - 1; j++)
10852 player->inventory_element[j] = player->inventory_element[j + 1];
10854 player->inventory_size--;
10857 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10859 if (player->inventory_size > 0)
10860 player->inventory_size--;
10862 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10864 player->inventory_infinite_element = EL_UNDEFINED;
10865 player->inventory_size = 0;
10867 else if (action_arg == CA_ARG_INVENTORY_RESET)
10869 player->inventory_infinite_element = EL_UNDEFINED;
10870 player->inventory_size = 0;
10872 if (level.use_initial_inventory[i])
10874 for (j = 0; j < level.initial_inventory_size[i]; j++)
10876 int element = level.initial_inventory_content[i][j];
10877 int collect_count = element_info[element].collect_count_initial;
10879 if (!IS_CUSTOM_ELEMENT(element))
10882 if (collect_count == 0)
10883 player->inventory_infinite_element = element;
10885 for (k = 0; k < collect_count; k++)
10886 if (player->inventory_size < MAX_INVENTORY_SIZE)
10887 player->inventory_element[player->inventory_size++] =
10898 /* ---------- CE actions ---------------------------------------------- */
10900 case CA_SET_CE_VALUE:
10902 #if USE_NEW_CUSTOM_VALUE
10903 int last_ce_value = CustomValue[x][y];
10905 CustomValue[x][y] = action_arg_number_new;
10907 if (CustomValue[x][y] != last_ce_value)
10909 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10910 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10912 if (CustomValue[x][y] == 0)
10914 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10915 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10923 case CA_SET_CE_SCORE:
10925 #if USE_NEW_CUSTOM_VALUE
10926 int last_ce_score = ei->collect_score;
10928 ei->collect_score = action_arg_number_new;
10930 if (ei->collect_score != last_ce_score)
10932 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10933 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10935 if (ei->collect_score == 0)
10939 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10940 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10943 This is a very special case that seems to be a mixture between
10944 CheckElementChange() and CheckTriggeredElementChange(): while
10945 the first one only affects single elements that are triggered
10946 directly, the second one affects multiple elements in the playfield
10947 that are triggered indirectly by another element. This is a third
10948 case: Changing the CE score always affects multiple identical CEs,
10949 so every affected CE must be checked, not only the single CE for
10950 which the CE score was changed in the first place (as every instance
10951 of that CE shares the same CE score, and therefore also can change)!
10953 SCAN_PLAYFIELD(xx, yy)
10955 if (Feld[xx][yy] == element)
10956 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10957 CE_SCORE_GETS_ZERO);
10966 case CA_SET_CE_ARTWORK:
10968 int artwork_element = action_arg_element;
10969 boolean reset_frame = FALSE;
10972 if (action_arg == CA_ARG_ELEMENT_RESET)
10973 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10976 if (ei->gfx_element != artwork_element)
10977 reset_frame = TRUE;
10979 ei->gfx_element = artwork_element;
10981 SCAN_PLAYFIELD(xx, yy)
10983 if (Feld[xx][yy] == element)
10987 ResetGfxAnimation(xx, yy);
10988 ResetRandomAnimationValue(xx, yy);
10991 TEST_DrawLevelField(xx, yy);
10998 /* ---------- engine actions ------------------------------------------ */
11000 case CA_SET_ENGINE_SCAN_MODE:
11002 InitPlayfieldScanMode(action_arg);
11012 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11014 int old_element = Feld[x][y];
11015 int new_element = GetElementFromGroupElement(element);
11016 int previous_move_direction = MovDir[x][y];
11017 #if USE_NEW_CUSTOM_VALUE
11018 int last_ce_value = CustomValue[x][y];
11020 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11021 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11022 boolean add_player_onto_element = (new_element_is_player &&
11023 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11024 /* this breaks SnakeBite when a snake is
11025 halfway through a door that closes */
11026 /* NOW FIXED AT LEVEL INIT IN files.c */
11027 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11029 IS_WALKABLE(old_element));
11032 /* check if element under the player changes from accessible to unaccessible
11033 (needed for special case of dropping element which then changes) */
11034 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11035 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11043 if (!add_player_onto_element)
11045 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11046 RemoveMovingField(x, y);
11050 Feld[x][y] = new_element;
11052 #if !USE_GFX_RESET_GFX_ANIMATION
11053 ResetGfxAnimation(x, y);
11054 ResetRandomAnimationValue(x, y);
11057 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11058 MovDir[x][y] = previous_move_direction;
11060 #if USE_NEW_CUSTOM_VALUE
11061 if (element_info[new_element].use_last_ce_value)
11062 CustomValue[x][y] = last_ce_value;
11065 InitField_WithBug1(x, y, FALSE);
11067 new_element = Feld[x][y]; /* element may have changed */
11069 #if USE_GFX_RESET_GFX_ANIMATION
11070 ResetGfxAnimation(x, y);
11071 ResetRandomAnimationValue(x, y);
11074 TEST_DrawLevelField(x, y);
11076 if (GFX_CRUMBLED(new_element))
11077 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11081 /* check if element under the player changes from accessible to unaccessible
11082 (needed for special case of dropping element which then changes) */
11083 /* (must be checked after creating new element for walkable group elements) */
11084 #if USE_FIX_KILLED_BY_NON_WALKABLE
11085 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11086 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11093 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11094 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11103 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11104 if (new_element_is_player)
11105 RelocatePlayer(x, y, new_element);
11108 ChangeCount[x][y]++; /* count number of changes in the same frame */
11110 TestIfBadThingTouchesPlayer(x, y);
11111 TestIfPlayerTouchesCustomElement(x, y);
11112 TestIfElementTouchesCustomElement(x, y);
11115 static void CreateField(int x, int y, int element)
11117 CreateFieldExt(x, y, element, FALSE);
11120 static void CreateElementFromChange(int x, int y, int element)
11122 element = GET_VALID_RUNTIME_ELEMENT(element);
11124 #if USE_STOP_CHANGED_ELEMENTS
11125 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11127 int old_element = Feld[x][y];
11129 /* prevent changed element from moving in same engine frame
11130 unless both old and new element can either fall or move */
11131 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11132 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11137 CreateFieldExt(x, y, element, TRUE);
11140 static boolean ChangeElement(int x, int y, int element, int page)
11142 struct ElementInfo *ei = &element_info[element];
11143 struct ElementChangeInfo *change = &ei->change_page[page];
11144 int ce_value = CustomValue[x][y];
11145 int ce_score = ei->collect_score;
11146 int target_element;
11147 int old_element = Feld[x][y];
11149 /* always use default change event to prevent running into a loop */
11150 if (ChangeEvent[x][y] == -1)
11151 ChangeEvent[x][y] = CE_DELAY;
11153 if (ChangeEvent[x][y] == CE_DELAY)
11155 /* reset actual trigger element, trigger player and action element */
11156 change->actual_trigger_element = EL_EMPTY;
11157 change->actual_trigger_player = EL_EMPTY;
11158 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11159 change->actual_trigger_side = CH_SIDE_NONE;
11160 change->actual_trigger_ce_value = 0;
11161 change->actual_trigger_ce_score = 0;
11164 /* do not change elements more than a specified maximum number of changes */
11165 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11168 ChangeCount[x][y]++; /* count number of changes in the same frame */
11170 if (change->explode)
11177 if (change->use_target_content)
11179 boolean complete_replace = TRUE;
11180 boolean can_replace[3][3];
11183 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11186 boolean is_walkable;
11187 boolean is_diggable;
11188 boolean is_collectible;
11189 boolean is_removable;
11190 boolean is_destructible;
11191 int ex = x + xx - 1;
11192 int ey = y + yy - 1;
11193 int content_element = change->target_content.e[xx][yy];
11196 can_replace[xx][yy] = TRUE;
11198 if (ex == x && ey == y) /* do not check changing element itself */
11201 if (content_element == EL_EMPTY_SPACE)
11203 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11208 if (!IN_LEV_FIELD(ex, ey))
11210 can_replace[xx][yy] = FALSE;
11211 complete_replace = FALSE;
11218 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11219 e = MovingOrBlocked2Element(ex, ey);
11221 is_empty = (IS_FREE(ex, ey) ||
11222 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11224 is_walkable = (is_empty || IS_WALKABLE(e));
11225 is_diggable = (is_empty || IS_DIGGABLE(e));
11226 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11227 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11228 is_removable = (is_diggable || is_collectible);
11230 can_replace[xx][yy] =
11231 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11232 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11233 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11234 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11235 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11236 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11237 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11239 if (!can_replace[xx][yy])
11240 complete_replace = FALSE;
11243 if (!change->only_if_complete || complete_replace)
11245 boolean something_has_changed = FALSE;
11247 if (change->only_if_complete && change->use_random_replace &&
11248 RND(100) < change->random_percentage)
11251 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11253 int ex = x + xx - 1;
11254 int ey = y + yy - 1;
11255 int content_element;
11257 if (can_replace[xx][yy] && (!change->use_random_replace ||
11258 RND(100) < change->random_percentage))
11260 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11261 RemoveMovingField(ex, ey);
11263 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11265 content_element = change->target_content.e[xx][yy];
11266 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11267 ce_value, ce_score);
11269 CreateElementFromChange(ex, ey, target_element);
11271 something_has_changed = TRUE;
11273 /* for symmetry reasons, freeze newly created border elements */
11274 if (ex != x || ey != y)
11275 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11279 if (something_has_changed)
11281 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11282 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11288 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11289 ce_value, ce_score);
11291 if (element == EL_DIAGONAL_GROWING ||
11292 element == EL_DIAGONAL_SHRINKING)
11294 target_element = Store[x][y];
11296 Store[x][y] = EL_EMPTY;
11299 CreateElementFromChange(x, y, target_element);
11301 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11302 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11305 /* this uses direct change before indirect change */
11306 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11311 #if USE_NEW_DELAYED_ACTION
11313 static void HandleElementChange(int x, int y, int page)
11315 int element = MovingOrBlocked2Element(x, y);
11316 struct ElementInfo *ei = &element_info[element];
11317 struct ElementChangeInfo *change = &ei->change_page[page];
11318 boolean handle_action_before_change = FALSE;
11321 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11322 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11325 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11326 x, y, element, element_info[element].token_name);
11327 printf("HandleElementChange(): This should never happen!\n");
11332 /* this can happen with classic bombs on walkable, changing elements */
11333 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11336 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11337 ChangeDelay[x][y] = 0;
11343 if (ChangeDelay[x][y] == 0) /* initialize element change */
11345 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11347 if (change->can_change)
11350 /* !!! not clear why graphic animation should be reset at all here !!! */
11351 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11352 #if USE_GFX_RESET_WHEN_NOT_MOVING
11353 /* when a custom element is about to change (for example by change delay),
11354 do not reset graphic animation when the custom element is moving */
11355 if (!IS_MOVING(x, y))
11358 ResetGfxAnimation(x, y);
11359 ResetRandomAnimationValue(x, y);
11363 if (change->pre_change_function)
11364 change->pre_change_function(x, y);
11368 ChangeDelay[x][y]--;
11370 if (ChangeDelay[x][y] != 0) /* continue element change */
11372 if (change->can_change)
11374 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11376 if (IS_ANIMATED(graphic))
11377 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11379 if (change->change_function)
11380 change->change_function(x, y);
11383 else /* finish element change */
11385 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11387 page = ChangePage[x][y];
11388 ChangePage[x][y] = -1;
11390 change = &ei->change_page[page];
11393 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11395 ChangeDelay[x][y] = 1; /* try change after next move step */
11396 ChangePage[x][y] = page; /* remember page to use for change */
11402 /* special case: set new level random seed before changing element */
11403 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11404 handle_action_before_change = TRUE;
11406 if (change->has_action && handle_action_before_change)
11407 ExecuteCustomElementAction(x, y, element, page);
11410 if (change->can_change)
11412 if (ChangeElement(x, y, element, page))
11414 if (change->post_change_function)
11415 change->post_change_function(x, y);
11419 if (change->has_action && !handle_action_before_change)
11420 ExecuteCustomElementAction(x, y, element, page);
11426 static void HandleElementChange(int x, int y, int page)
11428 int element = MovingOrBlocked2Element(x, y);
11429 struct ElementInfo *ei = &element_info[element];
11430 struct ElementChangeInfo *change = &ei->change_page[page];
11433 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11436 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11437 x, y, element, element_info[element].token_name);
11438 printf("HandleElementChange(): This should never happen!\n");
11443 /* this can happen with classic bombs on walkable, changing elements */
11444 if (!CAN_CHANGE(element))
11447 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11448 ChangeDelay[x][y] = 0;
11454 if (ChangeDelay[x][y] == 0) /* initialize element change */
11456 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11458 ResetGfxAnimation(x, y);
11459 ResetRandomAnimationValue(x, y);
11461 if (change->pre_change_function)
11462 change->pre_change_function(x, y);
11465 ChangeDelay[x][y]--;
11467 if (ChangeDelay[x][y] != 0) /* continue element change */
11469 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11471 if (IS_ANIMATED(graphic))
11472 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11474 if (change->change_function)
11475 change->change_function(x, y);
11477 else /* finish element change */
11479 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11481 page = ChangePage[x][y];
11482 ChangePage[x][y] = -1;
11484 change = &ei->change_page[page];
11487 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11489 ChangeDelay[x][y] = 1; /* try change after next move step */
11490 ChangePage[x][y] = page; /* remember page to use for change */
11495 if (ChangeElement(x, y, element, page))
11497 if (change->post_change_function)
11498 change->post_change_function(x, y);
11505 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11506 int trigger_element,
11508 int trigger_player,
11512 boolean change_done_any = FALSE;
11513 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11516 if (!(trigger_events[trigger_element][trigger_event]))
11520 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11521 trigger_event, recursion_loop_depth, recursion_loop_detected,
11522 recursion_loop_element, EL_NAME(recursion_loop_element));
11525 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11529 int element = EL_CUSTOM_START + i;
11530 boolean change_done = FALSE;
11533 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11534 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11537 for (p = 0; p < element_info[element].num_change_pages; p++)
11539 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11541 if (change->can_change_or_has_action &&
11542 change->has_event[trigger_event] &&
11543 change->trigger_side & trigger_side &&
11544 change->trigger_player & trigger_player &&
11545 change->trigger_page & trigger_page_bits &&
11546 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11548 change->actual_trigger_element = trigger_element;
11549 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11550 change->actual_trigger_player_bits = trigger_player;
11551 change->actual_trigger_side = trigger_side;
11552 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11553 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11556 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11557 element, EL_NAME(element), p);
11560 if ((change->can_change && !change_done) || change->has_action)
11564 SCAN_PLAYFIELD(x, y)
11566 if (Feld[x][y] == element)
11568 if (change->can_change && !change_done)
11570 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11571 /* if element already changed in this frame, not only prevent
11572 another element change (checked in ChangeElement()), but
11573 also prevent additional element actions for this element */
11575 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11576 !level.use_action_after_change_bug)
11581 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11582 element, EL_NAME(element), p);
11585 ChangeDelay[x][y] = 1;
11586 ChangeEvent[x][y] = trigger_event;
11588 HandleElementChange(x, y, p);
11590 #if USE_NEW_DELAYED_ACTION
11591 else if (change->has_action)
11593 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11594 /* if element already changed in this frame, not only prevent
11595 another element change (checked in ChangeElement()), but
11596 also prevent additional element actions for this element */
11598 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11599 !level.use_action_after_change_bug)
11605 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11606 element, EL_NAME(element), p);
11609 ExecuteCustomElementAction(x, y, element, p);
11610 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11613 if (change->has_action)
11615 ExecuteCustomElementAction(x, y, element, p);
11616 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11622 if (change->can_change)
11624 change_done = TRUE;
11625 change_done_any = TRUE;
11628 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11629 element, EL_NAME(element), p);
11638 RECURSION_LOOP_DETECTION_END();
11640 return change_done_any;
11643 static boolean CheckElementChangeExt(int x, int y,
11645 int trigger_element,
11647 int trigger_player,
11650 boolean change_done = FALSE;
11653 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11654 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11657 if (Feld[x][y] == EL_BLOCKED)
11659 Blocked2Moving(x, y, &x, &y);
11660 element = Feld[x][y];
11664 /* check if element has already changed */
11665 if (Feld[x][y] != element)
11668 /* check if element has already changed or is about to change after moving */
11669 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11670 Feld[x][y] != element) ||
11672 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11673 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11674 ChangePage[x][y] != -1)))
11679 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11680 trigger_event, recursion_loop_depth, recursion_loop_detected,
11681 recursion_loop_element, EL_NAME(recursion_loop_element));
11684 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11687 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11690 for (p = 0; p < element_info[element].num_change_pages; p++)
11692 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11694 /* check trigger element for all events where the element that is checked
11695 for changing interacts with a directly adjacent element -- this is
11696 different to element changes that affect other elements to change on the
11697 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11698 boolean check_trigger_element =
11699 (trigger_event == CE_TOUCHING_X ||
11700 trigger_event == CE_HITTING_X ||
11701 trigger_event == CE_HIT_BY_X ||
11703 /* this one was forgotten until 3.2.3 */
11704 trigger_event == CE_DIGGING_X);
11707 if (change->can_change_or_has_action &&
11708 change->has_event[trigger_event] &&
11709 change->trigger_side & trigger_side &&
11710 change->trigger_player & trigger_player &&
11711 (!check_trigger_element ||
11712 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11714 change->actual_trigger_element = trigger_element;
11715 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11716 change->actual_trigger_player_bits = trigger_player;
11717 change->actual_trigger_side = trigger_side;
11718 change->actual_trigger_ce_value = CustomValue[x][y];
11719 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11721 /* special case: trigger element not at (x,y) position for some events */
11722 if (check_trigger_element)
11734 { 0, 0 }, { 0, 0 }, { 0, 0 },
11738 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11739 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11741 change->actual_trigger_ce_value = CustomValue[xx][yy];
11742 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11745 if (change->can_change && !change_done)
11747 ChangeDelay[x][y] = 1;
11748 ChangeEvent[x][y] = trigger_event;
11750 HandleElementChange(x, y, p);
11752 change_done = TRUE;
11754 #if USE_NEW_DELAYED_ACTION
11755 else if (change->has_action)
11757 ExecuteCustomElementAction(x, y, element, p);
11758 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11761 if (change->has_action)
11763 ExecuteCustomElementAction(x, y, element, p);
11764 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11770 RECURSION_LOOP_DETECTION_END();
11772 return change_done;
11775 static void PlayPlayerSound(struct PlayerInfo *player)
11777 int jx = player->jx, jy = player->jy;
11778 int sound_element = player->artwork_element;
11779 int last_action = player->last_action_waiting;
11780 int action = player->action_waiting;
11782 if (player->is_waiting)
11784 if (action != last_action)
11785 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11787 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11791 if (action != last_action)
11792 StopSound(element_info[sound_element].sound[last_action]);
11794 if (last_action == ACTION_SLEEPING)
11795 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11799 static void PlayAllPlayersSound()
11803 for (i = 0; i < MAX_PLAYERS; i++)
11804 if (stored_player[i].active)
11805 PlayPlayerSound(&stored_player[i]);
11808 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11810 boolean last_waiting = player->is_waiting;
11811 int move_dir = player->MovDir;
11813 player->dir_waiting = move_dir;
11814 player->last_action_waiting = player->action_waiting;
11818 if (!last_waiting) /* not waiting -> waiting */
11820 player->is_waiting = TRUE;
11822 player->frame_counter_bored =
11824 game.player_boring_delay_fixed +
11825 GetSimpleRandom(game.player_boring_delay_random);
11826 player->frame_counter_sleeping =
11828 game.player_sleeping_delay_fixed +
11829 GetSimpleRandom(game.player_sleeping_delay_random);
11831 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11834 if (game.player_sleeping_delay_fixed +
11835 game.player_sleeping_delay_random > 0 &&
11836 player->anim_delay_counter == 0 &&
11837 player->post_delay_counter == 0 &&
11838 FrameCounter >= player->frame_counter_sleeping)
11839 player->is_sleeping = TRUE;
11840 else if (game.player_boring_delay_fixed +
11841 game.player_boring_delay_random > 0 &&
11842 FrameCounter >= player->frame_counter_bored)
11843 player->is_bored = TRUE;
11845 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11846 player->is_bored ? ACTION_BORING :
11849 if (player->is_sleeping && player->use_murphy)
11851 /* special case for sleeping Murphy when leaning against non-free tile */
11853 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11854 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11855 !IS_MOVING(player->jx - 1, player->jy)))
11856 move_dir = MV_LEFT;
11857 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11858 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11859 !IS_MOVING(player->jx + 1, player->jy)))
11860 move_dir = MV_RIGHT;
11862 player->is_sleeping = FALSE;
11864 player->dir_waiting = move_dir;
11867 if (player->is_sleeping)
11869 if (player->num_special_action_sleeping > 0)
11871 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11873 int last_special_action = player->special_action_sleeping;
11874 int num_special_action = player->num_special_action_sleeping;
11875 int special_action =
11876 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11877 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11878 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11879 last_special_action + 1 : ACTION_SLEEPING);
11880 int special_graphic =
11881 el_act_dir2img(player->artwork_element, special_action, move_dir);
11883 player->anim_delay_counter =
11884 graphic_info[special_graphic].anim_delay_fixed +
11885 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11886 player->post_delay_counter =
11887 graphic_info[special_graphic].post_delay_fixed +
11888 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11890 player->special_action_sleeping = special_action;
11893 if (player->anim_delay_counter > 0)
11895 player->action_waiting = player->special_action_sleeping;
11896 player->anim_delay_counter--;
11898 else if (player->post_delay_counter > 0)
11900 player->post_delay_counter--;
11904 else if (player->is_bored)
11906 if (player->num_special_action_bored > 0)
11908 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11910 int special_action =
11911 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11912 int special_graphic =
11913 el_act_dir2img(player->artwork_element, special_action, move_dir);
11915 player->anim_delay_counter =
11916 graphic_info[special_graphic].anim_delay_fixed +
11917 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11918 player->post_delay_counter =
11919 graphic_info[special_graphic].post_delay_fixed +
11920 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11922 player->special_action_bored = special_action;
11925 if (player->anim_delay_counter > 0)
11927 player->action_waiting = player->special_action_bored;
11928 player->anim_delay_counter--;
11930 else if (player->post_delay_counter > 0)
11932 player->post_delay_counter--;
11937 else if (last_waiting) /* waiting -> not waiting */
11939 player->is_waiting = FALSE;
11940 player->is_bored = FALSE;
11941 player->is_sleeping = FALSE;
11943 player->frame_counter_bored = -1;
11944 player->frame_counter_sleeping = -1;
11946 player->anim_delay_counter = 0;
11947 player->post_delay_counter = 0;
11949 player->dir_waiting = player->MovDir;
11950 player->action_waiting = ACTION_DEFAULT;
11952 player->special_action_bored = ACTION_DEFAULT;
11953 player->special_action_sleeping = ACTION_DEFAULT;
11957 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11959 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11960 int left = player_action & JOY_LEFT;
11961 int right = player_action & JOY_RIGHT;
11962 int up = player_action & JOY_UP;
11963 int down = player_action & JOY_DOWN;
11964 int button1 = player_action & JOY_BUTTON_1;
11965 int button2 = player_action & JOY_BUTTON_2;
11966 int dx = (left ? -1 : right ? 1 : 0);
11967 int dy = (up ? -1 : down ? 1 : 0);
11969 if (!player->active || tape.pausing)
11975 snapped = SnapField(player, dx, dy);
11979 dropped = DropElement(player);
11981 moved = MovePlayer(player, dx, dy);
11984 if (tape.single_step && tape.recording && !tape.pausing)
11987 /* as it is called "single step mode", just return to pause mode when the
11988 player stopped moving after one tile (or never starts moving at all) */
11989 if (!player->is_moving)
11991 /* this is buggy: there are quite some cases where the single step mode
11992 does not return to pause mode (like pushing things that don't move
11993 or simply by trying to run against a wall) */
11994 if (button1 || (dropped && !moved))
11997 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11998 SnapField(player, 0, 0); /* stop snapping */
12002 SetPlayerWaiting(player, FALSE);
12004 return player_action;
12008 /* no actions for this player (no input at player's configured device) */
12010 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12011 SnapField(player, 0, 0);
12012 CheckGravityMovementWhenNotMoving(player);
12014 if (player->MovPos == 0)
12015 SetPlayerWaiting(player, TRUE);
12017 if (player->MovPos == 0) /* needed for tape.playing */
12018 player->is_moving = FALSE;
12020 player->is_dropping = FALSE;
12021 player->is_dropping_pressed = FALSE;
12022 player->drop_pressed_delay = 0;
12028 static void CheckLevelTime()
12032 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12033 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12035 if (level.native_em_level->lev->home == 0) /* all players at home */
12037 PlayerWins(local_player);
12039 AllPlayersGone = TRUE;
12041 level.native_em_level->lev->home = -1;
12044 if (level.native_em_level->ply[0]->alive == 0 &&
12045 level.native_em_level->ply[1]->alive == 0 &&
12046 level.native_em_level->ply[2]->alive == 0 &&
12047 level.native_em_level->ply[3]->alive == 0) /* all dead */
12048 AllPlayersGone = TRUE;
12050 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12052 if (game_sp_info.LevelSolved &&
12053 !game_sp_info.GameOver) /* game won */
12055 PlayerWins(local_player);
12057 game_sp_info.GameOver = TRUE;
12059 AllPlayersGone = TRUE;
12062 if (game_sp_info.GameOver) /* game lost */
12063 AllPlayersGone = TRUE;
12066 if (TimeFrames >= FRAMES_PER_SECOND)
12071 for (i = 0; i < MAX_PLAYERS; i++)
12073 struct PlayerInfo *player = &stored_player[i];
12075 if (SHIELD_ON(player))
12077 player->shield_normal_time_left--;
12079 if (player->shield_deadly_time_left > 0)
12080 player->shield_deadly_time_left--;
12084 if (!local_player->LevelSolved && !level.use_step_counter)
12092 if (TimeLeft <= 10 && setup.time_limit)
12093 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12096 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12098 DisplayGameControlValues();
12100 DrawGameValue_Time(TimeLeft);
12103 if (!TimeLeft && setup.time_limit)
12105 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12106 level.native_em_level->lev->killed_out_of_time = TRUE;
12108 for (i = 0; i < MAX_PLAYERS; i++)
12109 KillPlayer(&stored_player[i]);
12113 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12115 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12117 DisplayGameControlValues();
12120 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12121 DrawGameValue_Time(TimePlayed);
12124 level.native_em_level->lev->time =
12125 (level.time == 0 ? TimePlayed : TimeLeft);
12128 if (tape.recording || tape.playing)
12129 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12133 UpdateAndDisplayGameControlValues();
12135 UpdateGameDoorValues();
12136 DrawGameDoorValues();
12140 void AdvanceFrameAndPlayerCounters(int player_nr)
12144 /* advance frame counters (global frame counter and time frame counter) */
12148 /* advance player counters (counters for move delay, move animation etc.) */
12149 for (i = 0; i < MAX_PLAYERS; i++)
12151 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12152 int move_delay_value = stored_player[i].move_delay_value;
12153 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12155 if (!advance_player_counters) /* not all players may be affected */
12158 #if USE_NEW_PLAYER_ANIM
12159 if (move_frames == 0) /* less than one move per game frame */
12161 int stepsize = TILEX / move_delay_value;
12162 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12163 int count = (stored_player[i].is_moving ?
12164 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12166 if (count % delay == 0)
12171 stored_player[i].Frame += move_frames;
12173 if (stored_player[i].MovPos != 0)
12174 stored_player[i].StepFrame += move_frames;
12176 if (stored_player[i].move_delay > 0)
12177 stored_player[i].move_delay--;
12179 /* due to bugs in previous versions, counter must count up, not down */
12180 if (stored_player[i].push_delay != -1)
12181 stored_player[i].push_delay++;
12183 if (stored_player[i].drop_delay > 0)
12184 stored_player[i].drop_delay--;
12186 if (stored_player[i].is_dropping_pressed)
12187 stored_player[i].drop_pressed_delay++;
12191 void StartGameActions(boolean init_network_game, boolean record_tape,
12194 unsigned long new_random_seed = InitRND(random_seed);
12197 TapeStartRecording(new_random_seed);
12199 #if defined(NETWORK_AVALIABLE)
12200 if (init_network_game)
12202 SendToServer_StartPlaying();
12213 static unsigned long game_frame_delay = 0;
12214 unsigned long game_frame_delay_value;
12215 byte *recorded_player_action;
12216 byte summarized_player_action = 0;
12217 byte tape_action[MAX_PLAYERS];
12220 /* detect endless loops, caused by custom element programming */
12221 if (recursion_loop_detected && recursion_loop_depth == 0)
12223 char *message = getStringCat3("Internal Error ! Element ",
12224 EL_NAME(recursion_loop_element),
12225 " caused endless loop ! Quit the game ?");
12227 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12228 EL_NAME(recursion_loop_element));
12230 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12232 recursion_loop_detected = FALSE; /* if game should be continued */
12239 if (game.restart_level)
12240 StartGameActions(options.network, setup.autorecord, level.random_seed);
12242 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12243 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12245 if (level.native_em_level->lev->home == 0) /* all players at home */
12247 PlayerWins(local_player);
12249 AllPlayersGone = TRUE;
12251 level.native_em_level->lev->home = -1;
12254 if (level.native_em_level->ply[0]->alive == 0 &&
12255 level.native_em_level->ply[1]->alive == 0 &&
12256 level.native_em_level->ply[2]->alive == 0 &&
12257 level.native_em_level->ply[3]->alive == 0) /* all dead */
12258 AllPlayersGone = TRUE;
12260 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12262 if (game_sp_info.LevelSolved &&
12263 !game_sp_info.GameOver) /* game won */
12265 PlayerWins(local_player);
12267 game_sp_info.GameOver = TRUE;
12269 AllPlayersGone = TRUE;
12272 if (game_sp_info.GameOver) /* game lost */
12273 AllPlayersGone = TRUE;
12276 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12279 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12282 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12285 game_frame_delay_value =
12286 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12288 if (tape.playing && tape.warp_forward && !tape.pausing)
12289 game_frame_delay_value = 0;
12291 /* ---------- main game synchronization point ---------- */
12293 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12295 if (network_playing && !network_player_action_received)
12297 /* try to get network player actions in time */
12299 #if defined(NETWORK_AVALIABLE)
12300 /* last chance to get network player actions without main loop delay */
12301 HandleNetworking();
12304 /* game was quit by network peer */
12305 if (game_status != GAME_MODE_PLAYING)
12308 if (!network_player_action_received)
12309 return; /* failed to get network player actions in time */
12311 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12317 /* at this point we know that we really continue executing the game */
12319 network_player_action_received = FALSE;
12321 /* when playing tape, read previously recorded player input from tape data */
12322 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12325 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12330 if (tape.set_centered_player)
12332 game.centered_player_nr_next = tape.centered_player_nr_next;
12333 game.set_centered_player = TRUE;
12336 for (i = 0; i < MAX_PLAYERS; i++)
12338 summarized_player_action |= stored_player[i].action;
12340 if (!network_playing)
12341 stored_player[i].effective_action = stored_player[i].action;
12344 #if defined(NETWORK_AVALIABLE)
12345 if (network_playing)
12346 SendToServer_MovePlayer(summarized_player_action);
12349 if (!options.network && !setup.team_mode)
12350 local_player->effective_action = summarized_player_action;
12352 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12354 for (i = 0; i < MAX_PLAYERS; i++)
12355 stored_player[i].effective_action =
12356 (i == game.centered_player_nr ? summarized_player_action : 0);
12359 if (recorded_player_action != NULL)
12360 for (i = 0; i < MAX_PLAYERS; i++)
12361 stored_player[i].effective_action = recorded_player_action[i];
12363 for (i = 0; i < MAX_PLAYERS; i++)
12365 tape_action[i] = stored_player[i].effective_action;
12367 /* (this can only happen in the R'n'D game engine) */
12368 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12369 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12372 /* only record actions from input devices, but not programmed actions */
12373 if (tape.recording)
12374 TapeRecordAction(tape_action);
12376 #if USE_NEW_PLAYER_ASSIGNMENTS
12378 byte mapped_action[MAX_PLAYERS];
12380 for (i = 0; i < MAX_PLAYERS; i++)
12381 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12383 for (i = 0; i < MAX_PLAYERS; i++)
12384 stored_player[i].effective_action = mapped_action[i];
12388 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12390 GameActions_EM_Main();
12392 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12394 GameActions_SP_Main();
12402 void GameActions_EM_Main()
12404 byte effective_action[MAX_PLAYERS];
12405 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12408 for (i = 0; i < MAX_PLAYERS; i++)
12409 effective_action[i] = stored_player[i].effective_action;
12411 GameActions_EM(effective_action, warp_mode);
12415 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12418 void GameActions_SP_Main()
12420 byte effective_action[MAX_PLAYERS];
12421 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12424 for (i = 0; i < MAX_PLAYERS; i++)
12425 effective_action[i] = stored_player[i].effective_action;
12427 GameActions_SP(effective_action, warp_mode);
12431 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12434 void GameActions_RND()
12436 int magic_wall_x = 0, magic_wall_y = 0;
12437 int i, x, y, element, graphic;
12439 InitPlayfieldScanModeVars();
12441 #if USE_ONE_MORE_CHANGE_PER_FRAME
12442 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12444 SCAN_PLAYFIELD(x, y)
12446 ChangeCount[x][y] = 0;
12447 ChangeEvent[x][y] = -1;
12452 if (game.set_centered_player)
12454 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12456 /* switching to "all players" only possible if all players fit to screen */
12457 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12459 game.centered_player_nr_next = game.centered_player_nr;
12460 game.set_centered_player = FALSE;
12463 /* do not switch focus to non-existing (or non-active) player */
12464 if (game.centered_player_nr_next >= 0 &&
12465 !stored_player[game.centered_player_nr_next].active)
12467 game.centered_player_nr_next = game.centered_player_nr;
12468 game.set_centered_player = FALSE;
12472 if (game.set_centered_player &&
12473 ScreenMovPos == 0) /* screen currently aligned at tile position */
12477 if (game.centered_player_nr_next == -1)
12479 setScreenCenteredToAllPlayers(&sx, &sy);
12483 sx = stored_player[game.centered_player_nr_next].jx;
12484 sy = stored_player[game.centered_player_nr_next].jy;
12487 game.centered_player_nr = game.centered_player_nr_next;
12488 game.set_centered_player = FALSE;
12490 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12491 DrawGameDoorValues();
12494 for (i = 0; i < MAX_PLAYERS; i++)
12496 int actual_player_action = stored_player[i].effective_action;
12499 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12500 - rnd_equinox_tetrachloride 048
12501 - rnd_equinox_tetrachloride_ii 096
12502 - rnd_emanuel_schmieg 002
12503 - doctor_sloan_ww 001, 020
12505 if (stored_player[i].MovPos == 0)
12506 CheckGravityMovement(&stored_player[i]);
12509 /* overwrite programmed action with tape action */
12510 if (stored_player[i].programmed_action)
12511 actual_player_action = stored_player[i].programmed_action;
12513 PlayerActions(&stored_player[i], actual_player_action);
12515 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12518 ScrollScreen(NULL, SCROLL_GO_ON);
12520 /* for backwards compatibility, the following code emulates a fixed bug that
12521 occured when pushing elements (causing elements that just made their last
12522 pushing step to already (if possible) make their first falling step in the
12523 same game frame, which is bad); this code is also needed to use the famous
12524 "spring push bug" which is used in older levels and might be wanted to be
12525 used also in newer levels, but in this case the buggy pushing code is only
12526 affecting the "spring" element and no other elements */
12528 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12530 for (i = 0; i < MAX_PLAYERS; i++)
12532 struct PlayerInfo *player = &stored_player[i];
12533 int x = player->jx;
12534 int y = player->jy;
12536 if (player->active && player->is_pushing && player->is_moving &&
12538 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12539 Feld[x][y] == EL_SPRING))
12541 ContinueMoving(x, y);
12543 /* continue moving after pushing (this is actually a bug) */
12544 if (!IS_MOVING(x, y))
12545 Stop[x][y] = FALSE;
12551 debug_print_timestamp(0, "start main loop profiling");
12554 SCAN_PLAYFIELD(x, y)
12556 ChangeCount[x][y] = 0;
12557 ChangeEvent[x][y] = -1;
12559 /* this must be handled before main playfield loop */
12560 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12563 if (MovDelay[x][y] <= 0)
12567 #if USE_NEW_SNAP_DELAY
12568 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12571 if (MovDelay[x][y] <= 0)
12574 TEST_DrawLevelField(x, y);
12576 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12582 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12584 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12585 printf("GameActions(): This should never happen!\n");
12587 ChangePage[x][y] = -1;
12591 Stop[x][y] = FALSE;
12592 if (WasJustMoving[x][y] > 0)
12593 WasJustMoving[x][y]--;
12594 if (WasJustFalling[x][y] > 0)
12595 WasJustFalling[x][y]--;
12596 if (CheckCollision[x][y] > 0)
12597 CheckCollision[x][y]--;
12598 if (CheckImpact[x][y] > 0)
12599 CheckImpact[x][y]--;
12603 /* reset finished pushing action (not done in ContinueMoving() to allow
12604 continuous pushing animation for elements with zero push delay) */
12605 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12607 ResetGfxAnimation(x, y);
12608 TEST_DrawLevelField(x, y);
12612 if (IS_BLOCKED(x, y))
12616 Blocked2Moving(x, y, &oldx, &oldy);
12617 if (!IS_MOVING(oldx, oldy))
12619 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12620 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12621 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12622 printf("GameActions(): This should never happen!\n");
12629 debug_print_timestamp(0, "- time for pre-main loop:");
12632 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12633 SCAN_PLAYFIELD(x, y)
12635 element = Feld[x][y];
12636 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12641 int element2 = element;
12642 int graphic2 = graphic;
12644 int element2 = Feld[x][y];
12645 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12647 int last_gfx_frame = GfxFrame[x][y];
12649 if (graphic_info[graphic2].anim_global_sync)
12650 GfxFrame[x][y] = FrameCounter;
12651 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12652 GfxFrame[x][y] = CustomValue[x][y];
12653 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12654 GfxFrame[x][y] = element_info[element2].collect_score;
12655 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12656 GfxFrame[x][y] = ChangeDelay[x][y];
12658 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12659 DrawLevelGraphicAnimation(x, y, graphic2);
12662 ResetGfxFrame(x, y, TRUE);
12666 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12667 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12668 ResetRandomAnimationValue(x, y);
12672 SetRandomAnimationValue(x, y);
12676 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12679 #endif // -------------------- !!! TEST ONLY !!! --------------------
12682 debug_print_timestamp(0, "- time for TEST loop: -->");
12685 SCAN_PLAYFIELD(x, y)
12687 element = Feld[x][y];
12688 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12690 ResetGfxFrame(x, y, TRUE);
12692 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12693 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12694 ResetRandomAnimationValue(x, y);
12696 SetRandomAnimationValue(x, y);
12698 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12700 if (IS_INACTIVE(element))
12702 if (IS_ANIMATED(graphic))
12703 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12708 /* this may take place after moving, so 'element' may have changed */
12709 if (IS_CHANGING(x, y) &&
12710 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12712 int page = element_info[element].event_page_nr[CE_DELAY];
12715 HandleElementChange(x, y, page);
12717 if (CAN_CHANGE(element))
12718 HandleElementChange(x, y, page);
12720 if (HAS_ACTION(element))
12721 ExecuteCustomElementAction(x, y, element, page);
12724 element = Feld[x][y];
12725 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12728 #if 0 // ---------------------------------------------------------------------
12730 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12734 element = Feld[x][y];
12735 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12737 if (IS_ANIMATED(graphic) &&
12738 !IS_MOVING(x, y) &&
12740 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12742 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12743 TEST_DrawTwinkleOnField(x, y);
12745 else if (IS_MOVING(x, y))
12746 ContinueMoving(x, y);
12753 case EL_EM_EXIT_OPEN:
12754 case EL_SP_EXIT_OPEN:
12755 case EL_STEEL_EXIT_OPEN:
12756 case EL_EM_STEEL_EXIT_OPEN:
12757 case EL_SP_TERMINAL:
12758 case EL_SP_TERMINAL_ACTIVE:
12759 case EL_EXTRA_TIME:
12760 case EL_SHIELD_NORMAL:
12761 case EL_SHIELD_DEADLY:
12762 if (IS_ANIMATED(graphic))
12763 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12766 case EL_DYNAMITE_ACTIVE:
12767 case EL_EM_DYNAMITE_ACTIVE:
12768 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12769 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12770 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12771 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12772 case EL_SP_DISK_RED_ACTIVE:
12773 CheckDynamite(x, y);
12776 case EL_AMOEBA_GROWING:
12777 AmoebeWaechst(x, y);
12780 case EL_AMOEBA_SHRINKING:
12781 AmoebaDisappearing(x, y);
12784 #if !USE_NEW_AMOEBA_CODE
12785 case EL_AMOEBA_WET:
12786 case EL_AMOEBA_DRY:
12787 case EL_AMOEBA_FULL:
12789 case EL_EMC_DRIPPER:
12790 AmoebeAbleger(x, y);
12794 case EL_GAME_OF_LIFE:
12799 case EL_EXIT_CLOSED:
12803 case EL_EM_EXIT_CLOSED:
12807 case EL_STEEL_EXIT_CLOSED:
12808 CheckExitSteel(x, y);
12811 case EL_EM_STEEL_EXIT_CLOSED:
12812 CheckExitSteelEM(x, y);
12815 case EL_SP_EXIT_CLOSED:
12819 case EL_EXPANDABLE_WALL_GROWING:
12820 case EL_EXPANDABLE_STEELWALL_GROWING:
12821 MauerWaechst(x, y);
12824 case EL_EXPANDABLE_WALL:
12825 case EL_EXPANDABLE_WALL_HORIZONTAL:
12826 case EL_EXPANDABLE_WALL_VERTICAL:
12827 case EL_EXPANDABLE_WALL_ANY:
12828 case EL_BD_EXPANDABLE_WALL:
12829 MauerAbleger(x, y);
12832 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12833 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12834 case EL_EXPANDABLE_STEELWALL_ANY:
12835 MauerAblegerStahl(x, y);
12839 CheckForDragon(x, y);
12845 case EL_ELEMENT_SNAPPING:
12846 case EL_DIAGONAL_SHRINKING:
12847 case EL_DIAGONAL_GROWING:
12850 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12852 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12857 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12858 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12863 #else // ---------------------------------------------------------------------
12865 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12869 element = Feld[x][y];
12870 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12872 if (IS_ANIMATED(graphic) &&
12873 !IS_MOVING(x, y) &&
12875 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12877 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12878 TEST_DrawTwinkleOnField(x, y);
12880 else if ((element == EL_ACID ||
12881 element == EL_EXIT_OPEN ||
12882 element == EL_EM_EXIT_OPEN ||
12883 element == EL_SP_EXIT_OPEN ||
12884 element == EL_STEEL_EXIT_OPEN ||
12885 element == EL_EM_STEEL_EXIT_OPEN ||
12886 element == EL_SP_TERMINAL ||
12887 element == EL_SP_TERMINAL_ACTIVE ||
12888 element == EL_EXTRA_TIME ||
12889 element == EL_SHIELD_NORMAL ||
12890 element == EL_SHIELD_DEADLY) &&
12891 IS_ANIMATED(graphic))
12892 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12893 else if (IS_MOVING(x, y))
12894 ContinueMoving(x, y);
12895 else if (IS_ACTIVE_BOMB(element))
12896 CheckDynamite(x, y);
12897 else if (element == EL_AMOEBA_GROWING)
12898 AmoebeWaechst(x, y);
12899 else if (element == EL_AMOEBA_SHRINKING)
12900 AmoebaDisappearing(x, y);
12902 #if !USE_NEW_AMOEBA_CODE
12903 else if (IS_AMOEBALIVE(element))
12904 AmoebeAbleger(x, y);
12907 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12909 else if (element == EL_EXIT_CLOSED)
12911 else if (element == EL_EM_EXIT_CLOSED)
12913 else if (element == EL_STEEL_EXIT_CLOSED)
12914 CheckExitSteel(x, y);
12915 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12916 CheckExitSteelEM(x, y);
12917 else if (element == EL_SP_EXIT_CLOSED)
12919 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12920 element == EL_EXPANDABLE_STEELWALL_GROWING)
12921 MauerWaechst(x, y);
12922 else if (element == EL_EXPANDABLE_WALL ||
12923 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12924 element == EL_EXPANDABLE_WALL_VERTICAL ||
12925 element == EL_EXPANDABLE_WALL_ANY ||
12926 element == EL_BD_EXPANDABLE_WALL)
12927 MauerAbleger(x, y);
12928 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12929 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12930 element == EL_EXPANDABLE_STEELWALL_ANY)
12931 MauerAblegerStahl(x, y);
12932 else if (element == EL_FLAMES)
12933 CheckForDragon(x, y);
12934 else if (element == EL_EXPLOSION)
12935 ; /* drawing of correct explosion animation is handled separately */
12936 else if (element == EL_ELEMENT_SNAPPING ||
12937 element == EL_DIAGONAL_SHRINKING ||
12938 element == EL_DIAGONAL_GROWING)
12940 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12942 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12944 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12945 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12947 #endif // ---------------------------------------------------------------------
12949 if (IS_BELT_ACTIVE(element))
12950 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12952 if (game.magic_wall_active)
12954 int jx = local_player->jx, jy = local_player->jy;
12956 /* play the element sound at the position nearest to the player */
12957 if ((element == EL_MAGIC_WALL_FULL ||
12958 element == EL_MAGIC_WALL_ACTIVE ||
12959 element == EL_MAGIC_WALL_EMPTYING ||
12960 element == EL_BD_MAGIC_WALL_FULL ||
12961 element == EL_BD_MAGIC_WALL_ACTIVE ||
12962 element == EL_BD_MAGIC_WALL_EMPTYING ||
12963 element == EL_DC_MAGIC_WALL_FULL ||
12964 element == EL_DC_MAGIC_WALL_ACTIVE ||
12965 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12966 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12975 debug_print_timestamp(0, "- time for MAIN loop: -->");
12978 #if USE_NEW_AMOEBA_CODE
12979 /* new experimental amoeba growth stuff */
12980 if (!(FrameCounter % 8))
12982 static unsigned long random = 1684108901;
12984 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12986 x = RND(lev_fieldx);
12987 y = RND(lev_fieldy);
12988 element = Feld[x][y];
12990 if (!IS_PLAYER(x,y) &&
12991 (element == EL_EMPTY ||
12992 CAN_GROW_INTO(element) ||
12993 element == EL_QUICKSAND_EMPTY ||
12994 element == EL_QUICKSAND_FAST_EMPTY ||
12995 element == EL_ACID_SPLASH_LEFT ||
12996 element == EL_ACID_SPLASH_RIGHT))
12998 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12999 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13000 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13001 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13002 Feld[x][y] = EL_AMOEBA_DROP;
13005 random = random * 129 + 1;
13011 if (game.explosions_delayed)
13014 game.explosions_delayed = FALSE;
13016 SCAN_PLAYFIELD(x, y)
13018 element = Feld[x][y];
13020 if (ExplodeField[x][y])
13021 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13022 else if (element == EL_EXPLOSION)
13023 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13025 ExplodeField[x][y] = EX_TYPE_NONE;
13028 game.explosions_delayed = TRUE;
13031 if (game.magic_wall_active)
13033 if (!(game.magic_wall_time_left % 4))
13035 int element = Feld[magic_wall_x][magic_wall_y];
13037 if (element == EL_BD_MAGIC_WALL_FULL ||
13038 element == EL_BD_MAGIC_WALL_ACTIVE ||
13039 element == EL_BD_MAGIC_WALL_EMPTYING)
13040 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13041 else if (element == EL_DC_MAGIC_WALL_FULL ||
13042 element == EL_DC_MAGIC_WALL_ACTIVE ||
13043 element == EL_DC_MAGIC_WALL_EMPTYING)
13044 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13046 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13049 if (game.magic_wall_time_left > 0)
13051 game.magic_wall_time_left--;
13053 if (!game.magic_wall_time_left)
13055 SCAN_PLAYFIELD(x, y)
13057 element = Feld[x][y];
13059 if (element == EL_MAGIC_WALL_ACTIVE ||
13060 element == EL_MAGIC_WALL_FULL)
13062 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13063 TEST_DrawLevelField(x, y);
13065 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13066 element == EL_BD_MAGIC_WALL_FULL)
13068 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13069 TEST_DrawLevelField(x, y);
13071 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13072 element == EL_DC_MAGIC_WALL_FULL)
13074 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13075 TEST_DrawLevelField(x, y);
13079 game.magic_wall_active = FALSE;
13084 if (game.light_time_left > 0)
13086 game.light_time_left--;
13088 if (game.light_time_left == 0)
13089 RedrawAllLightSwitchesAndInvisibleElements();
13092 if (game.timegate_time_left > 0)
13094 game.timegate_time_left--;
13096 if (game.timegate_time_left == 0)
13097 CloseAllOpenTimegates();
13100 if (game.lenses_time_left > 0)
13102 game.lenses_time_left--;
13104 if (game.lenses_time_left == 0)
13105 RedrawAllInvisibleElementsForLenses();
13108 if (game.magnify_time_left > 0)
13110 game.magnify_time_left--;
13112 if (game.magnify_time_left == 0)
13113 RedrawAllInvisibleElementsForMagnifier();
13116 for (i = 0; i < MAX_PLAYERS; i++)
13118 struct PlayerInfo *player = &stored_player[i];
13120 if (SHIELD_ON(player))
13122 if (player->shield_deadly_time_left)
13123 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13124 else if (player->shield_normal_time_left)
13125 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13129 #if USE_DELAYED_GFX_REDRAW
13130 SCAN_PLAYFIELD(x, y)
13133 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13135 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13136 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13139 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13140 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13142 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13143 DrawLevelField(x, y);
13145 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13146 DrawLevelFieldCrumbledSand(x, y);
13148 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13149 DrawLevelFieldCrumbledSandNeighbours(x, y);
13151 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13152 DrawTwinkleOnField(x, y);
13155 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13162 PlayAllPlayersSound();
13164 if (options.debug) /* calculate frames per second */
13166 static unsigned long fps_counter = 0;
13167 static int fps_frames = 0;
13168 unsigned long fps_delay_ms = Counter() - fps_counter;
13172 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13174 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13177 fps_counter = Counter();
13180 redraw_mask |= REDRAW_FPS;
13183 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13185 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13187 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13189 local_player->show_envelope = 0;
13193 debug_print_timestamp(0, "stop main loop profiling ");
13194 printf("----------------------------------------------------------\n");
13197 /* use random number generator in every frame to make it less predictable */
13198 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13202 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13204 int min_x = x, min_y = y, max_x = x, max_y = y;
13207 for (i = 0; i < MAX_PLAYERS; i++)
13209 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13211 if (!stored_player[i].active || &stored_player[i] == player)
13214 min_x = MIN(min_x, jx);
13215 min_y = MIN(min_y, jy);
13216 max_x = MAX(max_x, jx);
13217 max_y = MAX(max_y, jy);
13220 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13223 static boolean AllPlayersInVisibleScreen()
13227 for (i = 0; i < MAX_PLAYERS; i++)
13229 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13231 if (!stored_player[i].active)
13234 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13241 void ScrollLevel(int dx, int dy)
13244 /* (directly solved in BlitBitmap() now) */
13245 static Bitmap *bitmap_db_field2 = NULL;
13246 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13253 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13254 /* only horizontal XOR vertical scroll direction allowed */
13255 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13260 /* (directly solved in BlitBitmap() now) */
13261 if (bitmap_db_field2 == NULL)
13262 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13264 /* needed when blitting directly to same bitmap -- should not be needed with
13265 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13266 BlitBitmap(drawto_field, bitmap_db_field2,
13267 FX + TILEX * (dx == -1) - softscroll_offset,
13268 FY + TILEY * (dy == -1) - softscroll_offset,
13269 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13270 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13271 FX + TILEX * (dx == 1) - softscroll_offset,
13272 FY + TILEY * (dy == 1) - softscroll_offset);
13273 BlitBitmap(bitmap_db_field2, drawto_field,
13274 FX + TILEX * (dx == 1) - softscroll_offset,
13275 FY + TILEY * (dy == 1) - softscroll_offset,
13276 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13277 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13278 FX + TILEX * (dx == 1) - softscroll_offset,
13279 FY + TILEY * (dy == 1) - softscroll_offset);
13284 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13285 int xsize = (BX2 - BX1 + 1);
13286 int ysize = (BY2 - BY1 + 1);
13287 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13288 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13289 int step = (start < end ? +1 : -1);
13291 for (i = start; i != end; i += step)
13293 BlitBitmap(drawto_field, drawto_field,
13294 FX + TILEX * (dx != 0 ? i + step : 0),
13295 FY + TILEY * (dy != 0 ? i + step : 0),
13296 TILEX * (dx != 0 ? 1 : xsize),
13297 TILEY * (dy != 0 ? 1 : ysize),
13298 FX + TILEX * (dx != 0 ? i : 0),
13299 FY + TILEY * (dy != 0 ? i : 0));
13304 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13306 BlitBitmap(drawto_field, drawto_field,
13307 FX + TILEX * (dx == -1) - softscroll_offset,
13308 FY + TILEY * (dy == -1) - softscroll_offset,
13309 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13310 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13311 FX + TILEX * (dx == 1) - softscroll_offset,
13312 FY + TILEY * (dy == 1) - softscroll_offset);
13318 x = (dx == 1 ? BX1 : BX2);
13319 for (y = BY1; y <= BY2; y++)
13320 DrawScreenField(x, y);
13325 y = (dy == 1 ? BY1 : BY2);
13326 for (x = BX1; x <= BX2; x++)
13327 DrawScreenField(x, y);
13330 redraw_mask |= REDRAW_FIELD;
13333 static boolean canFallDown(struct PlayerInfo *player)
13335 int jx = player->jx, jy = player->jy;
13337 return (IN_LEV_FIELD(jx, jy + 1) &&
13338 (IS_FREE(jx, jy + 1) ||
13339 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13340 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13341 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13344 static boolean canPassField(int x, int y, int move_dir)
13346 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13347 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13348 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13349 int nextx = x + dx;
13350 int nexty = y + dy;
13351 int element = Feld[x][y];
13353 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13354 !CAN_MOVE(element) &&
13355 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13356 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13357 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13360 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13362 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13363 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13364 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13368 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13369 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13370 (IS_DIGGABLE(Feld[newx][newy]) ||
13371 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13372 canPassField(newx, newy, move_dir)));
13375 static void CheckGravityMovement(struct PlayerInfo *player)
13377 #if USE_PLAYER_GRAVITY
13378 if (player->gravity && !player->programmed_action)
13380 if (game.gravity && !player->programmed_action)
13383 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13384 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13385 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13386 int jx = player->jx, jy = player->jy;
13387 boolean player_is_moving_to_valid_field =
13388 (!player_is_snapping &&
13389 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13390 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13391 boolean player_can_fall_down = canFallDown(player);
13393 if (player_can_fall_down &&
13394 !player_is_moving_to_valid_field)
13395 player->programmed_action = MV_DOWN;
13399 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13401 return CheckGravityMovement(player);
13403 #if USE_PLAYER_GRAVITY
13404 if (player->gravity && !player->programmed_action)
13406 if (game.gravity && !player->programmed_action)
13409 int jx = player->jx, jy = player->jy;
13410 boolean field_under_player_is_free =
13411 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13412 boolean player_is_standing_on_valid_field =
13413 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13414 (IS_WALKABLE(Feld[jx][jy]) &&
13415 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13417 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13418 player->programmed_action = MV_DOWN;
13423 MovePlayerOneStep()
13424 -----------------------------------------------------------------------------
13425 dx, dy: direction (non-diagonal) to try to move the player to
13426 real_dx, real_dy: direction as read from input device (can be diagonal)
13429 boolean MovePlayerOneStep(struct PlayerInfo *player,
13430 int dx, int dy, int real_dx, int real_dy)
13432 int jx = player->jx, jy = player->jy;
13433 int new_jx = jx + dx, new_jy = jy + dy;
13434 #if !USE_FIXED_DONT_RUN_INTO
13438 boolean player_can_move = !player->cannot_move;
13440 if (!player->active || (!dx && !dy))
13441 return MP_NO_ACTION;
13443 player->MovDir = (dx < 0 ? MV_LEFT :
13444 dx > 0 ? MV_RIGHT :
13446 dy > 0 ? MV_DOWN : MV_NONE);
13448 if (!IN_LEV_FIELD(new_jx, new_jy))
13449 return MP_NO_ACTION;
13451 if (!player_can_move)
13453 if (player->MovPos == 0)
13455 player->is_moving = FALSE;
13456 player->is_digging = FALSE;
13457 player->is_collecting = FALSE;
13458 player->is_snapping = FALSE;
13459 player->is_pushing = FALSE;
13464 if (!options.network && game.centered_player_nr == -1 &&
13465 !AllPlayersInSight(player, new_jx, new_jy))
13466 return MP_NO_ACTION;
13468 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13469 return MP_NO_ACTION;
13472 #if !USE_FIXED_DONT_RUN_INTO
13473 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13475 /* (moved to DigField()) */
13476 if (player_can_move && DONT_RUN_INTO(element))
13478 if (element == EL_ACID && dx == 0 && dy == 1)
13480 SplashAcid(new_jx, new_jy);
13481 Feld[jx][jy] = EL_PLAYER_1;
13482 InitMovingField(jx, jy, MV_DOWN);
13483 Store[jx][jy] = EL_ACID;
13484 ContinueMoving(jx, jy);
13485 BuryPlayer(player);
13488 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13494 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13495 if (can_move != MP_MOVING)
13498 /* check if DigField() has caused relocation of the player */
13499 if (player->jx != jx || player->jy != jy)
13500 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13502 StorePlayer[jx][jy] = 0;
13503 player->last_jx = jx;
13504 player->last_jy = jy;
13505 player->jx = new_jx;
13506 player->jy = new_jy;
13507 StorePlayer[new_jx][new_jy] = player->element_nr;
13509 if (player->move_delay_value_next != -1)
13511 player->move_delay_value = player->move_delay_value_next;
13512 player->move_delay_value_next = -1;
13516 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13518 player->step_counter++;
13520 PlayerVisit[jx][jy] = FrameCounter;
13522 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13523 player->is_moving = TRUE;
13527 /* should better be called in MovePlayer(), but this breaks some tapes */
13528 ScrollPlayer(player, SCROLL_INIT);
13534 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13536 int jx = player->jx, jy = player->jy;
13537 int old_jx = jx, old_jy = jy;
13538 int moved = MP_NO_ACTION;
13540 if (!player->active)
13545 if (player->MovPos == 0)
13547 player->is_moving = FALSE;
13548 player->is_digging = FALSE;
13549 player->is_collecting = FALSE;
13550 player->is_snapping = FALSE;
13551 player->is_pushing = FALSE;
13557 if (player->move_delay > 0)
13560 player->move_delay = -1; /* set to "uninitialized" value */
13562 /* store if player is automatically moved to next field */
13563 player->is_auto_moving = (player->programmed_action != MV_NONE);
13565 /* remove the last programmed player action */
13566 player->programmed_action = 0;
13568 if (player->MovPos)
13570 /* should only happen if pre-1.2 tape recordings are played */
13571 /* this is only for backward compatibility */
13573 int original_move_delay_value = player->move_delay_value;
13576 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13580 /* scroll remaining steps with finest movement resolution */
13581 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13583 while (player->MovPos)
13585 ScrollPlayer(player, SCROLL_GO_ON);
13586 ScrollScreen(NULL, SCROLL_GO_ON);
13588 AdvanceFrameAndPlayerCounters(player->index_nr);
13594 player->move_delay_value = original_move_delay_value;
13597 player->is_active = FALSE;
13599 if (player->last_move_dir & MV_HORIZONTAL)
13601 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13602 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13606 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13607 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13610 #if USE_FIXED_BORDER_RUNNING_GFX
13611 if (!moved && !player->is_active)
13613 player->is_moving = FALSE;
13614 player->is_digging = FALSE;
13615 player->is_collecting = FALSE;
13616 player->is_snapping = FALSE;
13617 player->is_pushing = FALSE;
13625 if (moved & MP_MOVING && !ScreenMovPos &&
13626 (player->index_nr == game.centered_player_nr ||
13627 game.centered_player_nr == -1))
13629 if (moved & MP_MOVING && !ScreenMovPos &&
13630 (player == local_player || !options.network))
13633 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13634 int offset = game.scroll_delay_value;
13636 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13638 /* actual player has left the screen -- scroll in that direction */
13639 if (jx != old_jx) /* player has moved horizontally */
13640 scroll_x += (jx - old_jx);
13641 else /* player has moved vertically */
13642 scroll_y += (jy - old_jy);
13646 if (jx != old_jx) /* player has moved horizontally */
13648 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13649 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13650 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13652 /* don't scroll over playfield boundaries */
13653 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13654 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13656 /* don't scroll more than one field at a time */
13657 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13659 /* don't scroll against the player's moving direction */
13660 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13661 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13662 scroll_x = old_scroll_x;
13664 else /* player has moved vertically */
13666 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13667 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13668 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13670 /* don't scroll over playfield boundaries */
13671 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13672 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13674 /* don't scroll more than one field at a time */
13675 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13677 /* don't scroll against the player's moving direction */
13678 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13679 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13680 scroll_y = old_scroll_y;
13684 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13687 if (!options.network && game.centered_player_nr == -1 &&
13688 !AllPlayersInVisibleScreen())
13690 scroll_x = old_scroll_x;
13691 scroll_y = old_scroll_y;
13695 if (!options.network && !AllPlayersInVisibleScreen())
13697 scroll_x = old_scroll_x;
13698 scroll_y = old_scroll_y;
13703 ScrollScreen(player, SCROLL_INIT);
13704 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13709 player->StepFrame = 0;
13711 if (moved & MP_MOVING)
13713 if (old_jx != jx && old_jy == jy)
13714 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13715 else if (old_jx == jx && old_jy != jy)
13716 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13718 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13720 player->last_move_dir = player->MovDir;
13721 player->is_moving = TRUE;
13722 player->is_snapping = FALSE;
13723 player->is_switching = FALSE;
13724 player->is_dropping = FALSE;
13725 player->is_dropping_pressed = FALSE;
13726 player->drop_pressed_delay = 0;
13729 /* should better be called here than above, but this breaks some tapes */
13730 ScrollPlayer(player, SCROLL_INIT);
13735 CheckGravityMovementWhenNotMoving(player);
13737 player->is_moving = FALSE;
13739 /* at this point, the player is allowed to move, but cannot move right now
13740 (e.g. because of something blocking the way) -- ensure that the player
13741 is also allowed to move in the next frame (in old versions before 3.1.1,
13742 the player was forced to wait again for eight frames before next try) */
13744 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13745 player->move_delay = 0; /* allow direct movement in the next frame */
13748 if (player->move_delay == -1) /* not yet initialized by DigField() */
13749 player->move_delay = player->move_delay_value;
13751 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13753 TestIfPlayerTouchesBadThing(jx, jy);
13754 TestIfPlayerTouchesCustomElement(jx, jy);
13757 if (!player->active)
13758 RemovePlayer(player);
13763 void ScrollPlayer(struct PlayerInfo *player, int mode)
13765 int jx = player->jx, jy = player->jy;
13766 int last_jx = player->last_jx, last_jy = player->last_jy;
13767 int move_stepsize = TILEX / player->move_delay_value;
13769 #if USE_NEW_PLAYER_SPEED
13770 if (!player->active)
13773 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13776 if (!player->active || player->MovPos == 0)
13780 if (mode == SCROLL_INIT)
13782 player->actual_frame_counter = FrameCounter;
13783 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13785 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13786 Feld[last_jx][last_jy] == EL_EMPTY)
13788 int last_field_block_delay = 0; /* start with no blocking at all */
13789 int block_delay_adjustment = player->block_delay_adjustment;
13791 /* if player blocks last field, add delay for exactly one move */
13792 if (player->block_last_field)
13794 last_field_block_delay += player->move_delay_value;
13796 /* when blocking enabled, prevent moving up despite gravity */
13797 #if USE_PLAYER_GRAVITY
13798 if (player->gravity && player->MovDir == MV_UP)
13799 block_delay_adjustment = -1;
13801 if (game.gravity && player->MovDir == MV_UP)
13802 block_delay_adjustment = -1;
13806 /* add block delay adjustment (also possible when not blocking) */
13807 last_field_block_delay += block_delay_adjustment;
13809 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13810 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13813 #if USE_NEW_PLAYER_SPEED
13814 if (player->MovPos != 0) /* player has not yet reached destination */
13820 else if (!FrameReached(&player->actual_frame_counter, 1))
13823 #if USE_NEW_PLAYER_SPEED
13824 if (player->MovPos != 0)
13826 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13827 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13829 /* before DrawPlayer() to draw correct player graphic for this case */
13830 if (player->MovPos == 0)
13831 CheckGravityMovement(player);
13834 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13835 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13837 /* before DrawPlayer() to draw correct player graphic for this case */
13838 if (player->MovPos == 0)
13839 CheckGravityMovement(player);
13842 if (player->MovPos == 0) /* player reached destination field */
13844 if (player->move_delay_reset_counter > 0)
13846 player->move_delay_reset_counter--;
13848 if (player->move_delay_reset_counter == 0)
13850 /* continue with normal speed after quickly moving through gate */
13851 HALVE_PLAYER_SPEED(player);
13853 /* be able to make the next move without delay */
13854 player->move_delay = 0;
13858 player->last_jx = jx;
13859 player->last_jy = jy;
13861 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13862 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13864 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13866 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13867 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13869 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13871 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13872 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13874 DrawPlayer(player); /* needed here only to cleanup last field */
13875 RemovePlayer(player);
13877 if (local_player->friends_still_needed == 0 ||
13878 IS_SP_ELEMENT(Feld[jx][jy]))
13879 PlayerWins(player);
13882 /* this breaks one level: "machine", level 000 */
13884 int move_direction = player->MovDir;
13885 int enter_side = MV_DIR_OPPOSITE(move_direction);
13886 int leave_side = move_direction;
13887 int old_jx = last_jx;
13888 int old_jy = last_jy;
13889 int old_element = Feld[old_jx][old_jy];
13890 int new_element = Feld[jx][jy];
13892 if (IS_CUSTOM_ELEMENT(old_element))
13893 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13895 player->index_bit, leave_side);
13897 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13898 CE_PLAYER_LEAVES_X,
13899 player->index_bit, leave_side);
13901 if (IS_CUSTOM_ELEMENT(new_element))
13902 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13903 player->index_bit, enter_side);
13905 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13906 CE_PLAYER_ENTERS_X,
13907 player->index_bit, enter_side);
13909 #if USE_FIX_CE_ACTION_WITH_PLAYER
13910 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13911 CE_MOVE_OF_X, move_direction);
13913 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13914 CE_MOVE_OF_X, move_direction);
13918 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13920 TestIfPlayerTouchesBadThing(jx, jy);
13921 TestIfPlayerTouchesCustomElement(jx, jy);
13923 /* needed because pushed element has not yet reached its destination,
13924 so it would trigger a change event at its previous field location */
13925 if (!player->is_pushing)
13926 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13928 if (!player->active)
13929 RemovePlayer(player);
13932 if (!local_player->LevelSolved && level.use_step_counter)
13942 if (TimeLeft <= 10 && setup.time_limit)
13943 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13946 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13948 DisplayGameControlValues();
13950 DrawGameValue_Time(TimeLeft);
13953 if (!TimeLeft && setup.time_limit)
13954 for (i = 0; i < MAX_PLAYERS; i++)
13955 KillPlayer(&stored_player[i]);
13958 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13960 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13962 DisplayGameControlValues();
13965 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13966 DrawGameValue_Time(TimePlayed);
13970 if (tape.single_step && tape.recording && !tape.pausing &&
13971 !player->programmed_action)
13972 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13976 void ScrollScreen(struct PlayerInfo *player, int mode)
13978 static unsigned long screen_frame_counter = 0;
13980 if (mode == SCROLL_INIT)
13982 /* set scrolling step size according to actual player's moving speed */
13983 ScrollStepSize = TILEX / player->move_delay_value;
13985 screen_frame_counter = FrameCounter;
13986 ScreenMovDir = player->MovDir;
13987 ScreenMovPos = player->MovPos;
13988 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13991 else if (!FrameReached(&screen_frame_counter, 1))
13996 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13997 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13998 redraw_mask |= REDRAW_FIELD;
14001 ScreenMovDir = MV_NONE;
14004 void TestIfPlayerTouchesCustomElement(int x, int y)
14006 static int xy[4][2] =
14013 static int trigger_sides[4][2] =
14015 /* center side border side */
14016 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14017 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14018 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14019 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14021 static int touch_dir[4] =
14023 MV_LEFT | MV_RIGHT,
14028 int center_element = Feld[x][y]; /* should always be non-moving! */
14031 for (i = 0; i < NUM_DIRECTIONS; i++)
14033 int xx = x + xy[i][0];
14034 int yy = y + xy[i][1];
14035 int center_side = trigger_sides[i][0];
14036 int border_side = trigger_sides[i][1];
14037 int border_element;
14039 if (!IN_LEV_FIELD(xx, yy))
14042 if (IS_PLAYER(x, y)) /* player found at center element */
14044 struct PlayerInfo *player = PLAYERINFO(x, y);
14046 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14047 border_element = Feld[xx][yy]; /* may be moving! */
14048 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14049 border_element = Feld[xx][yy];
14050 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14051 border_element = MovingOrBlocked2Element(xx, yy);
14053 continue; /* center and border element do not touch */
14055 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14056 player->index_bit, border_side);
14057 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14058 CE_PLAYER_TOUCHES_X,
14059 player->index_bit, border_side);
14061 #if USE_FIX_CE_ACTION_WITH_PLAYER
14063 /* use player element that is initially defined in the level playfield,
14064 not the player element that corresponds to the runtime player number
14065 (example: a level that contains EL_PLAYER_3 as the only player would
14066 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14067 int player_element = PLAYERINFO(x, y)->initial_element;
14069 CheckElementChangeBySide(xx, yy, border_element, player_element,
14070 CE_TOUCHING_X, border_side);
14074 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14076 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14078 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14080 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14081 continue; /* center and border element do not touch */
14084 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14085 player->index_bit, center_side);
14086 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14087 CE_PLAYER_TOUCHES_X,
14088 player->index_bit, center_side);
14090 #if USE_FIX_CE_ACTION_WITH_PLAYER
14092 /* use player element that is initially defined in the level playfield,
14093 not the player element that corresponds to the runtime player number
14094 (example: a level that contains EL_PLAYER_3 as the only player would
14095 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14096 int player_element = PLAYERINFO(xx, yy)->initial_element;
14098 CheckElementChangeBySide(x, y, center_element, player_element,
14099 CE_TOUCHING_X, center_side);
14108 #if USE_ELEMENT_TOUCHING_BUGFIX
14110 void TestIfElementTouchesCustomElement(int x, int y)
14112 static int xy[4][2] =
14119 static int trigger_sides[4][2] =
14121 /* center side border side */
14122 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14123 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14124 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14125 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14127 static int touch_dir[4] =
14129 MV_LEFT | MV_RIGHT,
14134 boolean change_center_element = FALSE;
14135 int center_element = Feld[x][y]; /* should always be non-moving! */
14136 int border_element_old[NUM_DIRECTIONS];
14139 for (i = 0; i < NUM_DIRECTIONS; i++)
14141 int xx = x + xy[i][0];
14142 int yy = y + xy[i][1];
14143 int border_element;
14145 border_element_old[i] = -1;
14147 if (!IN_LEV_FIELD(xx, yy))
14150 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14151 border_element = Feld[xx][yy]; /* may be moving! */
14152 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14153 border_element = Feld[xx][yy];
14154 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14155 border_element = MovingOrBlocked2Element(xx, yy);
14157 continue; /* center and border element do not touch */
14159 border_element_old[i] = border_element;
14162 for (i = 0; i < NUM_DIRECTIONS; i++)
14164 int xx = x + xy[i][0];
14165 int yy = y + xy[i][1];
14166 int center_side = trigger_sides[i][0];
14167 int border_element = border_element_old[i];
14169 if (border_element == -1)
14172 /* check for change of border element */
14173 CheckElementChangeBySide(xx, yy, border_element, center_element,
14174 CE_TOUCHING_X, center_side);
14176 /* (center element cannot be player, so we dont have to check this here) */
14179 for (i = 0; i < NUM_DIRECTIONS; i++)
14181 int xx = x + xy[i][0];
14182 int yy = y + xy[i][1];
14183 int border_side = trigger_sides[i][1];
14184 int border_element = border_element_old[i];
14186 if (border_element == -1)
14189 /* check for change of center element (but change it only once) */
14190 if (!change_center_element)
14191 change_center_element =
14192 CheckElementChangeBySide(x, y, center_element, border_element,
14193 CE_TOUCHING_X, border_side);
14195 #if USE_FIX_CE_ACTION_WITH_PLAYER
14196 if (IS_PLAYER(xx, yy))
14198 /* use player element that is initially defined in the level playfield,
14199 not the player element that corresponds to the runtime player number
14200 (example: a level that contains EL_PLAYER_3 as the only player would
14201 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14202 int player_element = PLAYERINFO(xx, yy)->initial_element;
14204 CheckElementChangeBySide(x, y, center_element, player_element,
14205 CE_TOUCHING_X, border_side);
14213 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14215 static int xy[4][2] =
14222 static int trigger_sides[4][2] =
14224 /* center side border side */
14225 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14226 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14227 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14228 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14230 static int touch_dir[4] =
14232 MV_LEFT | MV_RIGHT,
14237 boolean change_center_element = FALSE;
14238 int center_element = Feld[x][y]; /* should always be non-moving! */
14241 for (i = 0; i < NUM_DIRECTIONS; i++)
14243 int xx = x + xy[i][0];
14244 int yy = y + xy[i][1];
14245 int center_side = trigger_sides[i][0];
14246 int border_side = trigger_sides[i][1];
14247 int border_element;
14249 if (!IN_LEV_FIELD(xx, yy))
14252 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14253 border_element = Feld[xx][yy]; /* may be moving! */
14254 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14255 border_element = Feld[xx][yy];
14256 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14257 border_element = MovingOrBlocked2Element(xx, yy);
14259 continue; /* center and border element do not touch */
14261 /* check for change of center element (but change it only once) */
14262 if (!change_center_element)
14263 change_center_element =
14264 CheckElementChangeBySide(x, y, center_element, border_element,
14265 CE_TOUCHING_X, border_side);
14267 /* check for change of border element */
14268 CheckElementChangeBySide(xx, yy, border_element, center_element,
14269 CE_TOUCHING_X, center_side);
14275 void TestIfElementHitsCustomElement(int x, int y, int direction)
14277 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14278 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14279 int hitx = x + dx, hity = y + dy;
14280 int hitting_element = Feld[x][y];
14281 int touched_element;
14283 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14286 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14287 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14289 if (IN_LEV_FIELD(hitx, hity))
14291 int opposite_direction = MV_DIR_OPPOSITE(direction);
14292 int hitting_side = direction;
14293 int touched_side = opposite_direction;
14294 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14295 MovDir[hitx][hity] != direction ||
14296 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14302 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14303 CE_HITTING_X, touched_side);
14305 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14306 CE_HIT_BY_X, hitting_side);
14308 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14309 CE_HIT_BY_SOMETHING, opposite_direction);
14311 #if USE_FIX_CE_ACTION_WITH_PLAYER
14312 if (IS_PLAYER(hitx, hity))
14314 /* use player element that is initially defined in the level playfield,
14315 not the player element that corresponds to the runtime player number
14316 (example: a level that contains EL_PLAYER_3 as the only player would
14317 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14318 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14320 CheckElementChangeBySide(x, y, hitting_element, player_element,
14321 CE_HITTING_X, touched_side);
14327 /* "hitting something" is also true when hitting the playfield border */
14328 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14329 CE_HITTING_SOMETHING, direction);
14333 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14335 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14336 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14337 int hitx = x + dx, hity = y + dy;
14338 int hitting_element = Feld[x][y];
14339 int touched_element;
14341 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14342 !IS_FREE(hitx, hity) &&
14343 (!IS_MOVING(hitx, hity) ||
14344 MovDir[hitx][hity] != direction ||
14345 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14348 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14352 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14356 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14357 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14359 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14360 EP_CAN_SMASH_EVERYTHING, direction);
14362 if (IN_LEV_FIELD(hitx, hity))
14364 int opposite_direction = MV_DIR_OPPOSITE(direction);
14365 int hitting_side = direction;
14366 int touched_side = opposite_direction;
14368 int touched_element = MovingOrBlocked2Element(hitx, hity);
14371 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14372 MovDir[hitx][hity] != direction ||
14373 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14382 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14383 CE_SMASHED_BY_SOMETHING, opposite_direction);
14385 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14386 CE_OTHER_IS_SMASHING, touched_side);
14388 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14389 CE_OTHER_GETS_SMASHED, hitting_side);
14395 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14397 int i, kill_x = -1, kill_y = -1;
14399 int bad_element = -1;
14400 static int test_xy[4][2] =
14407 static int test_dir[4] =
14415 for (i = 0; i < NUM_DIRECTIONS; i++)
14417 int test_x, test_y, test_move_dir, test_element;
14419 test_x = good_x + test_xy[i][0];
14420 test_y = good_y + test_xy[i][1];
14422 if (!IN_LEV_FIELD(test_x, test_y))
14426 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14428 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14430 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14431 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14433 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14434 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14438 bad_element = test_element;
14444 if (kill_x != -1 || kill_y != -1)
14446 if (IS_PLAYER(good_x, good_y))
14448 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14450 if (player->shield_deadly_time_left > 0 &&
14451 !IS_INDESTRUCTIBLE(bad_element))
14452 Bang(kill_x, kill_y);
14453 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14454 KillPlayer(player);
14457 Bang(good_x, good_y);
14461 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14463 int i, kill_x = -1, kill_y = -1;
14464 int bad_element = Feld[bad_x][bad_y];
14465 static int test_xy[4][2] =
14472 static int touch_dir[4] =
14474 MV_LEFT | MV_RIGHT,
14479 static int test_dir[4] =
14487 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14490 for (i = 0; i < NUM_DIRECTIONS; i++)
14492 int test_x, test_y, test_move_dir, test_element;
14494 test_x = bad_x + test_xy[i][0];
14495 test_y = bad_y + test_xy[i][1];
14497 if (!IN_LEV_FIELD(test_x, test_y))
14501 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14503 test_element = Feld[test_x][test_y];
14505 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14506 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14508 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14509 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14511 /* good thing is player or penguin that does not move away */
14512 if (IS_PLAYER(test_x, test_y))
14514 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14516 if (bad_element == EL_ROBOT && player->is_moving)
14517 continue; /* robot does not kill player if he is moving */
14519 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14521 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14522 continue; /* center and border element do not touch */
14530 else if (test_element == EL_PENGUIN)
14540 if (kill_x != -1 || kill_y != -1)
14542 if (IS_PLAYER(kill_x, kill_y))
14544 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14546 if (player->shield_deadly_time_left > 0 &&
14547 !IS_INDESTRUCTIBLE(bad_element))
14548 Bang(bad_x, bad_y);
14549 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14550 KillPlayer(player);
14553 Bang(kill_x, kill_y);
14557 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14559 int bad_element = Feld[bad_x][bad_y];
14560 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14561 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14562 int test_x = bad_x + dx, test_y = bad_y + dy;
14563 int test_move_dir, test_element;
14564 int kill_x = -1, kill_y = -1;
14566 if (!IN_LEV_FIELD(test_x, test_y))
14570 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14572 test_element = Feld[test_x][test_y];
14574 if (test_move_dir != bad_move_dir)
14576 /* good thing can be player or penguin that does not move away */
14577 if (IS_PLAYER(test_x, test_y))
14579 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14581 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14582 player as being hit when he is moving towards the bad thing, because
14583 the "get hit by" condition would be lost after the player stops) */
14584 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14585 return; /* player moves away from bad thing */
14590 else if (test_element == EL_PENGUIN)
14597 if (kill_x != -1 || kill_y != -1)
14599 if (IS_PLAYER(kill_x, kill_y))
14601 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14603 if (player->shield_deadly_time_left > 0 &&
14604 !IS_INDESTRUCTIBLE(bad_element))
14605 Bang(bad_x, bad_y);
14606 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14607 KillPlayer(player);
14610 Bang(kill_x, kill_y);
14614 void TestIfPlayerTouchesBadThing(int x, int y)
14616 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14619 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14621 TestIfGoodThingHitsBadThing(x, y, move_dir);
14624 void TestIfBadThingTouchesPlayer(int x, int y)
14626 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14629 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14631 TestIfBadThingHitsGoodThing(x, y, move_dir);
14634 void TestIfFriendTouchesBadThing(int x, int y)
14636 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14639 void TestIfBadThingTouchesFriend(int x, int y)
14641 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14644 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14646 int i, kill_x = bad_x, kill_y = bad_y;
14647 static int xy[4][2] =
14655 for (i = 0; i < NUM_DIRECTIONS; i++)
14659 x = bad_x + xy[i][0];
14660 y = bad_y + xy[i][1];
14661 if (!IN_LEV_FIELD(x, y))
14664 element = Feld[x][y];
14665 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14666 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14674 if (kill_x != bad_x || kill_y != bad_y)
14675 Bang(bad_x, bad_y);
14678 void KillPlayer(struct PlayerInfo *player)
14680 int jx = player->jx, jy = player->jy;
14682 if (!player->active)
14686 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14687 player->killed, player->active, player->reanimated);
14690 /* the following code was introduced to prevent an infinite loop when calling
14692 -> CheckTriggeredElementChangeExt()
14693 -> ExecuteCustomElementAction()
14695 -> (infinitely repeating the above sequence of function calls)
14696 which occurs when killing the player while having a CE with the setting
14697 "kill player X when explosion of <player X>"; the solution using a new
14698 field "player->killed" was chosen for backwards compatibility, although
14699 clever use of the fields "player->active" etc. would probably also work */
14701 if (player->killed)
14705 player->killed = TRUE;
14707 /* remove accessible field at the player's position */
14708 Feld[jx][jy] = EL_EMPTY;
14710 /* deactivate shield (else Bang()/Explode() would not work right) */
14711 player->shield_normal_time_left = 0;
14712 player->shield_deadly_time_left = 0;
14715 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14716 player->killed, player->active, player->reanimated);
14722 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14723 player->killed, player->active, player->reanimated);
14726 #if USE_PLAYER_REANIMATION
14728 if (player->reanimated) /* killed player may have been reanimated */
14729 player->killed = player->reanimated = FALSE;
14731 BuryPlayer(player);
14733 if (player->killed) /* player may have been reanimated */
14734 BuryPlayer(player);
14737 BuryPlayer(player);
14741 static void KillPlayerUnlessEnemyProtected(int x, int y)
14743 if (!PLAYER_ENEMY_PROTECTED(x, y))
14744 KillPlayer(PLAYERINFO(x, y));
14747 static void KillPlayerUnlessExplosionProtected(int x, int y)
14749 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14750 KillPlayer(PLAYERINFO(x, y));
14753 void BuryPlayer(struct PlayerInfo *player)
14755 int jx = player->jx, jy = player->jy;
14757 if (!player->active)
14760 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14761 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14763 player->GameOver = TRUE;
14764 RemovePlayer(player);
14767 void RemovePlayer(struct PlayerInfo *player)
14769 int jx = player->jx, jy = player->jy;
14770 int i, found = FALSE;
14772 player->present = FALSE;
14773 player->active = FALSE;
14775 if (!ExplodeField[jx][jy])
14776 StorePlayer[jx][jy] = 0;
14778 if (player->is_moving)
14779 TEST_DrawLevelField(player->last_jx, player->last_jy);
14781 for (i = 0; i < MAX_PLAYERS; i++)
14782 if (stored_player[i].active)
14786 AllPlayersGone = TRUE;
14792 #if USE_NEW_SNAP_DELAY
14793 static void setFieldForSnapping(int x, int y, int element, int direction)
14795 struct ElementInfo *ei = &element_info[element];
14796 int direction_bit = MV_DIR_TO_BIT(direction);
14797 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14798 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14799 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14801 Feld[x][y] = EL_ELEMENT_SNAPPING;
14802 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14804 ResetGfxAnimation(x, y);
14806 GfxElement[x][y] = element;
14807 GfxAction[x][y] = action;
14808 GfxDir[x][y] = direction;
14809 GfxFrame[x][y] = -1;
14814 =============================================================================
14815 checkDiagonalPushing()
14816 -----------------------------------------------------------------------------
14817 check if diagonal input device direction results in pushing of object
14818 (by checking if the alternative direction is walkable, diggable, ...)
14819 =============================================================================
14822 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14823 int x, int y, int real_dx, int real_dy)
14825 int jx, jy, dx, dy, xx, yy;
14827 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14830 /* diagonal direction: check alternative direction */
14835 xx = jx + (dx == 0 ? real_dx : 0);
14836 yy = jy + (dy == 0 ? real_dy : 0);
14838 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14842 =============================================================================
14844 -----------------------------------------------------------------------------
14845 x, y: field next to player (non-diagonal) to try to dig to
14846 real_dx, real_dy: direction as read from input device (can be diagonal)
14847 =============================================================================
14850 static int DigField(struct PlayerInfo *player,
14851 int oldx, int oldy, int x, int y,
14852 int real_dx, int real_dy, int mode)
14854 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14855 boolean player_was_pushing = player->is_pushing;
14856 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14857 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14858 int jx = oldx, jy = oldy;
14859 int dx = x - jx, dy = y - jy;
14860 int nextx = x + dx, nexty = y + dy;
14861 int move_direction = (dx == -1 ? MV_LEFT :
14862 dx == +1 ? MV_RIGHT :
14864 dy == +1 ? MV_DOWN : MV_NONE);
14865 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14866 int dig_side = MV_DIR_OPPOSITE(move_direction);
14867 int old_element = Feld[jx][jy];
14868 #if USE_FIXED_DONT_RUN_INTO
14869 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14875 if (is_player) /* function can also be called by EL_PENGUIN */
14877 if (player->MovPos == 0)
14879 player->is_digging = FALSE;
14880 player->is_collecting = FALSE;
14883 if (player->MovPos == 0) /* last pushing move finished */
14884 player->is_pushing = FALSE;
14886 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14888 player->is_switching = FALSE;
14889 player->push_delay = -1;
14891 return MP_NO_ACTION;
14895 #if !USE_FIXED_DONT_RUN_INTO
14896 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14897 return MP_NO_ACTION;
14900 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14901 old_element = Back[jx][jy];
14903 /* in case of element dropped at player position, check background */
14904 else if (Back[jx][jy] != EL_EMPTY &&
14905 game.engine_version >= VERSION_IDENT(2,2,0,0))
14906 old_element = Back[jx][jy];
14908 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14909 return MP_NO_ACTION; /* field has no opening in this direction */
14911 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14912 return MP_NO_ACTION; /* field has no opening in this direction */
14914 #if USE_FIXED_DONT_RUN_INTO
14915 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14919 Feld[jx][jy] = player->artwork_element;
14920 InitMovingField(jx, jy, MV_DOWN);
14921 Store[jx][jy] = EL_ACID;
14922 ContinueMoving(jx, jy);
14923 BuryPlayer(player);
14925 return MP_DONT_RUN_INTO;
14929 #if USE_FIXED_DONT_RUN_INTO
14930 if (player_can_move && DONT_RUN_INTO(element))
14932 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14934 return MP_DONT_RUN_INTO;
14938 #if USE_FIXED_DONT_RUN_INTO
14939 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14940 return MP_NO_ACTION;
14943 #if !USE_FIXED_DONT_RUN_INTO
14944 element = Feld[x][y];
14947 collect_count = element_info[element].collect_count_initial;
14949 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14950 return MP_NO_ACTION;
14952 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14953 player_can_move = player_can_move_or_snap;
14955 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14956 game.engine_version >= VERSION_IDENT(2,2,0,0))
14958 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14959 player->index_bit, dig_side);
14960 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14961 player->index_bit, dig_side);
14963 if (element == EL_DC_LANDMINE)
14966 if (Feld[x][y] != element) /* field changed by snapping */
14969 return MP_NO_ACTION;
14972 #if USE_PLAYER_GRAVITY
14973 if (player->gravity && is_player && !player->is_auto_moving &&
14974 canFallDown(player) && move_direction != MV_DOWN &&
14975 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14976 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14978 if (game.gravity && is_player && !player->is_auto_moving &&
14979 canFallDown(player) && move_direction != MV_DOWN &&
14980 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14981 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14984 if (player_can_move &&
14985 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14987 int sound_element = SND_ELEMENT(element);
14988 int sound_action = ACTION_WALKING;
14990 if (IS_RND_GATE(element))
14992 if (!player->key[RND_GATE_NR(element)])
14993 return MP_NO_ACTION;
14995 else if (IS_RND_GATE_GRAY(element))
14997 if (!player->key[RND_GATE_GRAY_NR(element)])
14998 return MP_NO_ACTION;
15000 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15002 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15003 return MP_NO_ACTION;
15005 else if (element == EL_EXIT_OPEN ||
15006 element == EL_EM_EXIT_OPEN ||
15008 element == EL_EM_EXIT_OPENING ||
15010 element == EL_STEEL_EXIT_OPEN ||
15011 element == EL_EM_STEEL_EXIT_OPEN ||
15013 element == EL_EM_STEEL_EXIT_OPENING ||
15015 element == EL_SP_EXIT_OPEN ||
15016 element == EL_SP_EXIT_OPENING)
15018 sound_action = ACTION_PASSING; /* player is passing exit */
15020 else if (element == EL_EMPTY)
15022 sound_action = ACTION_MOVING; /* nothing to walk on */
15025 /* play sound from background or player, whatever is available */
15026 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15027 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15029 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15031 else if (player_can_move &&
15032 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15034 if (!ACCESS_FROM(element, opposite_direction))
15035 return MP_NO_ACTION; /* field not accessible from this direction */
15037 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15038 return MP_NO_ACTION;
15040 if (IS_EM_GATE(element))
15042 if (!player->key[EM_GATE_NR(element)])
15043 return MP_NO_ACTION;
15045 else if (IS_EM_GATE_GRAY(element))
15047 if (!player->key[EM_GATE_GRAY_NR(element)])
15048 return MP_NO_ACTION;
15050 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15052 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15053 return MP_NO_ACTION;
15055 else if (IS_EMC_GATE(element))
15057 if (!player->key[EMC_GATE_NR(element)])
15058 return MP_NO_ACTION;
15060 else if (IS_EMC_GATE_GRAY(element))
15062 if (!player->key[EMC_GATE_GRAY_NR(element)])
15063 return MP_NO_ACTION;
15065 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15067 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15068 return MP_NO_ACTION;
15070 else if (element == EL_DC_GATE_WHITE ||
15071 element == EL_DC_GATE_WHITE_GRAY ||
15072 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15074 if (player->num_white_keys == 0)
15075 return MP_NO_ACTION;
15077 player->num_white_keys--;
15079 else if (IS_SP_PORT(element))
15081 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15082 element == EL_SP_GRAVITY_PORT_RIGHT ||
15083 element == EL_SP_GRAVITY_PORT_UP ||
15084 element == EL_SP_GRAVITY_PORT_DOWN)
15085 #if USE_PLAYER_GRAVITY
15086 player->gravity = !player->gravity;
15088 game.gravity = !game.gravity;
15090 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15091 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15092 element == EL_SP_GRAVITY_ON_PORT_UP ||
15093 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15094 #if USE_PLAYER_GRAVITY
15095 player->gravity = TRUE;
15097 game.gravity = TRUE;
15099 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15100 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15101 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15102 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15103 #if USE_PLAYER_GRAVITY
15104 player->gravity = FALSE;
15106 game.gravity = FALSE;
15110 /* automatically move to the next field with double speed */
15111 player->programmed_action = move_direction;
15113 if (player->move_delay_reset_counter == 0)
15115 player->move_delay_reset_counter = 2; /* two double speed steps */
15117 DOUBLE_PLAYER_SPEED(player);
15120 PlayLevelSoundAction(x, y, ACTION_PASSING);
15122 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15126 if (mode != DF_SNAP)
15128 GfxElement[x][y] = GFX_ELEMENT(element);
15129 player->is_digging = TRUE;
15132 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15134 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15135 player->index_bit, dig_side);
15137 if (mode == DF_SNAP)
15139 #if USE_NEW_SNAP_DELAY
15140 if (level.block_snap_field)
15141 setFieldForSnapping(x, y, element, move_direction);
15143 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15145 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15148 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15149 player->index_bit, dig_side);
15152 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15156 if (is_player && mode != DF_SNAP)
15158 GfxElement[x][y] = element;
15159 player->is_collecting = TRUE;
15162 if (element == EL_SPEED_PILL)
15164 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15166 else if (element == EL_EXTRA_TIME && level.time > 0)
15168 TimeLeft += level.extra_time;
15171 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15173 DisplayGameControlValues();
15175 DrawGameValue_Time(TimeLeft);
15178 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15180 player->shield_normal_time_left += level.shield_normal_time;
15181 if (element == EL_SHIELD_DEADLY)
15182 player->shield_deadly_time_left += level.shield_deadly_time;
15184 else if (element == EL_DYNAMITE ||
15185 element == EL_EM_DYNAMITE ||
15186 element == EL_SP_DISK_RED)
15188 if (player->inventory_size < MAX_INVENTORY_SIZE)
15189 player->inventory_element[player->inventory_size++] = element;
15191 DrawGameDoorValues();
15193 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15195 player->dynabomb_count++;
15196 player->dynabombs_left++;
15198 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15200 player->dynabomb_size++;
15202 else if (element == EL_DYNABOMB_INCREASE_POWER)
15204 player->dynabomb_xl = TRUE;
15206 else if (IS_KEY(element))
15208 player->key[KEY_NR(element)] = TRUE;
15210 DrawGameDoorValues();
15212 else if (element == EL_DC_KEY_WHITE)
15214 player->num_white_keys++;
15216 /* display white keys? */
15217 /* DrawGameDoorValues(); */
15219 else if (IS_ENVELOPE(element))
15221 player->show_envelope = element;
15223 else if (element == EL_EMC_LENSES)
15225 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15227 RedrawAllInvisibleElementsForLenses();
15229 else if (element == EL_EMC_MAGNIFIER)
15231 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15233 RedrawAllInvisibleElementsForMagnifier();
15235 else if (IS_DROPPABLE(element) ||
15236 IS_THROWABLE(element)) /* can be collected and dropped */
15240 if (collect_count == 0)
15241 player->inventory_infinite_element = element;
15243 for (i = 0; i < collect_count; i++)
15244 if (player->inventory_size < MAX_INVENTORY_SIZE)
15245 player->inventory_element[player->inventory_size++] = element;
15247 DrawGameDoorValues();
15249 else if (collect_count > 0)
15251 local_player->gems_still_needed -= collect_count;
15252 if (local_player->gems_still_needed < 0)
15253 local_player->gems_still_needed = 0;
15256 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15258 DisplayGameControlValues();
15260 DrawGameValue_Emeralds(local_player->gems_still_needed);
15264 RaiseScoreElement(element);
15265 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15268 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15269 player->index_bit, dig_side);
15271 if (mode == DF_SNAP)
15273 #if USE_NEW_SNAP_DELAY
15274 if (level.block_snap_field)
15275 setFieldForSnapping(x, y, element, move_direction);
15277 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15279 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15282 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15283 player->index_bit, dig_side);
15286 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15288 if (mode == DF_SNAP && element != EL_BD_ROCK)
15289 return MP_NO_ACTION;
15291 if (CAN_FALL(element) && dy)
15292 return MP_NO_ACTION;
15294 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15295 !(element == EL_SPRING && level.use_spring_bug))
15296 return MP_NO_ACTION;
15298 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15299 ((move_direction & MV_VERTICAL &&
15300 ((element_info[element].move_pattern & MV_LEFT &&
15301 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15302 (element_info[element].move_pattern & MV_RIGHT &&
15303 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15304 (move_direction & MV_HORIZONTAL &&
15305 ((element_info[element].move_pattern & MV_UP &&
15306 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15307 (element_info[element].move_pattern & MV_DOWN &&
15308 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15309 return MP_NO_ACTION;
15311 /* do not push elements already moving away faster than player */
15312 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15313 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15314 return MP_NO_ACTION;
15316 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15318 if (player->push_delay_value == -1 || !player_was_pushing)
15319 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15321 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15323 if (player->push_delay_value == -1)
15324 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15326 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15328 if (!player->is_pushing)
15329 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15332 player->is_pushing = TRUE;
15333 player->is_active = TRUE;
15335 if (!(IN_LEV_FIELD(nextx, nexty) &&
15336 (IS_FREE(nextx, nexty) ||
15337 (IS_SB_ELEMENT(element) &&
15338 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15339 (IS_CUSTOM_ELEMENT(element) &&
15340 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15341 return MP_NO_ACTION;
15343 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15344 return MP_NO_ACTION;
15346 if (player->push_delay == -1) /* new pushing; restart delay */
15347 player->push_delay = 0;
15349 if (player->push_delay < player->push_delay_value &&
15350 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15351 element != EL_SPRING && element != EL_BALLOON)
15353 /* make sure that there is no move delay before next try to push */
15354 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15355 player->move_delay = 0;
15357 return MP_NO_ACTION;
15360 if (IS_CUSTOM_ELEMENT(element) &&
15361 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15363 if (!DigFieldByCE(nextx, nexty, element))
15364 return MP_NO_ACTION;
15367 if (IS_SB_ELEMENT(element))
15369 if (element == EL_SOKOBAN_FIELD_FULL)
15371 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15372 local_player->sokobanfields_still_needed++;
15375 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15377 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15378 local_player->sokobanfields_still_needed--;
15381 Feld[x][y] = EL_SOKOBAN_OBJECT;
15383 if (Back[x][y] == Back[nextx][nexty])
15384 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15385 else if (Back[x][y] != 0)
15386 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15389 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15393 if (local_player->sokobanfields_still_needed == 0 &&
15394 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15396 if (local_player->sokobanfields_still_needed == 0 &&
15397 game.emulation == EMU_SOKOBAN)
15400 PlayerWins(player);
15402 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15406 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15408 InitMovingField(x, y, move_direction);
15409 GfxAction[x][y] = ACTION_PUSHING;
15411 if (mode == DF_SNAP)
15412 ContinueMoving(x, y);
15414 MovPos[x][y] = (dx != 0 ? dx : dy);
15416 Pushed[x][y] = TRUE;
15417 Pushed[nextx][nexty] = TRUE;
15419 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15420 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15422 player->push_delay_value = -1; /* get new value later */
15424 /* check for element change _after_ element has been pushed */
15425 if (game.use_change_when_pushing_bug)
15427 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15428 player->index_bit, dig_side);
15429 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15430 player->index_bit, dig_side);
15433 else if (IS_SWITCHABLE(element))
15435 if (PLAYER_SWITCHING(player, x, y))
15437 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15438 player->index_bit, dig_side);
15443 player->is_switching = TRUE;
15444 player->switch_x = x;
15445 player->switch_y = y;
15447 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15449 if (element == EL_ROBOT_WHEEL)
15451 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15455 game.robot_wheel_active = TRUE;
15457 TEST_DrawLevelField(x, y);
15459 else if (element == EL_SP_TERMINAL)
15463 SCAN_PLAYFIELD(xx, yy)
15465 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15467 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15468 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15471 else if (IS_BELT_SWITCH(element))
15473 ToggleBeltSwitch(x, y);
15475 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15476 element == EL_SWITCHGATE_SWITCH_DOWN ||
15477 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15478 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15480 ToggleSwitchgateSwitch(x, y);
15482 else if (element == EL_LIGHT_SWITCH ||
15483 element == EL_LIGHT_SWITCH_ACTIVE)
15485 ToggleLightSwitch(x, y);
15487 else if (element == EL_TIMEGATE_SWITCH ||
15488 element == EL_DC_TIMEGATE_SWITCH)
15490 ActivateTimegateSwitch(x, y);
15492 else if (element == EL_BALLOON_SWITCH_LEFT ||
15493 element == EL_BALLOON_SWITCH_RIGHT ||
15494 element == EL_BALLOON_SWITCH_UP ||
15495 element == EL_BALLOON_SWITCH_DOWN ||
15496 element == EL_BALLOON_SWITCH_NONE ||
15497 element == EL_BALLOON_SWITCH_ANY)
15499 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15500 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15501 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15502 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15503 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15506 else if (element == EL_LAMP)
15508 Feld[x][y] = EL_LAMP_ACTIVE;
15509 local_player->lights_still_needed--;
15511 ResetGfxAnimation(x, y);
15512 TEST_DrawLevelField(x, y);
15514 else if (element == EL_TIME_ORB_FULL)
15516 Feld[x][y] = EL_TIME_ORB_EMPTY;
15518 if (level.time > 0 || level.use_time_orb_bug)
15520 TimeLeft += level.time_orb_time;
15523 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15525 DisplayGameControlValues();
15527 DrawGameValue_Time(TimeLeft);
15531 ResetGfxAnimation(x, y);
15532 TEST_DrawLevelField(x, y);
15534 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15535 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15539 game.ball_state = !game.ball_state;
15541 SCAN_PLAYFIELD(xx, yy)
15543 int e = Feld[xx][yy];
15545 if (game.ball_state)
15547 if (e == EL_EMC_MAGIC_BALL)
15548 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15549 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15550 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15554 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15555 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15556 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15557 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15562 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15563 player->index_bit, dig_side);
15565 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15566 player->index_bit, dig_side);
15568 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15569 player->index_bit, dig_side);
15575 if (!PLAYER_SWITCHING(player, x, y))
15577 player->is_switching = TRUE;
15578 player->switch_x = x;
15579 player->switch_y = y;
15581 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15582 player->index_bit, dig_side);
15583 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15584 player->index_bit, dig_side);
15586 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15587 player->index_bit, dig_side);
15588 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15589 player->index_bit, dig_side);
15592 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15593 player->index_bit, dig_side);
15594 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15595 player->index_bit, dig_side);
15597 return MP_NO_ACTION;
15600 player->push_delay = -1;
15602 if (is_player) /* function can also be called by EL_PENGUIN */
15604 if (Feld[x][y] != element) /* really digged/collected something */
15606 player->is_collecting = !player->is_digging;
15607 player->is_active = TRUE;
15614 static boolean DigFieldByCE(int x, int y, int digging_element)
15616 int element = Feld[x][y];
15618 if (!IS_FREE(x, y))
15620 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15621 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15624 /* no element can dig solid indestructible elements */
15625 if (IS_INDESTRUCTIBLE(element) &&
15626 !IS_DIGGABLE(element) &&
15627 !IS_COLLECTIBLE(element))
15630 if (AmoebaNr[x][y] &&
15631 (element == EL_AMOEBA_FULL ||
15632 element == EL_BD_AMOEBA ||
15633 element == EL_AMOEBA_GROWING))
15635 AmoebaCnt[AmoebaNr[x][y]]--;
15636 AmoebaCnt2[AmoebaNr[x][y]]--;
15639 if (IS_MOVING(x, y))
15640 RemoveMovingField(x, y);
15644 TEST_DrawLevelField(x, y);
15647 /* if digged element was about to explode, prevent the explosion */
15648 ExplodeField[x][y] = EX_TYPE_NONE;
15650 PlayLevelSoundAction(x, y, action);
15653 Store[x][y] = EL_EMPTY;
15656 /* this makes it possible to leave the removed element again */
15657 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15658 Store[x][y] = element;
15660 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15662 int move_leave_element = element_info[digging_element].move_leave_element;
15664 /* this makes it possible to leave the removed element again */
15665 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15666 element : move_leave_element);
15673 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15675 int jx = player->jx, jy = player->jy;
15676 int x = jx + dx, y = jy + dy;
15677 int snap_direction = (dx == -1 ? MV_LEFT :
15678 dx == +1 ? MV_RIGHT :
15680 dy == +1 ? MV_DOWN : MV_NONE);
15681 boolean can_continue_snapping = (level.continuous_snapping &&
15682 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15684 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15687 if (!player->active || !IN_LEV_FIELD(x, y))
15695 if (player->MovPos == 0)
15696 player->is_pushing = FALSE;
15698 player->is_snapping = FALSE;
15700 if (player->MovPos == 0)
15702 player->is_moving = FALSE;
15703 player->is_digging = FALSE;
15704 player->is_collecting = FALSE;
15710 #if USE_NEW_CONTINUOUS_SNAPPING
15711 /* prevent snapping with already pressed snap key when not allowed */
15712 if (player->is_snapping && !can_continue_snapping)
15715 if (player->is_snapping)
15719 player->MovDir = snap_direction;
15721 if (player->MovPos == 0)
15723 player->is_moving = FALSE;
15724 player->is_digging = FALSE;
15725 player->is_collecting = FALSE;
15728 player->is_dropping = FALSE;
15729 player->is_dropping_pressed = FALSE;
15730 player->drop_pressed_delay = 0;
15732 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15735 player->is_snapping = TRUE;
15736 player->is_active = TRUE;
15738 if (player->MovPos == 0)
15740 player->is_moving = FALSE;
15741 player->is_digging = FALSE;
15742 player->is_collecting = FALSE;
15745 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15746 TEST_DrawLevelField(player->last_jx, player->last_jy);
15748 TEST_DrawLevelField(x, y);
15753 static boolean DropElement(struct PlayerInfo *player)
15755 int old_element, new_element;
15756 int dropx = player->jx, dropy = player->jy;
15757 int drop_direction = player->MovDir;
15758 int drop_side = drop_direction;
15760 int drop_element = get_next_dropped_element(player);
15762 int drop_element = (player->inventory_size > 0 ?
15763 player->inventory_element[player->inventory_size - 1] :
15764 player->inventory_infinite_element != EL_UNDEFINED ?
15765 player->inventory_infinite_element :
15766 player->dynabombs_left > 0 ?
15767 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15771 player->is_dropping_pressed = TRUE;
15773 /* do not drop an element on top of another element; when holding drop key
15774 pressed without moving, dropped element must move away before the next
15775 element can be dropped (this is especially important if the next element
15776 is dynamite, which can be placed on background for historical reasons) */
15777 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15780 if (IS_THROWABLE(drop_element))
15782 dropx += GET_DX_FROM_DIR(drop_direction);
15783 dropy += GET_DY_FROM_DIR(drop_direction);
15785 if (!IN_LEV_FIELD(dropx, dropy))
15789 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15790 new_element = drop_element; /* default: no change when dropping */
15792 /* check if player is active, not moving and ready to drop */
15793 if (!player->active || player->MovPos || player->drop_delay > 0)
15796 /* check if player has anything that can be dropped */
15797 if (new_element == EL_UNDEFINED)
15800 /* check if drop key was pressed long enough for EM style dynamite */
15801 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15804 /* check if anything can be dropped at the current position */
15805 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15808 /* collected custom elements can only be dropped on empty fields */
15809 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15812 if (old_element != EL_EMPTY)
15813 Back[dropx][dropy] = old_element; /* store old element on this field */
15815 ResetGfxAnimation(dropx, dropy);
15816 ResetRandomAnimationValue(dropx, dropy);
15818 if (player->inventory_size > 0 ||
15819 player->inventory_infinite_element != EL_UNDEFINED)
15821 if (player->inventory_size > 0)
15823 player->inventory_size--;
15825 DrawGameDoorValues();
15827 if (new_element == EL_DYNAMITE)
15828 new_element = EL_DYNAMITE_ACTIVE;
15829 else if (new_element == EL_EM_DYNAMITE)
15830 new_element = EL_EM_DYNAMITE_ACTIVE;
15831 else if (new_element == EL_SP_DISK_RED)
15832 new_element = EL_SP_DISK_RED_ACTIVE;
15835 Feld[dropx][dropy] = new_element;
15837 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15838 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15839 el2img(Feld[dropx][dropy]), 0);
15841 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15843 /* needed if previous element just changed to "empty" in the last frame */
15844 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15846 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15847 player->index_bit, drop_side);
15848 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15850 player->index_bit, drop_side);
15852 TestIfElementTouchesCustomElement(dropx, dropy);
15854 else /* player is dropping a dyna bomb */
15856 player->dynabombs_left--;
15858 Feld[dropx][dropy] = new_element;
15860 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15861 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15862 el2img(Feld[dropx][dropy]), 0);
15864 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15867 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15868 InitField_WithBug1(dropx, dropy, FALSE);
15870 new_element = Feld[dropx][dropy]; /* element might have changed */
15872 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15873 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15875 int move_direction, nextx, nexty;
15877 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15878 MovDir[dropx][dropy] = drop_direction;
15880 move_direction = MovDir[dropx][dropy];
15881 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15882 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15884 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15886 #if USE_FIX_IMPACT_COLLISION
15887 /* do not cause impact style collision by dropping elements that can fall */
15888 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15890 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15894 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15895 player->is_dropping = TRUE;
15897 player->drop_pressed_delay = 0;
15898 player->is_dropping_pressed = FALSE;
15900 player->drop_x = dropx;
15901 player->drop_y = dropy;
15906 /* ------------------------------------------------------------------------- */
15907 /* game sound playing functions */
15908 /* ------------------------------------------------------------------------- */
15910 static int *loop_sound_frame = NULL;
15911 static int *loop_sound_volume = NULL;
15913 void InitPlayLevelSound()
15915 int num_sounds = getSoundListSize();
15917 checked_free(loop_sound_frame);
15918 checked_free(loop_sound_volume);
15920 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15921 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15924 static void PlayLevelSound(int x, int y, int nr)
15926 int sx = SCREENX(x), sy = SCREENY(y);
15927 int volume, stereo_position;
15928 int max_distance = 8;
15929 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15931 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15932 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15935 if (!IN_LEV_FIELD(x, y) ||
15936 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15937 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15940 volume = SOUND_MAX_VOLUME;
15942 if (!IN_SCR_FIELD(sx, sy))
15944 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15945 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15947 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15950 stereo_position = (SOUND_MAX_LEFT +
15951 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15952 (SCR_FIELDX + 2 * max_distance));
15954 if (IS_LOOP_SOUND(nr))
15956 /* This assures that quieter loop sounds do not overwrite louder ones,
15957 while restarting sound volume comparison with each new game frame. */
15959 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15962 loop_sound_volume[nr] = volume;
15963 loop_sound_frame[nr] = FrameCounter;
15966 PlaySoundExt(nr, volume, stereo_position, type);
15969 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15971 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15972 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15973 y < LEVELY(BY1) ? LEVELY(BY1) :
15974 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15978 static void PlayLevelSoundAction(int x, int y, int action)
15980 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15983 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15985 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15987 if (sound_effect != SND_UNDEFINED)
15988 PlayLevelSound(x, y, sound_effect);
15991 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15994 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15996 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15997 PlayLevelSound(x, y, sound_effect);
16000 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16002 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16004 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16005 PlayLevelSound(x, y, sound_effect);
16008 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16010 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16012 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16013 StopSound(sound_effect);
16016 static void PlayLevelMusic()
16018 if (levelset.music[level_nr] != MUS_UNDEFINED)
16019 PlayMusic(levelset.music[level_nr]); /* from config file */
16021 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16024 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16026 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16027 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16028 int x = xx - 1 - offset;
16029 int y = yy - 1 - offset;
16034 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16038 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16042 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16046 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16050 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16054 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16058 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16061 case SAMPLE_android_clone:
16062 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16065 case SAMPLE_android_move:
16066 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16069 case SAMPLE_spring:
16070 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16074 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16078 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16081 case SAMPLE_eater_eat:
16082 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16086 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16089 case SAMPLE_collect:
16090 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16093 case SAMPLE_diamond:
16094 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16097 case SAMPLE_squash:
16098 /* !!! CHECK THIS !!! */
16100 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16102 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16106 case SAMPLE_wonderfall:
16107 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16111 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16115 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16119 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16123 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16127 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16131 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16134 case SAMPLE_wonder:
16135 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16139 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16142 case SAMPLE_exit_open:
16143 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16146 case SAMPLE_exit_leave:
16147 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16150 case SAMPLE_dynamite:
16151 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16155 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16159 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16163 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16167 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16171 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16175 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16179 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16185 void ChangeTime(int value)
16187 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16191 /* EMC game engine uses value from time counter of RND game engine */
16192 level.native_em_level->lev->time = *time;
16194 DrawGameValue_Time(*time);
16197 void RaiseScore(int value)
16199 /* EMC game engine and RND game engine have separate score counters */
16200 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16201 &level.native_em_level->lev->score : &local_player->score);
16205 DrawGameValue_Score(*score);
16209 void RaiseScore(int value)
16211 local_player->score += value;
16214 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16216 DisplayGameControlValues();
16218 DrawGameValue_Score(local_player->score);
16222 void RaiseScoreElement(int element)
16227 case EL_BD_DIAMOND:
16228 case EL_EMERALD_YELLOW:
16229 case EL_EMERALD_RED:
16230 case EL_EMERALD_PURPLE:
16231 case EL_SP_INFOTRON:
16232 RaiseScore(level.score[SC_EMERALD]);
16235 RaiseScore(level.score[SC_DIAMOND]);
16238 RaiseScore(level.score[SC_CRYSTAL]);
16241 RaiseScore(level.score[SC_PEARL]);
16244 case EL_BD_BUTTERFLY:
16245 case EL_SP_ELECTRON:
16246 RaiseScore(level.score[SC_BUG]);
16249 case EL_BD_FIREFLY:
16250 case EL_SP_SNIKSNAK:
16251 RaiseScore(level.score[SC_SPACESHIP]);
16254 case EL_DARK_YAMYAM:
16255 RaiseScore(level.score[SC_YAMYAM]);
16258 RaiseScore(level.score[SC_ROBOT]);
16261 RaiseScore(level.score[SC_PACMAN]);
16264 RaiseScore(level.score[SC_NUT]);
16267 case EL_EM_DYNAMITE:
16268 case EL_SP_DISK_RED:
16269 case EL_DYNABOMB_INCREASE_NUMBER:
16270 case EL_DYNABOMB_INCREASE_SIZE:
16271 case EL_DYNABOMB_INCREASE_POWER:
16272 RaiseScore(level.score[SC_DYNAMITE]);
16274 case EL_SHIELD_NORMAL:
16275 case EL_SHIELD_DEADLY:
16276 RaiseScore(level.score[SC_SHIELD]);
16278 case EL_EXTRA_TIME:
16279 RaiseScore(level.extra_time_score);
16293 case EL_DC_KEY_WHITE:
16294 RaiseScore(level.score[SC_KEY]);
16297 RaiseScore(element_info[element].collect_score);
16302 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16304 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16306 #if defined(NETWORK_AVALIABLE)
16307 if (options.network)
16308 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16317 FadeSkipNextFadeIn();
16319 fading = fading_none;
16323 OpenDoor(DOOR_CLOSE_1);
16326 game_status = GAME_MODE_MAIN;
16329 DrawAndFadeInMainMenu(REDRAW_FIELD);
16337 FadeOut(REDRAW_FIELD);
16340 game_status = GAME_MODE_MAIN;
16342 DrawAndFadeInMainMenu(REDRAW_FIELD);
16346 else /* continue playing the game */
16348 if (tape.playing && tape.deactivate_display)
16349 TapeDeactivateDisplayOff(TRUE);
16351 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16353 if (tape.playing && tape.deactivate_display)
16354 TapeDeactivateDisplayOn();
16358 void RequestQuitGame(boolean ask_if_really_quit)
16360 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16361 boolean skip_request = AllPlayersGone || quick_quit;
16363 RequestQuitGameExt(skip_request, quick_quit,
16364 "Do you really want to quit the game ?");
16368 /* ------------------------------------------------------------------------- */
16369 /* random generator functions */
16370 /* ------------------------------------------------------------------------- */
16372 unsigned int InitEngineRandom_RND(long seed)
16374 game.num_random_calls = 0;
16377 unsigned int rnd_seed = InitEngineRandom(seed);
16379 printf("::: START RND: %d\n", rnd_seed);
16384 return InitEngineRandom(seed);
16390 unsigned int RND(int max)
16394 game.num_random_calls++;
16396 return GetEngineRandom(max);
16403 /* ------------------------------------------------------------------------- */
16404 /* game engine snapshot handling functions */
16405 /* ------------------------------------------------------------------------- */
16407 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16409 struct EngineSnapshotInfo
16411 /* runtime values for custom element collect score */
16412 int collect_score[NUM_CUSTOM_ELEMENTS];
16414 /* runtime values for group element choice position */
16415 int choice_pos[NUM_GROUP_ELEMENTS];
16417 /* runtime values for belt position animations */
16418 int belt_graphic[4 * NUM_BELT_PARTS];
16419 int belt_anim_mode[4 * NUM_BELT_PARTS];
16422 struct EngineSnapshotNodeInfo
16429 static struct EngineSnapshotInfo engine_snapshot_rnd;
16430 static ListNode *engine_snapshot_list = NULL;
16431 static char *snapshot_level_identifier = NULL;
16432 static int snapshot_level_nr = -1;
16434 void FreeEngineSnapshot()
16436 while (engine_snapshot_list != NULL)
16437 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16440 setString(&snapshot_level_identifier, NULL);
16441 snapshot_level_nr = -1;
16444 static void SaveEngineSnapshotValues_RND()
16446 static int belt_base_active_element[4] =
16448 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16449 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16450 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16451 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16455 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16457 int element = EL_CUSTOM_START + i;
16459 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16462 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16464 int element = EL_GROUP_START + i;
16466 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16469 for (i = 0; i < 4; i++)
16471 for (j = 0; j < NUM_BELT_PARTS; j++)
16473 int element = belt_base_active_element[i] + j;
16474 int graphic = el2img(element);
16475 int anim_mode = graphic_info[graphic].anim_mode;
16477 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16478 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16483 static void LoadEngineSnapshotValues_RND()
16485 unsigned long num_random_calls = game.num_random_calls;
16488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16490 int element = EL_CUSTOM_START + i;
16492 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16495 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16497 int element = EL_GROUP_START + i;
16499 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16502 for (i = 0; i < 4; i++)
16504 for (j = 0; j < NUM_BELT_PARTS; j++)
16506 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16507 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16509 graphic_info[graphic].anim_mode = anim_mode;
16513 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16515 InitRND(tape.random_seed);
16516 for (i = 0; i < num_random_calls; i++)
16520 if (game.num_random_calls != num_random_calls)
16522 Error(ERR_INFO, "number of random calls out of sync");
16523 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16524 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16525 Error(ERR_EXIT, "this should not happen -- please debug");
16529 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16531 struct EngineSnapshotNodeInfo *bi =
16532 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16534 bi->buffer_orig = buffer;
16535 bi->buffer_copy = checked_malloc(size);
16538 memcpy(bi->buffer_copy, buffer, size);
16540 addNodeToList(&engine_snapshot_list, NULL, bi);
16543 void SaveEngineSnapshot()
16545 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16547 if (level_editor_test_game) /* do not save snapshots from editor */
16550 /* copy some special values to a structure better suited for the snapshot */
16552 SaveEngineSnapshotValues_RND();
16553 SaveEngineSnapshotValues_EM();
16555 /* save values stored in special snapshot structure */
16557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16560 /* save further RND engine values */
16562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16605 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16611 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16624 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16626 /* save level identification information */
16628 setString(&snapshot_level_identifier, leveldir_current->identifier);
16629 snapshot_level_nr = level_nr;
16632 ListNode *node = engine_snapshot_list;
16635 while (node != NULL)
16637 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16642 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16646 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16648 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16651 void LoadEngineSnapshot()
16653 ListNode *node = engine_snapshot_list;
16655 if (engine_snapshot_list == NULL)
16658 while (node != NULL)
16660 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16665 /* restore special values from snapshot structure */
16667 LoadEngineSnapshotValues_RND();
16668 LoadEngineSnapshotValues_EM();
16671 boolean CheckEngineSnapshot()
16673 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16674 snapshot_level_nr == level_nr);
16678 /* ---------- new game button stuff ---------------------------------------- */
16680 /* graphic position values for game buttons */
16681 #define GAME_BUTTON_XSIZE 30
16682 #define GAME_BUTTON_YSIZE 30
16683 #define GAME_BUTTON_XPOS 5
16684 #define GAME_BUTTON_YPOS 215
16685 #define SOUND_BUTTON_XPOS 5
16686 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16688 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16689 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16690 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16691 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16692 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16693 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16701 } gamebutton_info[NUM_GAME_BUTTONS] =
16705 &game.button.stop.x, &game.button.stop.y,
16706 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16711 &game.button.pause.x, &game.button.pause.y,
16712 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16713 GAME_CTRL_ID_PAUSE,
16717 &game.button.play.x, &game.button.play.y,
16718 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16723 &game.button.sound_music.x, &game.button.sound_music.y,
16724 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16725 SOUND_CTRL_ID_MUSIC,
16726 "background music on/off"
16729 &game.button.sound_loops.x, &game.button.sound_loops.y,
16730 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16731 SOUND_CTRL_ID_LOOPS,
16732 "sound loops on/off"
16735 &game.button.sound_simple.x,&game.button.sound_simple.y,
16736 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16737 SOUND_CTRL_ID_SIMPLE,
16738 "normal sounds on/off"
16742 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16747 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16748 GAME_CTRL_ID_PAUSE,
16752 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16757 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16758 SOUND_CTRL_ID_MUSIC,
16759 "background music on/off"
16762 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16763 SOUND_CTRL_ID_LOOPS,
16764 "sound loops on/off"
16767 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16768 SOUND_CTRL_ID_SIMPLE,
16769 "normal sounds on/off"
16774 void CreateGameButtons()
16778 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16780 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16781 struct GadgetInfo *gi;
16784 unsigned long event_mask;
16786 int gd_xoffset, gd_yoffset;
16787 int gd_x1, gd_x2, gd_y1, gd_y2;
16790 x = DX + *gamebutton_info[i].x;
16791 y = DY + *gamebutton_info[i].y;
16792 gd_xoffset = gamebutton_info[i].gd_x;
16793 gd_yoffset = gamebutton_info[i].gd_y;
16794 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16795 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16797 if (id == GAME_CTRL_ID_STOP ||
16798 id == GAME_CTRL_ID_PAUSE ||
16799 id == GAME_CTRL_ID_PLAY)
16801 button_type = GD_TYPE_NORMAL_BUTTON;
16803 event_mask = GD_EVENT_RELEASED;
16804 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16805 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16809 button_type = GD_TYPE_CHECK_BUTTON;
16811 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16812 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16813 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16814 event_mask = GD_EVENT_PRESSED;
16815 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16816 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16819 gi = CreateGadget(GDI_CUSTOM_ID, id,
16820 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16825 GDI_X, DX + gd_xoffset,
16826 GDI_Y, DY + gd_yoffset,
16828 GDI_WIDTH, GAME_BUTTON_XSIZE,
16829 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16830 GDI_TYPE, button_type,
16831 GDI_STATE, GD_BUTTON_UNPRESSED,
16832 GDI_CHECKED, checked,
16833 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16834 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16835 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16836 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16837 GDI_DIRECT_DRAW, FALSE,
16838 GDI_EVENT_MASK, event_mask,
16839 GDI_CALLBACK_ACTION, HandleGameButtons,
16843 Error(ERR_EXIT, "cannot create gadget");
16845 game_gadget[id] = gi;
16849 void FreeGameButtons()
16853 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16854 FreeGadget(game_gadget[i]);
16857 static void MapGameButtons()
16861 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862 MapGadget(game_gadget[i]);
16865 void UnmapGameButtons()
16869 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870 UnmapGadget(game_gadget[i]);
16873 void RedrawGameButtons()
16877 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878 RedrawGadget(game_gadget[i]);
16881 static void HandleGameButtons(struct GadgetInfo *gi)
16883 int id = gi->custom_id;
16885 if (game_status != GAME_MODE_PLAYING)
16890 case GAME_CTRL_ID_STOP:
16894 RequestQuitGame(TRUE);
16897 case GAME_CTRL_ID_PAUSE:
16898 if (options.network)
16900 #if defined(NETWORK_AVALIABLE)
16902 SendToServer_ContinuePlaying();
16904 SendToServer_PausePlaying();
16908 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16911 case GAME_CTRL_ID_PLAY:
16914 #if defined(NETWORK_AVALIABLE)
16915 if (options.network)
16916 SendToServer_ContinuePlaying();
16920 tape.pausing = FALSE;
16921 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16926 case SOUND_CTRL_ID_MUSIC:
16927 if (setup.sound_music)
16929 setup.sound_music = FALSE;
16932 else if (audio.music_available)
16934 setup.sound = setup.sound_music = TRUE;
16936 SetAudioMode(setup.sound);
16942 case SOUND_CTRL_ID_LOOPS:
16943 if (setup.sound_loops)
16944 setup.sound_loops = FALSE;
16945 else if (audio.loops_available)
16947 setup.sound = setup.sound_loops = TRUE;
16948 SetAudioMode(setup.sound);
16952 case SOUND_CTRL_ID_SIMPLE:
16953 if (setup.sound_simple)
16954 setup.sound_simple = FALSE;
16955 else if (audio.sound_available)
16957 setup.sound = setup.sound_simple = TRUE;
16958 SetAudioMode(setup.sound);