1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
63 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
65 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 1)
67 #if USE_DELAYED_GFX_REDRAW
68 #define TEST_DrawLevelField(x, y) \
69 GfxRedraw[x][y] |= GFX_REDRAW_TILE
70 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
71 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
72 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
73 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
74 #define TEST_DrawTwinkleOnField(x, y) \
75 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
77 #define TEST_DrawLevelField(x, y) \
79 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
80 DrawLevelFieldCrumbledSand(x, y)
81 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
82 DrawLevelFieldCrumbledSandNeighbours(x, y)
83 #define TEST_DrawTwinkleOnField(x, y) \
84 DrawTwinkleOnField(x, y)
93 /* for MovePlayer() */
94 #define MP_NO_ACTION 0
97 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
99 /* for ScrollPlayer() */
100 #define SCROLL_INIT 0
101 #define SCROLL_GO_ON 1
103 /* for Bang()/Explode() */
104 #define EX_PHASE_START 0
105 #define EX_TYPE_NONE 0
106 #define EX_TYPE_NORMAL (1 << 0)
107 #define EX_TYPE_CENTER (1 << 1)
108 #define EX_TYPE_BORDER (1 << 2)
109 #define EX_TYPE_CROSS (1 << 3)
110 #define EX_TYPE_DYNA (1 << 4)
111 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
113 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
114 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
115 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
116 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
118 /* special positions in the game control window (relative to control window) */
119 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
120 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
121 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
122 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
123 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
124 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
125 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
126 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
127 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
128 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
129 #define XX_SCORE (PANEL_XPOS(game.panel.score))
130 #define YY_SCORE (PANEL_YPOS(game.panel.score))
131 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
132 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
133 #define XX_TIME (PANEL_XPOS(game.panel.time))
134 #define YY_TIME (PANEL_YPOS(game.panel.time))
136 /* special positions in the game control window (relative to main window) */
137 #define DX_LEVEL1 (DX + XX_LEVEL1)
138 #define DX_LEVEL2 (DX + XX_LEVEL2)
139 #define DX_LEVEL (DX + XX_LEVEL)
140 #define DY_LEVEL (DY + YY_LEVEL)
141 #define DX_EMERALDS (DX + XX_EMERALDS)
142 #define DY_EMERALDS (DY + YY_EMERALDS)
143 #define DX_DYNAMITE (DX + XX_DYNAMITE)
144 #define DY_DYNAMITE (DY + YY_DYNAMITE)
145 #define DX_KEYS (DX + XX_KEYS)
146 #define DY_KEYS (DY + YY_KEYS)
147 #define DX_SCORE (DX + XX_SCORE)
148 #define DY_SCORE (DY + YY_SCORE)
149 #define DX_TIME1 (DX + XX_TIME1)
150 #define DX_TIME2 (DX + XX_TIME2)
151 #define DX_TIME (DX + XX_TIME)
152 #define DY_TIME (DY + YY_TIME)
155 /* game panel display and control definitions */
157 #define GAME_PANEL_LEVEL_NUMBER 0
158 #define GAME_PANEL_GEMS 1
159 #define GAME_PANEL_INVENTORY_COUNT 2
160 #define GAME_PANEL_INVENTORY_FIRST_1 3
161 #define GAME_PANEL_INVENTORY_FIRST_2 4
162 #define GAME_PANEL_INVENTORY_FIRST_3 5
163 #define GAME_PANEL_INVENTORY_FIRST_4 6
164 #define GAME_PANEL_INVENTORY_FIRST_5 7
165 #define GAME_PANEL_INVENTORY_FIRST_6 8
166 #define GAME_PANEL_INVENTORY_FIRST_7 9
167 #define GAME_PANEL_INVENTORY_FIRST_8 10
168 #define GAME_PANEL_INVENTORY_LAST_1 11
169 #define GAME_PANEL_INVENTORY_LAST_2 12
170 #define GAME_PANEL_INVENTORY_LAST_3 13
171 #define GAME_PANEL_INVENTORY_LAST_4 14
172 #define GAME_PANEL_INVENTORY_LAST_5 15
173 #define GAME_PANEL_INVENTORY_LAST_6 16
174 #define GAME_PANEL_INVENTORY_LAST_7 17
175 #define GAME_PANEL_INVENTORY_LAST_8 18
176 #define GAME_PANEL_KEY_1 19
177 #define GAME_PANEL_KEY_2 20
178 #define GAME_PANEL_KEY_3 21
179 #define GAME_PANEL_KEY_4 22
180 #define GAME_PANEL_KEY_5 23
181 #define GAME_PANEL_KEY_6 24
182 #define GAME_PANEL_KEY_7 25
183 #define GAME_PANEL_KEY_8 26
184 #define GAME_PANEL_KEY_WHITE 27
185 #define GAME_PANEL_KEY_WHITE_COUNT 28
186 #define GAME_PANEL_SCORE 29
187 #define GAME_PANEL_HIGHSCORE 30
188 #define GAME_PANEL_TIME 31
189 #define GAME_PANEL_TIME_HH 32
190 #define GAME_PANEL_TIME_MM 33
191 #define GAME_PANEL_TIME_SS 34
192 #define GAME_PANEL_SHIELD_NORMAL 35
193 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
194 #define GAME_PANEL_SHIELD_DEADLY 37
195 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
196 #define GAME_PANEL_EXIT 39
197 #define GAME_PANEL_EMC_MAGIC_BALL 40
198 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
199 #define GAME_PANEL_LIGHT_SWITCH 42
200 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
201 #define GAME_PANEL_TIMEGATE_SWITCH 44
202 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
203 #define GAME_PANEL_SWITCHGATE_SWITCH 46
204 #define GAME_PANEL_EMC_LENSES 47
205 #define GAME_PANEL_EMC_LENSES_TIME 48
206 #define GAME_PANEL_EMC_MAGNIFIER 49
207 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
208 #define GAME_PANEL_BALLOON_SWITCH 51
209 #define GAME_PANEL_DYNABOMB_NUMBER 52
210 #define GAME_PANEL_DYNABOMB_SIZE 53
211 #define GAME_PANEL_DYNABOMB_POWER 54
212 #define GAME_PANEL_PENGUINS 55
213 #define GAME_PANEL_SOKOBAN_OBJECTS 56
214 #define GAME_PANEL_SOKOBAN_FIELDS 57
215 #define GAME_PANEL_ROBOT_WHEEL 58
216 #define GAME_PANEL_CONVEYOR_BELT_1 59
217 #define GAME_PANEL_CONVEYOR_BELT_2 60
218 #define GAME_PANEL_CONVEYOR_BELT_3 61
219 #define GAME_PANEL_CONVEYOR_BELT_4 62
220 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
221 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
222 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
223 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
224 #define GAME_PANEL_MAGIC_WALL 67
225 #define GAME_PANEL_MAGIC_WALL_TIME 68
226 #define GAME_PANEL_GRAVITY_STATE 69
227 #define GAME_PANEL_GRAPHIC_1 70
228 #define GAME_PANEL_GRAPHIC_2 71
229 #define GAME_PANEL_GRAPHIC_3 72
230 #define GAME_PANEL_GRAPHIC_4 73
231 #define GAME_PANEL_GRAPHIC_5 74
232 #define GAME_PANEL_GRAPHIC_6 75
233 #define GAME_PANEL_GRAPHIC_7 76
234 #define GAME_PANEL_GRAPHIC_8 77
235 #define GAME_PANEL_ELEMENT_1 78
236 #define GAME_PANEL_ELEMENT_2 79
237 #define GAME_PANEL_ELEMENT_3 80
238 #define GAME_PANEL_ELEMENT_4 81
239 #define GAME_PANEL_ELEMENT_5 82
240 #define GAME_PANEL_ELEMENT_6 83
241 #define GAME_PANEL_ELEMENT_7 84
242 #define GAME_PANEL_ELEMENT_8 85
243 #define GAME_PANEL_ELEMENT_COUNT_1 86
244 #define GAME_PANEL_ELEMENT_COUNT_2 87
245 #define GAME_PANEL_ELEMENT_COUNT_3 88
246 #define GAME_PANEL_ELEMENT_COUNT_4 89
247 #define GAME_PANEL_ELEMENT_COUNT_5 90
248 #define GAME_PANEL_ELEMENT_COUNT_6 91
249 #define GAME_PANEL_ELEMENT_COUNT_7 92
250 #define GAME_PANEL_ELEMENT_COUNT_8 93
251 #define GAME_PANEL_CE_SCORE_1 94
252 #define GAME_PANEL_CE_SCORE_2 95
253 #define GAME_PANEL_CE_SCORE_3 96
254 #define GAME_PANEL_CE_SCORE_4 97
255 #define GAME_PANEL_CE_SCORE_5 98
256 #define GAME_PANEL_CE_SCORE_6 99
257 #define GAME_PANEL_CE_SCORE_7 100
258 #define GAME_PANEL_CE_SCORE_8 101
259 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
260 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
261 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
262 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
263 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
264 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
265 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
266 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
267 #define GAME_PANEL_PLAYER_NAME 110
268 #define GAME_PANEL_LEVEL_NAME 111
269 #define GAME_PANEL_LEVEL_AUTHOR 112
271 #define NUM_GAME_PANEL_CONTROLS 113
273 struct GamePanelOrderInfo
279 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
281 struct GamePanelControlInfo
285 struct TextPosInfo *pos;
288 int value, last_value;
289 int frame, last_frame;
294 static struct GamePanelControlInfo game_panel_controls[] =
297 GAME_PANEL_LEVEL_NUMBER,
298 &game.panel.level_number,
307 GAME_PANEL_INVENTORY_COUNT,
308 &game.panel.inventory_count,
312 GAME_PANEL_INVENTORY_FIRST_1,
313 &game.panel.inventory_first[0],
317 GAME_PANEL_INVENTORY_FIRST_2,
318 &game.panel.inventory_first[1],
322 GAME_PANEL_INVENTORY_FIRST_3,
323 &game.panel.inventory_first[2],
327 GAME_PANEL_INVENTORY_FIRST_4,
328 &game.panel.inventory_first[3],
332 GAME_PANEL_INVENTORY_FIRST_5,
333 &game.panel.inventory_first[4],
337 GAME_PANEL_INVENTORY_FIRST_6,
338 &game.panel.inventory_first[5],
342 GAME_PANEL_INVENTORY_FIRST_7,
343 &game.panel.inventory_first[6],
347 GAME_PANEL_INVENTORY_FIRST_8,
348 &game.panel.inventory_first[7],
352 GAME_PANEL_INVENTORY_LAST_1,
353 &game.panel.inventory_last[0],
357 GAME_PANEL_INVENTORY_LAST_2,
358 &game.panel.inventory_last[1],
362 GAME_PANEL_INVENTORY_LAST_3,
363 &game.panel.inventory_last[2],
367 GAME_PANEL_INVENTORY_LAST_4,
368 &game.panel.inventory_last[3],
372 GAME_PANEL_INVENTORY_LAST_5,
373 &game.panel.inventory_last[4],
377 GAME_PANEL_INVENTORY_LAST_6,
378 &game.panel.inventory_last[5],
382 GAME_PANEL_INVENTORY_LAST_7,
383 &game.panel.inventory_last[6],
387 GAME_PANEL_INVENTORY_LAST_8,
388 &game.panel.inventory_last[7],
432 GAME_PANEL_KEY_WHITE,
433 &game.panel.key_white,
437 GAME_PANEL_KEY_WHITE_COUNT,
438 &game.panel.key_white_count,
447 GAME_PANEL_HIGHSCORE,
448 &game.panel.highscore,
472 GAME_PANEL_SHIELD_NORMAL,
473 &game.panel.shield_normal,
477 GAME_PANEL_SHIELD_NORMAL_TIME,
478 &game.panel.shield_normal_time,
482 GAME_PANEL_SHIELD_DEADLY,
483 &game.panel.shield_deadly,
487 GAME_PANEL_SHIELD_DEADLY_TIME,
488 &game.panel.shield_deadly_time,
497 GAME_PANEL_EMC_MAGIC_BALL,
498 &game.panel.emc_magic_ball,
502 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
503 &game.panel.emc_magic_ball_switch,
507 GAME_PANEL_LIGHT_SWITCH,
508 &game.panel.light_switch,
512 GAME_PANEL_LIGHT_SWITCH_TIME,
513 &game.panel.light_switch_time,
517 GAME_PANEL_TIMEGATE_SWITCH,
518 &game.panel.timegate_switch,
522 GAME_PANEL_TIMEGATE_SWITCH_TIME,
523 &game.panel.timegate_switch_time,
527 GAME_PANEL_SWITCHGATE_SWITCH,
528 &game.panel.switchgate_switch,
532 GAME_PANEL_EMC_LENSES,
533 &game.panel.emc_lenses,
537 GAME_PANEL_EMC_LENSES_TIME,
538 &game.panel.emc_lenses_time,
542 GAME_PANEL_EMC_MAGNIFIER,
543 &game.panel.emc_magnifier,
547 GAME_PANEL_EMC_MAGNIFIER_TIME,
548 &game.panel.emc_magnifier_time,
552 GAME_PANEL_BALLOON_SWITCH,
553 &game.panel.balloon_switch,
557 GAME_PANEL_DYNABOMB_NUMBER,
558 &game.panel.dynabomb_number,
562 GAME_PANEL_DYNABOMB_SIZE,
563 &game.panel.dynabomb_size,
567 GAME_PANEL_DYNABOMB_POWER,
568 &game.panel.dynabomb_power,
573 &game.panel.penguins,
577 GAME_PANEL_SOKOBAN_OBJECTS,
578 &game.panel.sokoban_objects,
582 GAME_PANEL_SOKOBAN_FIELDS,
583 &game.panel.sokoban_fields,
587 GAME_PANEL_ROBOT_WHEEL,
588 &game.panel.robot_wheel,
592 GAME_PANEL_CONVEYOR_BELT_1,
593 &game.panel.conveyor_belt[0],
597 GAME_PANEL_CONVEYOR_BELT_2,
598 &game.panel.conveyor_belt[1],
602 GAME_PANEL_CONVEYOR_BELT_3,
603 &game.panel.conveyor_belt[2],
607 GAME_PANEL_CONVEYOR_BELT_4,
608 &game.panel.conveyor_belt[3],
612 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
613 &game.panel.conveyor_belt_switch[0],
617 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
618 &game.panel.conveyor_belt_switch[1],
622 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
623 &game.panel.conveyor_belt_switch[2],
627 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
628 &game.panel.conveyor_belt_switch[3],
632 GAME_PANEL_MAGIC_WALL,
633 &game.panel.magic_wall,
637 GAME_PANEL_MAGIC_WALL_TIME,
638 &game.panel.magic_wall_time,
642 GAME_PANEL_GRAVITY_STATE,
643 &game.panel.gravity_state,
647 GAME_PANEL_GRAPHIC_1,
648 &game.panel.graphic[0],
652 GAME_PANEL_GRAPHIC_2,
653 &game.panel.graphic[1],
657 GAME_PANEL_GRAPHIC_3,
658 &game.panel.graphic[2],
662 GAME_PANEL_GRAPHIC_4,
663 &game.panel.graphic[3],
667 GAME_PANEL_GRAPHIC_5,
668 &game.panel.graphic[4],
672 GAME_PANEL_GRAPHIC_6,
673 &game.panel.graphic[5],
677 GAME_PANEL_GRAPHIC_7,
678 &game.panel.graphic[6],
682 GAME_PANEL_GRAPHIC_8,
683 &game.panel.graphic[7],
687 GAME_PANEL_ELEMENT_1,
688 &game.panel.element[0],
692 GAME_PANEL_ELEMENT_2,
693 &game.panel.element[1],
697 GAME_PANEL_ELEMENT_3,
698 &game.panel.element[2],
702 GAME_PANEL_ELEMENT_4,
703 &game.panel.element[3],
707 GAME_PANEL_ELEMENT_5,
708 &game.panel.element[4],
712 GAME_PANEL_ELEMENT_6,
713 &game.panel.element[5],
717 GAME_PANEL_ELEMENT_7,
718 &game.panel.element[6],
722 GAME_PANEL_ELEMENT_8,
723 &game.panel.element[7],
727 GAME_PANEL_ELEMENT_COUNT_1,
728 &game.panel.element_count[0],
732 GAME_PANEL_ELEMENT_COUNT_2,
733 &game.panel.element_count[1],
737 GAME_PANEL_ELEMENT_COUNT_3,
738 &game.panel.element_count[2],
742 GAME_PANEL_ELEMENT_COUNT_4,
743 &game.panel.element_count[3],
747 GAME_PANEL_ELEMENT_COUNT_5,
748 &game.panel.element_count[4],
752 GAME_PANEL_ELEMENT_COUNT_6,
753 &game.panel.element_count[5],
757 GAME_PANEL_ELEMENT_COUNT_7,
758 &game.panel.element_count[6],
762 GAME_PANEL_ELEMENT_COUNT_8,
763 &game.panel.element_count[7],
767 GAME_PANEL_CE_SCORE_1,
768 &game.panel.ce_score[0],
772 GAME_PANEL_CE_SCORE_2,
773 &game.panel.ce_score[1],
777 GAME_PANEL_CE_SCORE_3,
778 &game.panel.ce_score[2],
782 GAME_PANEL_CE_SCORE_4,
783 &game.panel.ce_score[3],
787 GAME_PANEL_CE_SCORE_5,
788 &game.panel.ce_score[4],
792 GAME_PANEL_CE_SCORE_6,
793 &game.panel.ce_score[5],
797 GAME_PANEL_CE_SCORE_7,
798 &game.panel.ce_score[6],
802 GAME_PANEL_CE_SCORE_8,
803 &game.panel.ce_score[7],
807 GAME_PANEL_CE_SCORE_1_ELEMENT,
808 &game.panel.ce_score_element[0],
812 GAME_PANEL_CE_SCORE_2_ELEMENT,
813 &game.panel.ce_score_element[1],
817 GAME_PANEL_CE_SCORE_3_ELEMENT,
818 &game.panel.ce_score_element[2],
822 GAME_PANEL_CE_SCORE_4_ELEMENT,
823 &game.panel.ce_score_element[3],
827 GAME_PANEL_CE_SCORE_5_ELEMENT,
828 &game.panel.ce_score_element[4],
832 GAME_PANEL_CE_SCORE_6_ELEMENT,
833 &game.panel.ce_score_element[5],
837 GAME_PANEL_CE_SCORE_7_ELEMENT,
838 &game.panel.ce_score_element[6],
842 GAME_PANEL_CE_SCORE_8_ELEMENT,
843 &game.panel.ce_score_element[7],
847 GAME_PANEL_PLAYER_NAME,
848 &game.panel.player_name,
852 GAME_PANEL_LEVEL_NAME,
853 &game.panel.level_name,
857 GAME_PANEL_LEVEL_AUTHOR,
858 &game.panel.level_author,
871 /* values for delayed check of falling and moving elements and for collision */
872 #define CHECK_DELAY_MOVING 3
873 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
874 #define CHECK_DELAY_COLLISION 2
875 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
877 /* values for initial player move delay (initial delay counter value) */
878 #define INITIAL_MOVE_DELAY_OFF -1
879 #define INITIAL_MOVE_DELAY_ON 0
881 /* values for player movement speed (which is in fact a delay value) */
882 #define MOVE_DELAY_MIN_SPEED 32
883 #define MOVE_DELAY_NORMAL_SPEED 8
884 #define MOVE_DELAY_HIGH_SPEED 4
885 #define MOVE_DELAY_MAX_SPEED 1
887 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
888 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
890 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
891 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
893 /* values for other actions */
894 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
895 #define MOVE_STEPSIZE_MIN (1)
896 #define MOVE_STEPSIZE_MAX (TILEX)
898 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
899 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
901 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
903 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
904 RND(element_info[e].push_delay_random))
905 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
906 RND(element_info[e].drop_delay_random))
907 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
908 RND(element_info[e].move_delay_random))
909 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
910 (element_info[e].move_delay_random))
911 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
912 RND(element_info[e].ce_value_random_initial))
913 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
914 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
915 RND((c)->delay_random * (c)->delay_frames))
916 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
917 RND((c)->delay_random))
920 #define GET_VALID_RUNTIME_ELEMENT(e) \
921 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
923 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
924 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
925 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
926 (be) + (e) - EL_SELF)
928 #define GET_PLAYER_FROM_BITS(p) \
929 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
931 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
932 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
933 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
934 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
935 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
936 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
937 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
938 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
939 RESOLVED_REFERENCE_ELEMENT(be, e) : \
942 #define CAN_GROW_INTO(e) \
943 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
945 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
946 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
949 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
950 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
951 (CAN_MOVE_INTO_ACID(e) && \
952 Feld[x][y] == EL_ACID) || \
955 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
956 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
957 (CAN_MOVE_INTO_ACID(e) && \
958 Feld[x][y] == EL_ACID) || \
961 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
962 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
964 (CAN_MOVE_INTO_ACID(e) && \
965 Feld[x][y] == EL_ACID) || \
966 (DONT_COLLIDE_WITH(e) && \
968 !PLAYER_ENEMY_PROTECTED(x, y))))
970 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
971 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
973 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
974 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
976 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
977 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
979 #define ANDROID_CAN_CLONE_FIELD(x, y) \
980 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
981 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
983 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
984 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
986 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
987 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
989 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
990 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
992 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
993 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
995 #define PIG_CAN_ENTER_FIELD(e, x, y) \
996 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
998 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
999 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1000 Feld[x][y] == EL_EM_EXIT_OPEN || \
1001 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1002 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1003 IS_FOOD_PENGUIN(Feld[x][y])))
1004 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1005 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1007 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1008 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1010 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1011 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1013 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1014 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1015 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1017 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1019 #define CE_ENTER_FIELD_COND(e, x, y) \
1020 (!IS_PLAYER(x, y) && \
1021 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1023 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1024 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1026 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1027 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1029 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1030 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1031 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1032 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1034 /* game button identifiers */
1035 #define GAME_CTRL_ID_STOP 0
1036 #define GAME_CTRL_ID_PAUSE 1
1037 #define GAME_CTRL_ID_PLAY 2
1038 #define SOUND_CTRL_ID_MUSIC 3
1039 #define SOUND_CTRL_ID_LOOPS 4
1040 #define SOUND_CTRL_ID_SIMPLE 5
1042 #define NUM_GAME_BUTTONS 6
1045 /* forward declaration for internal use */
1047 static void CreateField(int, int, int);
1049 static void ResetGfxAnimation(int, int);
1051 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1052 static void AdvanceFrameAndPlayerCounters(int);
1054 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1055 static boolean MovePlayer(struct PlayerInfo *, int, int);
1056 static void ScrollPlayer(struct PlayerInfo *, int);
1057 static void ScrollScreen(struct PlayerInfo *, int);
1059 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1060 static boolean DigFieldByCE(int, int, int);
1061 static boolean SnapField(struct PlayerInfo *, int, int);
1062 static boolean DropElement(struct PlayerInfo *);
1064 static void InitBeltMovement(void);
1065 static void CloseAllOpenTimegates(void);
1066 static void CheckGravityMovement(struct PlayerInfo *);
1067 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1068 static void KillPlayerUnlessEnemyProtected(int, int);
1069 static void KillPlayerUnlessExplosionProtected(int, int);
1071 static void TestIfPlayerTouchesCustomElement(int, int);
1072 static void TestIfElementTouchesCustomElement(int, int);
1073 static void TestIfElementHitsCustomElement(int, int, int);
1075 static void TestIfElementSmashesCustomElement(int, int, int);
1078 static void HandleElementChange(int, int, int);
1079 static void ExecuteCustomElementAction(int, int, int, int);
1080 static boolean ChangeElement(int, int, int, int);
1082 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1083 #define CheckTriggeredElementChange(x, y, e, ev) \
1084 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1085 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1086 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1087 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1088 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1089 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1090 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1092 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1093 #define CheckElementChange(x, y, e, te, ev) \
1094 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1095 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1096 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1097 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1098 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1100 static void PlayLevelSound(int, int, int);
1101 static void PlayLevelSoundNearest(int, int, int);
1102 static void PlayLevelSoundAction(int, int, int);
1103 static void PlayLevelSoundElementAction(int, int, int, int);
1104 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1105 static void PlayLevelSoundActionIfLoop(int, int, int);
1106 static void StopLevelSoundActionIfLoop(int, int, int);
1107 static void PlayLevelMusic();
1109 static void MapGameButtons();
1110 static void HandleGameButtons(struct GadgetInfo *);
1112 int AmoebeNachbarNr(int, int);
1113 void AmoebeUmwandeln(int, int);
1114 void ContinueMoving(int, int);
1115 void Bang(int, int);
1116 void InitMovDir(int, int);
1117 void InitAmoebaNr(int, int);
1118 int NewHiScore(void);
1120 void TestIfGoodThingHitsBadThing(int, int, int);
1121 void TestIfBadThingHitsGoodThing(int, int, int);
1122 void TestIfPlayerTouchesBadThing(int, int);
1123 void TestIfPlayerRunsIntoBadThing(int, int, int);
1124 void TestIfBadThingTouchesPlayer(int, int);
1125 void TestIfBadThingRunsIntoPlayer(int, int, int);
1126 void TestIfFriendTouchesBadThing(int, int);
1127 void TestIfBadThingTouchesFriend(int, int);
1128 void TestIfBadThingTouchesOtherBadThing(int, int);
1130 void KillPlayer(struct PlayerInfo *);
1131 void BuryPlayer(struct PlayerInfo *);
1132 void RemovePlayer(struct PlayerInfo *);
1134 static int getInvisibleActiveFromInvisibleElement(int);
1135 static int getInvisibleFromInvisibleActiveElement(int);
1137 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1139 /* for detection of endless loops, caused by custom element programming */
1140 /* (using maximal playfield width x 10 is just a rough approximation) */
1141 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1143 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1145 if (recursion_loop_detected) \
1148 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1150 recursion_loop_detected = TRUE; \
1151 recursion_loop_element = (e); \
1154 recursion_loop_depth++; \
1157 #define RECURSION_LOOP_DETECTION_END() \
1159 recursion_loop_depth--; \
1162 static int recursion_loop_depth;
1163 static boolean recursion_loop_detected;
1164 static boolean recursion_loop_element;
1167 /* ------------------------------------------------------------------------- */
1168 /* definition of elements that automatically change to other elements after */
1169 /* a specified time, eventually calling a function when changing */
1170 /* ------------------------------------------------------------------------- */
1172 /* forward declaration for changer functions */
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1190 struct ChangingElementInfo
1195 void (*pre_change_function)(int x, int y);
1196 void (*change_function)(int x, int y);
1197 void (*post_change_function)(int x, int y);
1200 static struct ChangingElementInfo change_delay_list[] =
1235 EL_STEEL_EXIT_OPENING,
1243 EL_STEEL_EXIT_CLOSING,
1244 EL_STEEL_EXIT_CLOSED,
1271 EL_EM_STEEL_EXIT_OPENING,
1272 EL_EM_STEEL_EXIT_OPEN,
1279 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_EM_STEEL_EXIT_CLOSED,
1307 EL_SWITCHGATE_OPENING,
1315 EL_SWITCHGATE_CLOSING,
1316 EL_SWITCHGATE_CLOSED,
1323 EL_TIMEGATE_OPENING,
1331 EL_TIMEGATE_CLOSING,
1340 EL_ACID_SPLASH_LEFT,
1348 EL_ACID_SPLASH_RIGHT,
1357 EL_SP_BUGGY_BASE_ACTIVATING,
1364 EL_SP_BUGGY_BASE_ACTIVATING,
1365 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_SP_BUGGY_BASE_ACTIVE,
1396 EL_ROBOT_WHEEL_ACTIVE,
1404 EL_TIMEGATE_SWITCH_ACTIVE,
1412 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1413 EL_DC_TIMEGATE_SWITCH,
1420 EL_EMC_MAGIC_BALL_ACTIVE,
1421 EL_EMC_MAGIC_BALL_ACTIVE,
1428 EL_EMC_SPRING_BUMPER_ACTIVE,
1429 EL_EMC_SPRING_BUMPER,
1436 EL_DIAGONAL_SHRINKING,
1444 EL_DIAGONAL_GROWING,
1465 int push_delay_fixed, push_delay_random;
1469 { EL_SPRING, 0, 0 },
1470 { EL_BALLOON, 0, 0 },
1472 { EL_SOKOBAN_OBJECT, 2, 0 },
1473 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1474 { EL_SATELLITE, 2, 0 },
1475 { EL_SP_DISK_YELLOW, 2, 0 },
1477 { EL_UNDEFINED, 0, 0 },
1485 move_stepsize_list[] =
1487 { EL_AMOEBA_DROP, 2 },
1488 { EL_AMOEBA_DROPPING, 2 },
1489 { EL_QUICKSAND_FILLING, 1 },
1490 { EL_QUICKSAND_EMPTYING, 1 },
1491 { EL_QUICKSAND_FAST_FILLING, 2 },
1492 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1493 { EL_MAGIC_WALL_FILLING, 2 },
1494 { EL_MAGIC_WALL_EMPTYING, 2 },
1495 { EL_BD_MAGIC_WALL_FILLING, 2 },
1496 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1497 { EL_DC_MAGIC_WALL_FILLING, 2 },
1498 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1500 { EL_UNDEFINED, 0 },
1508 collect_count_list[] =
1511 { EL_BD_DIAMOND, 1 },
1512 { EL_EMERALD_YELLOW, 1 },
1513 { EL_EMERALD_RED, 1 },
1514 { EL_EMERALD_PURPLE, 1 },
1516 { EL_SP_INFOTRON, 1 },
1520 { EL_UNDEFINED, 0 },
1528 access_direction_list[] =
1530 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1531 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1532 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1533 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1534 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1535 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1536 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1537 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1538 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1539 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1540 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1542 { EL_SP_PORT_LEFT, MV_RIGHT },
1543 { EL_SP_PORT_RIGHT, MV_LEFT },
1544 { EL_SP_PORT_UP, MV_DOWN },
1545 { EL_SP_PORT_DOWN, MV_UP },
1546 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1548 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1550 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1551 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1552 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1553 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1554 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1555 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1556 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1557 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1558 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1559 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1560 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1562 { EL_UNDEFINED, MV_NONE }
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1567 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1570 IS_JUST_CHANGING(x, y))
1572 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1574 /* static variables for playfield scan mode (scanning forward or backward) */
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1580 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1581 (y) >= 0 && (y) <= lev_fieldy - 1; \
1582 (y) += playfield_scan_delta_y) \
1583 for ((x) = playfield_scan_start_x; \
1584 (x) >= 0 && (x) <= lev_fieldx - 1; \
1585 (x) += playfield_scan_delta_x)
1588 void DEBUG_SetMaximumDynamite()
1592 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594 local_player->inventory_element[local_player->inventory_size++] =
1599 static void InitPlayfieldScanModeVars()
1601 if (game.use_reverse_scan_direction)
1603 playfield_scan_start_x = lev_fieldx - 1;
1604 playfield_scan_start_y = lev_fieldy - 1;
1606 playfield_scan_delta_x = -1;
1607 playfield_scan_delta_y = -1;
1611 playfield_scan_start_x = 0;
1612 playfield_scan_start_y = 0;
1614 playfield_scan_delta_x = 1;
1615 playfield_scan_delta_y = 1;
1619 static void InitPlayfieldScanMode(int mode)
1621 game.use_reverse_scan_direction =
1622 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1624 InitPlayfieldScanModeVars();
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1630 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1632 /* make sure that stepsize value is always a power of 2 */
1633 move_stepsize = (1 << log_2(move_stepsize));
1635 return TILEX / move_stepsize;
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1641 int player_nr = player->index_nr;
1642 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1645 /* do no immediately change move delay -- the player might just be moving */
1646 player->move_delay_value_next = move_delay;
1648 /* information if player can move must be set separately */
1649 player->cannot_move = cannot_move;
1653 player->move_delay = game.initial_move_delay[player_nr];
1654 player->move_delay_value = game.initial_move_delay_value[player_nr];
1656 player->move_delay_value_next = -1;
1658 player->move_delay_reset_counter = 0;
1662 void GetPlayerConfig()
1664 GameFrameDelay = setup.game_frame_delay;
1666 if (!audio.sound_available)
1667 setup.sound_simple = FALSE;
1669 if (!audio.loops_available)
1670 setup.sound_loops = FALSE;
1672 if (!audio.music_available)
1673 setup.sound_music = FALSE;
1675 if (!video.fullscreen_available)
1676 setup.fullscreen = FALSE;
1678 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1680 SetAudioMode(setup.sound);
1684 int GetElementFromGroupElement(int element)
1686 if (IS_GROUP_ELEMENT(element))
1688 struct ElementGroupInfo *group = element_info[element].group;
1689 int last_anim_random_frame = gfx.anim_random_frame;
1692 if (group->choice_mode == ANIM_RANDOM)
1693 gfx.anim_random_frame = RND(group->num_elements_resolved);
1695 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1696 group->choice_mode, 0,
1699 if (group->choice_mode == ANIM_RANDOM)
1700 gfx.anim_random_frame = last_anim_random_frame;
1702 group->choice_pos++;
1704 element = group->element_resolved[element_pos];
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1712 if (element == EL_SP_MURPHY)
1716 if (stored_player[0].present)
1718 Feld[x][y] = EL_SP_MURPHY_CLONE;
1724 stored_player[0].initial_element = element;
1725 stored_player[0].use_murphy = TRUE;
1727 if (!level.use_artwork_element[0])
1728 stored_player[0].artwork_element = EL_SP_MURPHY;
1731 Feld[x][y] = EL_PLAYER_1;
1737 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738 int jx = player->jx, jy = player->jy;
1740 player->present = TRUE;
1742 player->block_last_field = (element == EL_SP_MURPHY ?
1743 level.sp_block_last_field :
1744 level.block_last_field);
1746 /* ---------- initialize player's last field block delay --------------- */
1748 /* always start with reliable default value (no adjustment needed) */
1749 player->block_delay_adjustment = 0;
1751 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1752 if (player->block_last_field && element == EL_SP_MURPHY)
1753 player->block_delay_adjustment = 1;
1755 /* special case 2: in game engines before 3.1.1, blocking was different */
1756 if (game.use_block_last_field_bug)
1757 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1759 if (!options.network || player->connected)
1761 player->active = TRUE;
1763 /* remove potentially duplicate players */
1764 if (StorePlayer[jx][jy] == Feld[x][y])
1765 StorePlayer[jx][jy] = 0;
1767 StorePlayer[x][y] = Feld[x][y];
1771 printf("Player %d activated.\n", player->element_nr);
1772 printf("[Local player is %d and currently %s.]\n",
1773 local_player->element_nr,
1774 local_player->active ? "active" : "not active");
1778 Feld[x][y] = EL_EMPTY;
1780 player->jx = player->last_jx = x;
1781 player->jy = player->last_jy = y;
1785 static void InitField(int x, int y, boolean init_game)
1787 int element = Feld[x][y];
1796 InitPlayerField(x, y, element, init_game);
1799 case EL_SOKOBAN_FIELD_PLAYER:
1800 element = Feld[x][y] = EL_PLAYER_1;
1801 InitField(x, y, init_game);
1803 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1804 InitField(x, y, init_game);
1807 case EL_SOKOBAN_FIELD_EMPTY:
1808 local_player->sokobanfields_still_needed++;
1812 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1813 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1814 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1815 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1816 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1817 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1818 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1819 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1820 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1821 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1830 case EL_SPACESHIP_RIGHT:
1831 case EL_SPACESHIP_UP:
1832 case EL_SPACESHIP_LEFT:
1833 case EL_SPACESHIP_DOWN:
1834 case EL_BD_BUTTERFLY:
1835 case EL_BD_BUTTERFLY_RIGHT:
1836 case EL_BD_BUTTERFLY_UP:
1837 case EL_BD_BUTTERFLY_LEFT:
1838 case EL_BD_BUTTERFLY_DOWN:
1840 case EL_BD_FIREFLY_RIGHT:
1841 case EL_BD_FIREFLY_UP:
1842 case EL_BD_FIREFLY_LEFT:
1843 case EL_BD_FIREFLY_DOWN:
1844 case EL_PACMAN_RIGHT:
1846 case EL_PACMAN_LEFT:
1847 case EL_PACMAN_DOWN:
1849 case EL_YAMYAM_LEFT:
1850 case EL_YAMYAM_RIGHT:
1852 case EL_YAMYAM_DOWN:
1853 case EL_DARK_YAMYAM:
1856 case EL_SP_SNIKSNAK:
1857 case EL_SP_ELECTRON:
1866 case EL_AMOEBA_FULL:
1871 case EL_AMOEBA_DROP:
1872 if (y == lev_fieldy - 1)
1874 Feld[x][y] = EL_AMOEBA_GROWING;
1875 Store[x][y] = EL_AMOEBA_WET;
1879 case EL_DYNAMITE_ACTIVE:
1880 case EL_SP_DISK_RED_ACTIVE:
1881 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1882 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1883 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1884 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1885 MovDelay[x][y] = 96;
1888 case EL_EM_DYNAMITE_ACTIVE:
1889 MovDelay[x][y] = 32;
1893 local_player->lights_still_needed++;
1897 local_player->friends_still_needed++;
1902 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1905 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1906 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1907 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1908 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1909 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1910 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1911 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1912 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1913 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1914 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1915 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1916 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1919 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1920 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1921 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1923 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1925 game.belt_dir[belt_nr] = belt_dir;
1926 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1928 else /* more than one switch -- set it like the first switch */
1930 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1935 #if !USE_BOTH_SWITCHGATE_SWITCHES
1936 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1938 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1941 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1943 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1947 case EL_LIGHT_SWITCH_ACTIVE:
1949 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1952 case EL_INVISIBLE_STEELWALL:
1953 case EL_INVISIBLE_WALL:
1954 case EL_INVISIBLE_SAND:
1955 if (game.light_time_left > 0 ||
1956 game.lenses_time_left > 0)
1957 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1960 case EL_EMC_MAGIC_BALL:
1961 if (game.ball_state)
1962 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1965 case EL_EMC_MAGIC_BALL_SWITCH:
1966 if (game.ball_state)
1967 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1970 case EL_TRIGGER_PLAYER:
1971 case EL_TRIGGER_ELEMENT:
1972 case EL_TRIGGER_CE_VALUE:
1973 case EL_TRIGGER_CE_SCORE:
1975 case EL_ANY_ELEMENT:
1976 case EL_CURRENT_CE_VALUE:
1977 case EL_CURRENT_CE_SCORE:
1994 /* reference elements should not be used on the playfield */
1995 Feld[x][y] = EL_EMPTY;
1999 if (IS_CUSTOM_ELEMENT(element))
2001 if (CAN_MOVE(element))
2004 #if USE_NEW_CUSTOM_VALUE
2005 if (!element_info[element].use_last_ce_value || init_game)
2006 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009 else if (IS_GROUP_ELEMENT(element))
2011 Feld[x][y] = GetElementFromGroupElement(element);
2013 InitField(x, y, init_game);
2020 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2023 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2025 InitField(x, y, init_game);
2027 /* not needed to call InitMovDir() -- already done by InitField()! */
2028 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2029 CAN_MOVE(Feld[x][y]))
2033 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2035 int old_element = Feld[x][y];
2037 InitField(x, y, init_game);
2039 /* not needed to call InitMovDir() -- already done by InitField()! */
2040 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2041 CAN_MOVE(old_element) &&
2042 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2045 /* this case is in fact a combination of not less than three bugs:
2046 first, it calls InitMovDir() for elements that can move, although this is
2047 already done by InitField(); then, it checks the element that was at this
2048 field _before_ the call to InitField() (which can change it); lastly, it
2049 was not called for "mole with direction" elements, which were treated as
2050 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2056 static int get_key_element_from_nr(int key_nr)
2058 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2059 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2060 EL_EM_KEY_1 : EL_KEY_1);
2062 return key_base_element + key_nr;
2065 static int get_next_dropped_element(struct PlayerInfo *player)
2067 return (player->inventory_size > 0 ?
2068 player->inventory_element[player->inventory_size - 1] :
2069 player->inventory_infinite_element != EL_UNDEFINED ?
2070 player->inventory_infinite_element :
2071 player->dynabombs_left > 0 ?
2072 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2076 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 /* pos >= 0: get element from bottom of the stack;
2079 pos < 0: get element from top of the stack */
2083 int min_inventory_size = -pos;
2084 int inventory_pos = player->inventory_size - min_inventory_size;
2085 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087 return (player->inventory_size >= min_inventory_size ?
2088 player->inventory_element[inventory_pos] :
2089 player->inventory_infinite_element != EL_UNDEFINED ?
2090 player->inventory_infinite_element :
2091 player->dynabombs_left >= min_dynabombs_left ?
2092 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2097 int min_dynabombs_left = pos + 1;
2098 int min_inventory_size = pos + 1 - player->dynabombs_left;
2099 int inventory_pos = pos - player->dynabombs_left;
2101 return (player->inventory_infinite_element != EL_UNDEFINED ?
2102 player->inventory_infinite_element :
2103 player->dynabombs_left >= min_dynabombs_left ?
2104 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2105 player->inventory_size >= min_inventory_size ?
2106 player->inventory_element[inventory_pos] :
2111 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2114 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2117 if (gpo1->sort_priority != gpo2->sort_priority)
2118 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120 compare_result = gpo1->nr - gpo2->nr;
2122 return compare_result;
2125 void InitGameControlValues()
2129 for (i = 0; game_panel_controls[i].nr != -1; i++)
2131 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2132 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2133 struct TextPosInfo *pos = gpc->pos;
2135 int type = gpc->type;
2139 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2140 Error(ERR_EXIT, "this should not happen -- please debug");
2143 /* force update of game controls after initialization */
2144 gpc->value = gpc->last_value = -1;
2145 gpc->frame = gpc->last_frame = -1;
2146 gpc->gfx_frame = -1;
2148 /* determine panel value width for later calculation of alignment */
2149 if (type == TYPE_INTEGER || type == TYPE_STRING)
2151 pos->width = pos->size * getFontWidth(pos->font);
2152 pos->height = getFontHeight(pos->font);
2154 else if (type == TYPE_ELEMENT)
2156 pos->width = pos->size;
2157 pos->height = pos->size;
2160 /* fill structure for game panel draw order */
2162 gpo->sort_priority = pos->sort_priority;
2165 /* sort game panel controls according to sort_priority and control number */
2166 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2167 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2170 void UpdatePlayfieldElementCount()
2172 boolean use_element_count = FALSE;
2175 /* first check if it is needed at all to calculate playfield element count */
2176 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2177 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2178 use_element_count = TRUE;
2180 if (!use_element_count)
2183 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2184 element_info[i].element_count = 0;
2186 SCAN_PLAYFIELD(x, y)
2188 element_info[Feld[x][y]].element_count++;
2191 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2192 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2193 if (IS_IN_GROUP(j, i))
2194 element_info[EL_GROUP_START + i].element_count +=
2195 element_info[j].element_count;
2198 void UpdateGameControlValues()
2201 int time = (local_player->LevelSolved ?
2202 local_player->LevelSolved_CountingTime :
2203 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204 level.native_em_level->lev->time :
2205 level.time == 0 ? TimePlayed : TimeLeft);
2206 int score = (local_player->LevelSolved ?
2207 local_player->LevelSolved_CountingScore :
2208 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2209 level.native_em_level->lev->score :
2210 local_player->score);
2211 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212 level.native_em_level->lev->required :
2213 local_player->gems_still_needed);
2214 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215 level.native_em_level->lev->required > 0 :
2216 local_player->gems_still_needed > 0 ||
2217 local_player->sokobanfields_still_needed > 0 ||
2218 local_player->lights_still_needed > 0);
2220 UpdatePlayfieldElementCount();
2222 /* update game panel control values */
2224 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2225 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2227 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2228 for (i = 0; i < MAX_NUM_KEYS; i++)
2229 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2230 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2231 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2233 if (game.centered_player_nr == -1)
2235 for (i = 0; i < MAX_PLAYERS; i++)
2237 for (k = 0; k < MAX_NUM_KEYS; k++)
2239 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2241 if (level.native_em_level->ply[i]->keys & (1 << k))
2242 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2243 get_key_element_from_nr(k);
2245 else if (stored_player[i].key[k])
2246 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247 get_key_element_from_nr(k);
2250 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2251 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2252 level.native_em_level->ply[i]->dynamite;
2254 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255 stored_player[i].inventory_size;
2257 if (stored_player[i].num_white_keys > 0)
2258 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2262 stored_player[i].num_white_keys;
2267 int player_nr = game.centered_player_nr;
2269 for (k = 0; k < MAX_NUM_KEYS; k++)
2271 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2273 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2274 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2275 get_key_element_from_nr(k);
2277 else if (stored_player[player_nr].key[k])
2278 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279 get_key_element_from_nr(k);
2282 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2283 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2284 level.native_em_level->ply[player_nr]->dynamite;
2286 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287 stored_player[player_nr].inventory_size;
2289 if (stored_player[player_nr].num_white_keys > 0)
2290 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2292 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293 stored_player[player_nr].num_white_keys;
2296 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2298 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, i);
2300 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301 get_inventory_element_from_pos(local_player, -i - 1);
2304 game_panel_controls[GAME_PANEL_SCORE].value = score;
2305 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2307 game_panel_controls[GAME_PANEL_TIME].value = time;
2309 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2313 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2314 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2316 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2317 local_player->shield_normal_time_left;
2318 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2319 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2321 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2322 local_player->shield_deadly_time_left;
2324 game_panel_controls[GAME_PANEL_EXIT].value =
2325 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2327 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2328 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2329 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2330 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2331 EL_EMC_MAGIC_BALL_SWITCH);
2333 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2334 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2335 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2336 game.light_time_left;
2338 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2339 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2340 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2341 game.timegate_time_left;
2343 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2344 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2346 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2347 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2348 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2349 game.lenses_time_left;
2351 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2352 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2353 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2354 game.magnify_time_left;
2356 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2357 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2358 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2359 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2360 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2361 EL_BALLOON_SWITCH_NONE);
2363 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2364 local_player->dynabomb_count;
2365 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2366 local_player->dynabomb_size;
2367 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2368 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2370 game_panel_controls[GAME_PANEL_PENGUINS].value =
2371 local_player->friends_still_needed;
2373 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2374 local_player->sokobanfields_still_needed;
2375 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2376 local_player->sokobanfields_still_needed;
2378 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2379 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2381 for (i = 0; i < NUM_BELTS; i++)
2383 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2384 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2385 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2386 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2387 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2390 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2391 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2392 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2393 game.magic_wall_time_left;
2395 #if USE_PLAYER_GRAVITY
2396 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2397 local_player->gravity;
2399 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2402 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2403 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2405 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2406 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2407 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2408 game.panel.element[i].id : EL_UNDEFINED);
2410 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2411 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2412 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2413 element_info[game.panel.element_count[i].id].element_count : 0);
2415 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2416 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2417 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2418 element_info[game.panel.ce_score[i].id].collect_score : 0);
2420 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2421 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2422 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2423 element_info[game.panel.ce_score_element[i].id].collect_score :
2426 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2427 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2428 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2430 /* update game panel control frames */
2432 for (i = 0; game_panel_controls[i].nr != -1; i++)
2434 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2436 if (gpc->type == TYPE_ELEMENT)
2438 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2440 int last_anim_random_frame = gfx.anim_random_frame;
2441 int element = gpc->value;
2442 int graphic = el2panelimg(element);
2444 if (gpc->value != gpc->last_value)
2447 gpc->gfx_random = INIT_GFX_RANDOM();
2453 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2454 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2455 gpc->gfx_random = INIT_GFX_RANDOM();
2458 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2459 gfx.anim_random_frame = gpc->gfx_random;
2461 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2462 gpc->gfx_frame = element_info[element].collect_score;
2464 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2467 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2468 gfx.anim_random_frame = last_anim_random_frame;
2474 void DisplayGameControlValues()
2476 boolean redraw_panel = FALSE;
2479 for (i = 0; game_panel_controls[i].nr != -1; i++)
2481 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2483 if (PANEL_DEACTIVATED(gpc->pos))
2486 if (gpc->value == gpc->last_value &&
2487 gpc->frame == gpc->last_frame)
2490 redraw_panel = TRUE;
2496 /* copy default game door content to main double buffer */
2497 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2498 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2500 /* redraw game control buttons */
2502 RedrawGameButtons();
2508 game_status = GAME_MODE_PSEUDO_PANEL;
2511 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2517 int nr = game_panel_order[i].nr;
2518 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2520 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2523 struct TextPosInfo *pos = gpc->pos;
2524 int type = gpc->type;
2525 int value = gpc->value;
2526 int frame = gpc->frame;
2528 int last_value = gpc->last_value;
2529 int last_frame = gpc->last_frame;
2531 int size = pos->size;
2532 int font = pos->font;
2533 boolean draw_masked = pos->draw_masked;
2534 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2536 if (PANEL_DEACTIVATED(pos))
2540 if (value == last_value && frame == last_frame)
2544 gpc->last_value = value;
2545 gpc->last_frame = frame;
2548 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2551 if (type == TYPE_INTEGER)
2553 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2554 nr == GAME_PANEL_TIME)
2556 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2558 if (use_dynamic_size) /* use dynamic number of digits */
2560 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2561 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2562 int size2 = size1 + 1;
2563 int font1 = pos->font;
2564 int font2 = pos->font_alt;
2566 size = (value < value_change ? size1 : size2);
2567 font = (value < value_change ? font1 : font2);
2570 /* clear background if value just changed its size (dynamic digits) */
2571 if ((last_value < value_change) != (value < value_change))
2573 int width1 = size1 * getFontWidth(font1);
2574 int width2 = size2 * getFontWidth(font2);
2575 int max_width = MAX(width1, width2);
2576 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2578 pos->width = max_width;
2580 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2581 max_width, max_height);
2588 /* correct text size if "digits" is zero or less */
2590 size = strlen(int2str(value, size));
2592 /* dynamically correct text alignment */
2593 pos->width = size * getFontWidth(font);
2596 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2597 int2str(value, size), font, mask_mode);
2599 else if (type == TYPE_ELEMENT)
2601 int element, graphic;
2605 int dst_x = PANEL_XPOS(pos);
2606 int dst_y = PANEL_YPOS(pos);
2609 if (value != EL_UNDEFINED && value != EL_EMPTY)
2612 graphic = el2panelimg(value);
2614 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2617 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2621 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2624 width = graphic_info[graphic].width * size / TILESIZE;
2625 height = graphic_info[graphic].height * size / TILESIZE;
2629 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2630 dst_x - src_x, dst_y - src_y);
2631 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2636 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2641 if (value == EL_UNDEFINED || value == EL_EMPTY)
2643 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2644 graphic = el2panelimg(element);
2646 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2647 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2648 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2653 graphic = el2panelimg(value);
2655 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2658 width = graphic_info[graphic].width * size / TILESIZE;
2659 height = graphic_info[graphic].height * size / TILESIZE;
2661 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2664 else if (type == TYPE_STRING)
2666 boolean active = (value != 0);
2667 char *state_normal = "off";
2668 char *state_active = "on";
2669 char *state = (active ? state_active : state_normal);
2670 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2671 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2672 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2673 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2675 if (nr == GAME_PANEL_GRAVITY_STATE)
2677 int font1 = pos->font; /* (used for normal state) */
2678 int font2 = pos->font_alt; /* (used for active state) */
2680 int size1 = strlen(state_normal);
2681 int size2 = strlen(state_active);
2682 int width1 = size1 * getFontWidth(font1);
2683 int width2 = size2 * getFontWidth(font2);
2684 int max_width = MAX(width1, width2);
2685 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2687 pos->width = max_width;
2689 /* clear background for values that may have changed its size */
2690 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2691 max_width, max_height);
2694 font = (active ? font2 : font1);
2704 /* don't truncate output if "chars" is zero or less */
2707 /* dynamically correct text alignment */
2708 pos->width = size * getFontWidth(font);
2712 s_cut = getStringCopyN(s, size);
2714 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2715 s_cut, font, mask_mode);
2721 redraw_mask |= REDRAW_DOOR_1;
2724 game_status = GAME_MODE_PLAYING;
2727 void UpdateAndDisplayGameControlValues()
2729 if (tape.warp_forward)
2732 UpdateGameControlValues();
2733 DisplayGameControlValues();
2736 void DrawGameValue_Emeralds(int value)
2738 struct TextPosInfo *pos = &game.panel.gems;
2740 int font_nr = pos->font;
2742 int font_nr = FONT_TEXT_2;
2744 int font_width = getFontWidth(font_nr);
2745 int chars = pos->size;
2748 return; /* !!! USE NEW STUFF !!! */
2751 if (PANEL_DEACTIVATED(pos))
2754 pos->width = chars * font_width;
2756 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2759 void DrawGameValue_Dynamite(int value)
2761 struct TextPosInfo *pos = &game.panel.inventory_count;
2763 int font_nr = pos->font;
2765 int font_nr = FONT_TEXT_2;
2767 int font_width = getFontWidth(font_nr);
2768 int chars = pos->size;
2771 return; /* !!! USE NEW STUFF !!! */
2774 if (PANEL_DEACTIVATED(pos))
2777 pos->width = chars * font_width;
2779 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2782 void DrawGameValue_Score(int value)
2784 struct TextPosInfo *pos = &game.panel.score;
2786 int font_nr = pos->font;
2788 int font_nr = FONT_TEXT_2;
2790 int font_width = getFontWidth(font_nr);
2791 int chars = pos->size;
2794 return; /* !!! USE NEW STUFF !!! */
2797 if (PANEL_DEACTIVATED(pos))
2800 pos->width = chars * font_width;
2802 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2805 void DrawGameValue_Time(int value)
2807 struct TextPosInfo *pos = &game.panel.time;
2808 static int last_value = -1;
2811 int chars = pos->size;
2813 int font1_nr = pos->font;
2814 int font2_nr = pos->font_alt;
2816 int font1_nr = FONT_TEXT_2;
2817 int font2_nr = FONT_TEXT_1;
2819 int font_nr = font1_nr;
2820 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2823 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2829 if (use_dynamic_chars) /* use dynamic number of chars */
2831 chars = (value < 1000 ? chars1 : chars2);
2832 font_nr = (value < 1000 ? font1_nr : font2_nr);
2835 /* clear background if value just changed its size (dynamic chars only) */
2836 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838 int width1 = chars1 * getFontWidth(font1_nr);
2839 int width2 = chars2 * getFontWidth(font2_nr);
2840 int max_width = MAX(width1, width2);
2841 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843 pos->width = max_width;
2845 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2846 max_width, max_height);
2849 pos->width = chars * getFontWidth(font_nr);
2851 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2856 void DrawGameValue_Level(int value)
2858 struct TextPosInfo *pos = &game.panel.level_number;
2861 int chars = pos->size;
2863 int font1_nr = pos->font;
2864 int font2_nr = pos->font_alt;
2866 int font1_nr = FONT_TEXT_2;
2867 int font2_nr = FONT_TEXT_1;
2869 int font_nr = font1_nr;
2870 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2873 return; /* !!! USE NEW STUFF !!! */
2876 if (PANEL_DEACTIVATED(pos))
2879 if (use_dynamic_chars) /* use dynamic number of chars */
2881 chars = (level_nr < 100 ? chars1 : chars2);
2882 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2885 pos->width = chars * getFontWidth(font_nr);
2887 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2890 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2893 struct TextPosInfo *pos = &game.panel.keys;
2896 int base_key_graphic = EL_KEY_1;
2901 return; /* !!! USE NEW STUFF !!! */
2905 if (PANEL_DEACTIVATED(pos))
2910 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2911 base_key_graphic = EL_EM_KEY_1;
2915 pos->width = 4 * MINI_TILEX;
2919 for (i = 0; i < MAX_NUM_KEYS; i++)
2921 /* currently only 4 of 8 possible keys are displayed */
2922 for (i = 0; i < STD_NUM_KEYS; i++)
2926 struct TextPosInfo *pos = &game.panel.key[i];
2928 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2929 int src_y = DOOR_GFX_PAGEY1 + 123;
2931 int dst_x = PANEL_XPOS(pos);
2932 int dst_y = PANEL_YPOS(pos);
2934 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2935 int dst_y = PANEL_YPOS(pos);
2939 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2940 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2942 int graphic = el2edimg(element);
2946 if (PANEL_DEACTIVATED(pos))
2951 /* masked blit with tiles from half-size scaled bitmap does not work yet
2952 (no mask bitmap created for these sizes after loading and scaling) --
2953 solution: load without creating mask, scale, then create final mask */
2955 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2956 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2961 int graphic = el2edimg(base_key_graphic + i);
2966 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2968 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2969 dst_x - src_x, dst_y - src_y);
2970 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2976 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2978 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2979 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2982 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2984 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2985 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2993 void DrawGameValue_Emeralds(int value)
2995 int font_nr = FONT_TEXT_2;
2996 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2998 if (PANEL_DEACTIVATED(game.panel.gems))
3001 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3004 void DrawGameValue_Dynamite(int value)
3006 int font_nr = FONT_TEXT_2;
3007 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3009 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3012 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3015 void DrawGameValue_Score(int value)
3017 int font_nr = FONT_TEXT_2;
3018 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3020 if (PANEL_DEACTIVATED(game.panel.score))
3023 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3026 void DrawGameValue_Time(int value)
3028 int font1_nr = FONT_TEXT_2;
3030 int font2_nr = FONT_TEXT_1;
3032 int font2_nr = FONT_LEVEL_NUMBER;
3034 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3035 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3037 if (PANEL_DEACTIVATED(game.panel.time))
3040 /* clear background if value just changed its size */
3041 if (value == 999 || value == 1000)
3042 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3045 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3047 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3050 void DrawGameValue_Level(int value)
3052 int font1_nr = FONT_TEXT_2;
3054 int font2_nr = FONT_TEXT_1;
3056 int font2_nr = FONT_LEVEL_NUMBER;
3059 if (PANEL_DEACTIVATED(game.panel.level))
3063 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3065 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3068 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3070 int base_key_graphic = EL_KEY_1;
3073 if (PANEL_DEACTIVATED(game.panel.keys))
3076 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3077 base_key_graphic = EL_EM_KEY_1;
3079 /* currently only 4 of 8 possible keys are displayed */
3080 for (i = 0; i < STD_NUM_KEYS; i++)
3082 int x = XX_KEYS + i * MINI_TILEX;
3086 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3088 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3089 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3095 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3098 int key[MAX_NUM_KEYS];
3101 /* prevent EM engine from updating time/score values parallel to GameWon() */
3102 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3103 local_player->LevelSolved)
3106 for (i = 0; i < MAX_NUM_KEYS; i++)
3107 key[i] = key_bits & (1 << i);
3109 DrawGameValue_Level(level_nr);
3111 DrawGameValue_Emeralds(emeralds);
3112 DrawGameValue_Dynamite(dynamite);
3113 DrawGameValue_Score(score);
3114 DrawGameValue_Time(time);
3116 DrawGameValue_Keys(key);
3119 void UpdateGameDoorValues()
3121 UpdateGameControlValues();
3124 void DrawGameDoorValues()
3126 DisplayGameControlValues();
3129 void DrawGameDoorValues_OLD()
3131 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3132 int dynamite_value = 0;
3133 int score_value = (local_player->LevelSolved ? local_player->score_final :
3134 local_player->score);
3135 int gems_value = local_player->gems_still_needed;
3139 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3141 DrawGameDoorValues_EM();
3146 if (game.centered_player_nr == -1)
3148 for (i = 0; i < MAX_PLAYERS; i++)
3150 for (j = 0; j < MAX_NUM_KEYS; j++)
3151 if (stored_player[i].key[j])
3152 key_bits |= (1 << j);
3154 dynamite_value += stored_player[i].inventory_size;
3159 int player_nr = game.centered_player_nr;
3161 for (i = 0; i < MAX_NUM_KEYS; i++)
3162 if (stored_player[player_nr].key[i])
3163 key_bits |= (1 << i);
3165 dynamite_value = stored_player[player_nr].inventory_size;
3168 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3174 =============================================================================
3176 -----------------------------------------------------------------------------
3177 initialize game engine due to level / tape version number
3178 =============================================================================
3181 static void InitGameEngine()
3183 int i, j, k, l, x, y;
3185 /* set game engine from tape file when re-playing, else from level file */
3186 game.engine_version = (tape.playing ? tape.engine_version :
3187 level.game_version);
3189 /* ---------------------------------------------------------------------- */
3190 /* set flags for bugs and changes according to active game engine version */
3191 /* ---------------------------------------------------------------------- */
3194 Summary of bugfix/change:
3195 Fixed handling for custom elements that change when pushed by the player.
3197 Fixed/changed in version:
3201 Before 3.1.0, custom elements that "change when pushing" changed directly
3202 after the player started pushing them (until then handled in "DigField()").
3203 Since 3.1.0, these custom elements are not changed until the "pushing"
3204 move of the element is finished (now handled in "ContinueMoving()").
3206 Affected levels/tapes:
3207 The first condition is generally needed for all levels/tapes before version
3208 3.1.0, which might use the old behaviour before it was changed; known tapes
3209 that are affected are some tapes from the level set "Walpurgis Gardens" by
3211 The second condition is an exception from the above case and is needed for
3212 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3213 above (including some development versions of 3.1.0), but before it was
3214 known that this change would break tapes like the above and was fixed in
3215 3.1.1, so that the changed behaviour was active although the engine version
3216 while recording maybe was before 3.1.0. There is at least one tape that is
3217 affected by this exception, which is the tape for the one-level set "Bug
3218 Machine" by Juergen Bonhagen.
3221 game.use_change_when_pushing_bug =
3222 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3224 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3225 tape.game_version < VERSION_IDENT(3,1,1,0)));
3228 Summary of bugfix/change:
3229 Fixed handling for blocking the field the player leaves when moving.
3231 Fixed/changed in version:
3235 Before 3.1.1, when "block last field when moving" was enabled, the field
3236 the player is leaving when moving was blocked for the time of the move,
3237 and was directly unblocked afterwards. This resulted in the last field
3238 being blocked for exactly one less than the number of frames of one player
3239 move. Additionally, even when blocking was disabled, the last field was
3240 blocked for exactly one frame.
3241 Since 3.1.1, due to changes in player movement handling, the last field
3242 is not blocked at all when blocking is disabled. When blocking is enabled,
3243 the last field is blocked for exactly the number of frames of one player
3244 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3245 last field is blocked for exactly one more than the number of frames of
3248 Affected levels/tapes:
3249 (!!! yet to be determined -- probably many !!!)
3252 game.use_block_last_field_bug =
3253 (game.engine_version < VERSION_IDENT(3,1,1,0));
3256 Summary of bugfix/change:
3257 Changed behaviour of CE changes with multiple changes per single frame.
3259 Fixed/changed in version:
3263 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3264 This resulted in race conditions where CEs seem to behave strange in some
3265 situations (where triggered CE changes were just skipped because there was
3266 already a CE change on that tile in the playfield in that engine frame).
3267 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3268 (The number of changes per frame must be limited in any case, because else
3269 it is easily possible to define CE changes that would result in an infinite
3270 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3271 should be set large enough so that it would only be reached in cases where
3272 the corresponding CE change conditions run into a loop. Therefore, it seems
3273 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3274 maximal number of change pages for custom elements.)
3276 Affected levels/tapes:
3280 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3281 game.max_num_changes_per_frame = 1;
3283 game.max_num_changes_per_frame =
3284 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3287 /* ---------------------------------------------------------------------- */
3289 /* default scan direction: scan playfield from top/left to bottom/right */
3290 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3292 /* dynamically adjust element properties according to game engine version */
3293 InitElementPropertiesEngine(game.engine_version);
3296 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3297 printf(" tape version == %06d [%s] [file: %06d]\n",
3298 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3300 printf(" => game.engine_version == %06d\n", game.engine_version);
3303 /* ---------- initialize player's initial move delay --------------------- */
3305 /* dynamically adjust player properties according to level information */
3306 for (i = 0; i < MAX_PLAYERS; i++)
3307 game.initial_move_delay_value[i] =
3308 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3310 /* dynamically adjust player properties according to game engine version */
3311 for (i = 0; i < MAX_PLAYERS; i++)
3312 game.initial_move_delay[i] =
3313 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3314 game.initial_move_delay_value[i] : 0);
3316 /* ---------- initialize player's initial push delay --------------------- */
3318 /* dynamically adjust player properties according to game engine version */
3319 game.initial_push_delay_value =
3320 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3322 /* ---------- initialize changing elements ------------------------------- */
3324 /* initialize changing elements information */
3325 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327 struct ElementInfo *ei = &element_info[i];
3329 /* this pointer might have been changed in the level editor */
3330 ei->change = &ei->change_page[0];
3332 if (!IS_CUSTOM_ELEMENT(i))
3334 ei->change->target_element = EL_EMPTY_SPACE;
3335 ei->change->delay_fixed = 0;
3336 ei->change->delay_random = 0;
3337 ei->change->delay_frames = 1;
3340 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3342 ei->has_change_event[j] = FALSE;
3344 ei->event_page_nr[j] = 0;
3345 ei->event_page[j] = &ei->change_page[0];
3349 /* add changing elements from pre-defined list */
3350 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3352 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3353 struct ElementInfo *ei = &element_info[ch_delay->element];
3355 ei->change->target_element = ch_delay->target_element;
3356 ei->change->delay_fixed = ch_delay->change_delay;
3358 ei->change->pre_change_function = ch_delay->pre_change_function;
3359 ei->change->change_function = ch_delay->change_function;
3360 ei->change->post_change_function = ch_delay->post_change_function;
3362 ei->change->can_change = TRUE;
3363 ei->change->can_change_or_has_action = TRUE;
3365 ei->has_change_event[CE_DELAY] = TRUE;
3367 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3368 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3371 /* ---------- initialize internal run-time variables --------------------- */
3373 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3375 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3377 for (j = 0; j < ei->num_change_pages; j++)
3379 ei->change_page[j].can_change_or_has_action =
3380 (ei->change_page[j].can_change |
3381 ei->change_page[j].has_action);
3385 /* add change events from custom element configuration */
3386 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3388 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3390 for (j = 0; j < ei->num_change_pages; j++)
3392 if (!ei->change_page[j].can_change_or_has_action)
3395 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3397 /* only add event page for the first page found with this event */
3398 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3400 ei->has_change_event[k] = TRUE;
3402 ei->event_page_nr[k] = j;
3403 ei->event_page[k] = &ei->change_page[j];
3410 /* ---------- initialize reference elements in change conditions --------- */
3412 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3414 int element = EL_CUSTOM_START + i;
3415 struct ElementInfo *ei = &element_info[element];
3417 for (j = 0; j < ei->num_change_pages; j++)
3419 int trigger_element = ei->change_page[j].initial_trigger_element;
3421 if (trigger_element >= EL_PREV_CE_8 &&
3422 trigger_element <= EL_NEXT_CE_8)
3423 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3425 ei->change_page[j].trigger_element = trigger_element;
3430 /* ---------- initialize run-time trigger player and element ------------- */
3432 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3434 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3436 for (j = 0; j < ei->num_change_pages; j++)
3438 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3439 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3440 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3441 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3442 ei->change_page[j].actual_trigger_ce_value = 0;
3443 ei->change_page[j].actual_trigger_ce_score = 0;
3447 /* ---------- initialize trigger events ---------------------------------- */
3449 /* initialize trigger events information */
3450 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3451 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3452 trigger_events[i][j] = FALSE;
3454 /* add trigger events from element change event properties */
3455 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457 struct ElementInfo *ei = &element_info[i];
3459 for (j = 0; j < ei->num_change_pages; j++)
3461 if (!ei->change_page[j].can_change_or_has_action)
3464 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3466 int trigger_element = ei->change_page[j].trigger_element;
3468 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3470 if (ei->change_page[j].has_event[k])
3472 if (IS_GROUP_ELEMENT(trigger_element))
3474 struct ElementGroupInfo *group =
3475 element_info[trigger_element].group;
3477 for (l = 0; l < group->num_elements_resolved; l++)
3478 trigger_events[group->element_resolved[l]][k] = TRUE;
3480 else if (trigger_element == EL_ANY_ELEMENT)
3481 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3482 trigger_events[l][k] = TRUE;
3484 trigger_events[trigger_element][k] = TRUE;
3491 /* ---------- initialize push delay -------------------------------------- */
3493 /* initialize push delay values to default */
3494 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496 if (!IS_CUSTOM_ELEMENT(i))
3498 /* set default push delay values (corrected since version 3.0.7-1) */
3499 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3501 element_info[i].push_delay_fixed = 2;
3502 element_info[i].push_delay_random = 8;
3506 element_info[i].push_delay_fixed = 8;
3507 element_info[i].push_delay_random = 8;
3512 /* set push delay value for certain elements from pre-defined list */
3513 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3515 int e = push_delay_list[i].element;
3517 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3518 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3521 /* set push delay value for Supaplex elements for newer engine versions */
3522 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3524 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3526 if (IS_SP_ELEMENT(i))
3528 /* set SP push delay to just enough to push under a falling zonk */
3529 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3531 element_info[i].push_delay_fixed = delay;
3532 element_info[i].push_delay_random = 0;
3537 /* ---------- initialize move stepsize ----------------------------------- */
3539 /* initialize move stepsize values to default */
3540 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3541 if (!IS_CUSTOM_ELEMENT(i))
3542 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3544 /* set move stepsize value for certain elements from pre-defined list */
3545 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3547 int e = move_stepsize_list[i].element;
3549 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3552 /* ---------- initialize collect score ----------------------------------- */
3554 /* initialize collect score values for custom elements from initial value */
3555 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3556 if (IS_CUSTOM_ELEMENT(i))
3557 element_info[i].collect_score = element_info[i].collect_score_initial;
3559 /* ---------- initialize collect count ----------------------------------- */
3561 /* initialize collect count values for non-custom elements */
3562 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3563 if (!IS_CUSTOM_ELEMENT(i))
3564 element_info[i].collect_count_initial = 0;
3566 /* add collect count values for all elements from pre-defined list */
3567 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3568 element_info[collect_count_list[i].element].collect_count_initial =
3569 collect_count_list[i].count;
3571 /* ---------- initialize access direction -------------------------------- */
3573 /* initialize access direction values to default (access from every side) */
3574 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575 if (!IS_CUSTOM_ELEMENT(i))
3576 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3578 /* set access direction value for certain elements from pre-defined list */
3579 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3580 element_info[access_direction_list[i].element].access_direction =
3581 access_direction_list[i].direction;
3583 /* ---------- initialize explosion content ------------------------------- */
3584 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586 if (IS_CUSTOM_ELEMENT(i))
3589 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3591 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3593 element_info[i].content.e[x][y] =
3594 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3595 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3596 i == EL_PLAYER_3 ? EL_EMERALD :
3597 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3598 i == EL_MOLE ? EL_EMERALD_RED :
3599 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3600 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3601 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3602 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3603 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3604 i == EL_WALL_EMERALD ? EL_EMERALD :
3605 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3606 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3607 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3608 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3609 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3610 i == EL_WALL_PEARL ? EL_PEARL :
3611 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3616 /* ---------- initialize recursion detection ------------------------------ */
3617 recursion_loop_depth = 0;
3618 recursion_loop_detected = FALSE;
3619 recursion_loop_element = EL_UNDEFINED;
3621 /* ---------- initialize graphics engine ---------------------------------- */
3622 game.scroll_delay_value =
3623 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3624 setup.scroll_delay ? setup.scroll_delay_value : 0);
3625 game.scroll_delay_value =
3626 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3629 int get_num_special_action(int element, int action_first, int action_last)
3631 int num_special_action = 0;
3634 for (i = action_first; i <= action_last; i++)
3636 boolean found = FALSE;
3638 for (j = 0; j < NUM_DIRECTIONS; j++)
3639 if (el_act_dir2img(element, i, j) !=
3640 el_act_dir2img(element, ACTION_DEFAULT, j))
3644 num_special_action++;
3649 return num_special_action;
3654 =============================================================================
3656 -----------------------------------------------------------------------------
3657 initialize and start new game
3658 =============================================================================
3663 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3664 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3665 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3667 boolean do_fading = (game_status == GAME_MODE_MAIN);
3671 game_status = GAME_MODE_PLAYING;
3674 InitGameControlValues();
3676 /* don't play tapes over network */
3677 network_playing = (options.network && !tape.playing);
3679 for (i = 0; i < MAX_PLAYERS; i++)
3681 struct PlayerInfo *player = &stored_player[i];
3683 player->index_nr = i;
3684 player->index_bit = (1 << i);
3685 player->element_nr = EL_PLAYER_1 + i;
3687 player->present = FALSE;
3688 player->active = FALSE;
3689 player->killed = FALSE;
3692 player->effective_action = 0;
3693 player->programmed_action = 0;
3696 player->score_final = 0;
3698 player->gems_still_needed = level.gems_needed;
3699 player->sokobanfields_still_needed = 0;
3700 player->lights_still_needed = 0;
3701 player->friends_still_needed = 0;
3703 for (j = 0; j < MAX_NUM_KEYS; j++)
3704 player->key[j] = FALSE;
3706 player->num_white_keys = 0;
3708 player->dynabomb_count = 0;
3709 player->dynabomb_size = 1;
3710 player->dynabombs_left = 0;
3711 player->dynabomb_xl = FALSE;
3713 player->MovDir = MV_NONE;
3716 player->GfxDir = MV_NONE;
3717 player->GfxAction = ACTION_DEFAULT;
3719 player->StepFrame = 0;
3721 player->initial_element = player->element_nr;
3722 player->artwork_element =
3723 (level.use_artwork_element[i] ? level.artwork_element[i] :
3724 player->element_nr);
3725 player->use_murphy = FALSE;
3727 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3728 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3730 player->gravity = level.initial_player_gravity[i];
3732 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3734 player->actual_frame_counter = 0;
3736 player->step_counter = 0;
3738 player->last_move_dir = MV_NONE;
3740 player->is_active = FALSE;
3742 player->is_waiting = FALSE;
3743 player->is_moving = FALSE;
3744 player->is_auto_moving = FALSE;
3745 player->is_digging = FALSE;
3746 player->is_snapping = FALSE;
3747 player->is_collecting = FALSE;
3748 player->is_pushing = FALSE;
3749 player->is_switching = FALSE;
3750 player->is_dropping = FALSE;
3751 player->is_dropping_pressed = FALSE;
3753 player->is_bored = FALSE;
3754 player->is_sleeping = FALSE;
3756 player->frame_counter_bored = -1;
3757 player->frame_counter_sleeping = -1;
3759 player->anim_delay_counter = 0;
3760 player->post_delay_counter = 0;
3762 player->dir_waiting = MV_NONE;
3763 player->action_waiting = ACTION_DEFAULT;
3764 player->last_action_waiting = ACTION_DEFAULT;
3765 player->special_action_bored = ACTION_DEFAULT;
3766 player->special_action_sleeping = ACTION_DEFAULT;
3768 player->switch_x = -1;
3769 player->switch_y = -1;
3771 player->drop_x = -1;
3772 player->drop_y = -1;
3774 player->show_envelope = 0;
3776 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3778 player->push_delay = -1; /* initialized when pushing starts */
3779 player->push_delay_value = game.initial_push_delay_value;
3781 player->drop_delay = 0;
3782 player->drop_pressed_delay = 0;
3784 player->last_jx = -1;
3785 player->last_jy = -1;
3789 player->shield_normal_time_left = 0;
3790 player->shield_deadly_time_left = 0;
3792 player->inventory_infinite_element = EL_UNDEFINED;
3793 player->inventory_size = 0;
3795 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3796 SnapField(player, 0, 0);
3798 player->LevelSolved = FALSE;
3799 player->GameOver = FALSE;
3801 player->LevelSolved_GameWon = FALSE;
3802 player->LevelSolved_GameEnd = FALSE;
3803 player->LevelSolved_PanelOff = FALSE;
3804 player->LevelSolved_SaveTape = FALSE;
3805 player->LevelSolved_SaveScore = FALSE;
3806 player->LevelSolved_CountingTime = 0;
3807 player->LevelSolved_CountingScore = 0;
3810 network_player_action_received = FALSE;
3812 #if defined(NETWORK_AVALIABLE)
3813 /* initial null action */
3814 if (network_playing)
3815 SendToServer_MovePlayer(MV_NONE);
3824 TimeLeft = level.time;
3827 ScreenMovDir = MV_NONE;
3831 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3833 AllPlayersGone = FALSE;
3835 game.yamyam_content_nr = 0;
3836 game.robot_wheel_active = FALSE;
3837 game.magic_wall_active = FALSE;
3838 game.magic_wall_time_left = 0;
3839 game.light_time_left = 0;
3840 game.timegate_time_left = 0;
3841 game.switchgate_pos = 0;
3842 game.wind_direction = level.wind_direction_initial;
3844 #if !USE_PLAYER_GRAVITY
3845 game.gravity = FALSE;
3846 game.explosions_delayed = TRUE;
3849 game.lenses_time_left = 0;
3850 game.magnify_time_left = 0;
3852 game.ball_state = level.ball_state_initial;
3853 game.ball_content_nr = 0;
3855 game.envelope_active = FALSE;
3857 /* set focus to local player for network games, else to all players */
3858 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3859 game.centered_player_nr_next = game.centered_player_nr;
3860 game.set_centered_player = FALSE;
3862 if (network_playing && tape.recording)
3864 /* store client dependent player focus when recording network games */
3865 tape.centered_player_nr_next = game.centered_player_nr_next;
3866 tape.set_centered_player = TRUE;
3869 for (i = 0; i < NUM_BELTS; i++)
3871 game.belt_dir[i] = MV_NONE;
3872 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3875 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3876 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3878 SCAN_PLAYFIELD(x, y)
3880 Feld[x][y] = level.field[x][y];
3881 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3882 ChangeDelay[x][y] = 0;
3883 ChangePage[x][y] = -1;
3884 #if USE_NEW_CUSTOM_VALUE
3885 CustomValue[x][y] = 0; /* initialized in InitField() */
3887 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3889 WasJustMoving[x][y] = 0;
3890 WasJustFalling[x][y] = 0;
3891 CheckCollision[x][y] = 0;
3892 CheckImpact[x][y] = 0;
3894 Pushed[x][y] = FALSE;
3896 ChangeCount[x][y] = 0;
3897 ChangeEvent[x][y] = -1;
3899 ExplodePhase[x][y] = 0;
3900 ExplodeDelay[x][y] = 0;
3901 ExplodeField[x][y] = EX_TYPE_NONE;
3903 RunnerVisit[x][y] = 0;
3904 PlayerVisit[x][y] = 0;
3907 GfxRandom[x][y] = INIT_GFX_RANDOM();
3908 GfxElement[x][y] = EL_UNDEFINED;
3909 GfxAction[x][y] = ACTION_DEFAULT;
3910 GfxDir[x][y] = MV_NONE;
3911 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3914 SCAN_PLAYFIELD(x, y)
3916 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3918 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3920 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3923 InitField(x, y, TRUE);
3925 ResetGfxAnimation(x, y);
3930 for (i = 0; i < MAX_PLAYERS; i++)
3932 struct PlayerInfo *player = &stored_player[i];
3934 /* set number of special actions for bored and sleeping animation */
3935 player->num_special_action_bored =
3936 get_num_special_action(player->artwork_element,
3937 ACTION_BORING_1, ACTION_BORING_LAST);
3938 player->num_special_action_sleeping =
3939 get_num_special_action(player->artwork_element,
3940 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3943 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3944 emulate_sb ? EMU_SOKOBAN :
3945 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3947 #if USE_NEW_ALL_SLIPPERY
3948 /* initialize type of slippery elements */
3949 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3951 if (!IS_CUSTOM_ELEMENT(i))
3953 /* default: elements slip down either to the left or right randomly */
3954 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3956 /* SP style elements prefer to slip down on the left side */
3957 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3958 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3960 /* BD style elements prefer to slip down on the left side */
3961 if (game.emulation == EMU_BOULDERDASH)
3962 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3967 /* initialize explosion and ignition delay */
3968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970 if (!IS_CUSTOM_ELEMENT(i))
3973 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3974 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3975 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3976 int last_phase = (num_phase + 1) * delay;
3977 int half_phase = (num_phase / 2) * delay;
3979 element_info[i].explosion_delay = last_phase - 1;
3980 element_info[i].ignition_delay = half_phase;
3982 if (i == EL_BLACK_ORB)
3983 element_info[i].ignition_delay = 1;
3987 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3988 element_info[i].explosion_delay = 1;
3990 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3991 element_info[i].ignition_delay = 1;
3995 /* correct non-moving belts to start moving left */
3996 for (i = 0; i < NUM_BELTS; i++)
3997 if (game.belt_dir[i] == MV_NONE)
3998 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4000 /* check if any connected player was not found in playfield */
4001 for (i = 0; i < MAX_PLAYERS; i++)
4003 struct PlayerInfo *player = &stored_player[i];
4005 if (player->connected && !player->present)
4007 for (j = 0; j < MAX_PLAYERS; j++)
4009 struct PlayerInfo *some_player = &stored_player[j];
4010 int jx = some_player->jx, jy = some_player->jy;
4012 /* assign first free player found that is present in the playfield */
4013 if (some_player->present && !some_player->connected)
4015 player->present = TRUE;
4016 player->active = TRUE;
4018 some_player->present = FALSE;
4019 some_player->active = FALSE;
4021 player->initial_element = some_player->initial_element;
4022 player->artwork_element = some_player->artwork_element;
4024 player->block_last_field = some_player->block_last_field;
4025 player->block_delay_adjustment = some_player->block_delay_adjustment;
4027 StorePlayer[jx][jy] = player->element_nr;
4028 player->jx = player->last_jx = jx;
4029 player->jy = player->last_jy = jy;
4039 /* when playing a tape, eliminate all players who do not participate */
4041 for (i = 0; i < MAX_PLAYERS; i++)
4043 if (stored_player[i].active && !tape.player_participates[i])
4045 struct PlayerInfo *player = &stored_player[i];
4046 int jx = player->jx, jy = player->jy;
4048 player->active = FALSE;
4049 StorePlayer[jx][jy] = 0;
4050 Feld[jx][jy] = EL_EMPTY;
4054 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4056 /* when in single player mode, eliminate all but the first active player */
4058 for (i = 0; i < MAX_PLAYERS; i++)
4060 if (stored_player[i].active)
4062 for (j = i + 1; j < MAX_PLAYERS; j++)
4064 if (stored_player[j].active)
4066 struct PlayerInfo *player = &stored_player[j];
4067 int jx = player->jx, jy = player->jy;
4069 player->active = FALSE;
4070 player->present = FALSE;
4072 StorePlayer[jx][jy] = 0;
4073 Feld[jx][jy] = EL_EMPTY;
4080 /* when recording the game, store which players take part in the game */
4083 for (i = 0; i < MAX_PLAYERS; i++)
4084 if (stored_player[i].active)
4085 tape.player_participates[i] = TRUE;
4090 for (i = 0; i < MAX_PLAYERS; i++)
4092 struct PlayerInfo *player = &stored_player[i];
4094 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4099 if (local_player == player)
4100 printf("Player %d is local player.\n", i+1);
4104 if (BorderElement == EL_EMPTY)
4107 SBX_Right = lev_fieldx - SCR_FIELDX;
4109 SBY_Lower = lev_fieldy - SCR_FIELDY;
4114 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4116 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4119 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4120 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4122 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4123 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4125 /* if local player not found, look for custom element that might create
4126 the player (make some assumptions about the right custom element) */
4127 if (!local_player->present)
4129 int start_x = 0, start_y = 0;
4130 int found_rating = 0;
4131 int found_element = EL_UNDEFINED;
4132 int player_nr = local_player->index_nr;
4134 SCAN_PLAYFIELD(x, y)
4136 int element = Feld[x][y];
4141 if (level.use_start_element[player_nr] &&
4142 level.start_element[player_nr] == element &&
4149 found_element = element;
4152 if (!IS_CUSTOM_ELEMENT(element))
4155 if (CAN_CHANGE(element))
4157 for (i = 0; i < element_info[element].num_change_pages; i++)
4159 /* check for player created from custom element as single target */
4160 content = element_info[element].change_page[i].target_element;
4161 is_player = ELEM_IS_PLAYER(content);
4163 if (is_player && (found_rating < 3 ||
4164 (found_rating == 3 && element < found_element)))
4170 found_element = element;
4175 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4177 /* check for player created from custom element as explosion content */
4178 content = element_info[element].content.e[xx][yy];
4179 is_player = ELEM_IS_PLAYER(content);
4181 if (is_player && (found_rating < 2 ||
4182 (found_rating == 2 && element < found_element)))
4184 start_x = x + xx - 1;
4185 start_y = y + yy - 1;
4188 found_element = element;
4191 if (!CAN_CHANGE(element))
4194 for (i = 0; i < element_info[element].num_change_pages; i++)
4196 /* check for player created from custom element as extended target */
4198 element_info[element].change_page[i].target_content.e[xx][yy];
4200 is_player = ELEM_IS_PLAYER(content);
4202 if (is_player && (found_rating < 1 ||
4203 (found_rating == 1 && element < found_element)))
4205 start_x = x + xx - 1;
4206 start_y = y + yy - 1;
4209 found_element = element;
4215 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4216 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4219 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4220 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4225 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4226 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4227 local_player->jx - MIDPOSX);
4229 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4230 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4231 local_player->jy - MIDPOSY);
4235 /* do not use PLAYING mask for fading out from main screen */
4236 game_status = GAME_MODE_MAIN;
4241 if (!game.restart_level)
4242 CloseDoor(DOOR_CLOSE_1);
4245 if (level_editor_test_game)
4246 FadeSkipNextFadeIn();
4248 FadeSetEnterScreen();
4250 if (level_editor_test_game)
4251 fading = fading_none;
4253 fading = menu.destination;
4257 FadeOut(REDRAW_FIELD);
4260 FadeOut(REDRAW_FIELD);
4264 game_status = GAME_MODE_PLAYING;
4267 /* !!! FIX THIS (START) !!! */
4268 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4270 InitGameEngine_EM();
4272 /* blit playfield from scroll buffer to normal back buffer for fading in */
4273 BlitScreenToBitmap_EM(backbuffer);
4280 /* after drawing the level, correct some elements */
4281 if (game.timegate_time_left == 0)
4282 CloseAllOpenTimegates();
4284 /* blit playfield from scroll buffer to normal back buffer for fading in */
4285 if (setup.soft_scrolling)
4286 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4288 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4290 /* !!! FIX THIS (END) !!! */
4293 FadeIn(REDRAW_FIELD);
4296 FadeIn(REDRAW_FIELD);
4301 if (!game.restart_level)
4303 /* copy default game door content to main double buffer */
4304 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4305 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4308 SetPanelBackground();
4309 SetDrawBackgroundMask(REDRAW_DOOR_1);
4312 UpdateAndDisplayGameControlValues();
4314 UpdateGameDoorValues();
4315 DrawGameDoorValues();
4318 if (!game.restart_level)
4322 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4323 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4324 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4328 /* copy actual game door content to door double buffer for OpenDoor() */
4329 BlitBitmap(drawto, bitmap_db_door,
4330 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4332 OpenDoor(DOOR_OPEN_ALL);
4334 PlaySound(SND_GAME_STARTING);
4336 if (setup.sound_music)
4339 KeyboardAutoRepeatOffUnlessAutoplay();
4343 for (i = 0; i < MAX_PLAYERS; i++)
4344 printf("Player %d %sactive.\n",
4345 i + 1, (stored_player[i].active ? "" : "not "));
4356 game.restart_level = FALSE;
4359 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4361 /* this is used for non-R'n'D game engines to update certain engine values */
4363 /* needed to determine if sounds are played within the visible screen area */
4364 scroll_x = actual_scroll_x;
4365 scroll_y = actual_scroll_y;
4368 void InitMovDir(int x, int y)
4370 int i, element = Feld[x][y];
4371 static int xy[4][2] =
4378 static int direction[3][4] =
4380 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4381 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4382 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4391 Feld[x][y] = EL_BUG;
4392 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4395 case EL_SPACESHIP_RIGHT:
4396 case EL_SPACESHIP_UP:
4397 case EL_SPACESHIP_LEFT:
4398 case EL_SPACESHIP_DOWN:
4399 Feld[x][y] = EL_SPACESHIP;
4400 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4403 case EL_BD_BUTTERFLY_RIGHT:
4404 case EL_BD_BUTTERFLY_UP:
4405 case EL_BD_BUTTERFLY_LEFT:
4406 case EL_BD_BUTTERFLY_DOWN:
4407 Feld[x][y] = EL_BD_BUTTERFLY;
4408 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4411 case EL_BD_FIREFLY_RIGHT:
4412 case EL_BD_FIREFLY_UP:
4413 case EL_BD_FIREFLY_LEFT:
4414 case EL_BD_FIREFLY_DOWN:
4415 Feld[x][y] = EL_BD_FIREFLY;
4416 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4419 case EL_PACMAN_RIGHT:
4421 case EL_PACMAN_LEFT:
4422 case EL_PACMAN_DOWN:
4423 Feld[x][y] = EL_PACMAN;
4424 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4427 case EL_YAMYAM_LEFT:
4428 case EL_YAMYAM_RIGHT:
4430 case EL_YAMYAM_DOWN:
4431 Feld[x][y] = EL_YAMYAM;
4432 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4435 case EL_SP_SNIKSNAK:
4436 MovDir[x][y] = MV_UP;
4439 case EL_SP_ELECTRON:
4440 MovDir[x][y] = MV_LEFT;
4447 Feld[x][y] = EL_MOLE;
4448 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4452 if (IS_CUSTOM_ELEMENT(element))
4454 struct ElementInfo *ei = &element_info[element];
4455 int move_direction_initial = ei->move_direction_initial;
4456 int move_pattern = ei->move_pattern;
4458 if (move_direction_initial == MV_START_PREVIOUS)
4460 if (MovDir[x][y] != MV_NONE)
4463 move_direction_initial = MV_START_AUTOMATIC;
4466 if (move_direction_initial == MV_START_RANDOM)
4467 MovDir[x][y] = 1 << RND(4);
4468 else if (move_direction_initial & MV_ANY_DIRECTION)
4469 MovDir[x][y] = move_direction_initial;
4470 else if (move_pattern == MV_ALL_DIRECTIONS ||
4471 move_pattern == MV_TURNING_LEFT ||
4472 move_pattern == MV_TURNING_RIGHT ||
4473 move_pattern == MV_TURNING_LEFT_RIGHT ||
4474 move_pattern == MV_TURNING_RIGHT_LEFT ||
4475 move_pattern == MV_TURNING_RANDOM)
4476 MovDir[x][y] = 1 << RND(4);
4477 else if (move_pattern == MV_HORIZONTAL)
4478 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4479 else if (move_pattern == MV_VERTICAL)
4480 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4481 else if (move_pattern & MV_ANY_DIRECTION)
4482 MovDir[x][y] = element_info[element].move_pattern;
4483 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4484 move_pattern == MV_ALONG_RIGHT_SIDE)
4486 /* use random direction as default start direction */
4487 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4488 MovDir[x][y] = 1 << RND(4);
4490 for (i = 0; i < NUM_DIRECTIONS; i++)
4492 int x1 = x + xy[i][0];
4493 int y1 = y + xy[i][1];
4495 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4497 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4498 MovDir[x][y] = direction[0][i];
4500 MovDir[x][y] = direction[1][i];
4509 MovDir[x][y] = 1 << RND(4);
4511 if (element != EL_BUG &&
4512 element != EL_SPACESHIP &&
4513 element != EL_BD_BUTTERFLY &&
4514 element != EL_BD_FIREFLY)
4517 for (i = 0; i < NUM_DIRECTIONS; i++)
4519 int x1 = x + xy[i][0];
4520 int y1 = y + xy[i][1];
4522 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4524 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4526 MovDir[x][y] = direction[0][i];
4529 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4530 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4532 MovDir[x][y] = direction[1][i];
4541 GfxDir[x][y] = MovDir[x][y];
4544 void InitAmoebaNr(int x, int y)
4547 int group_nr = AmoebeNachbarNr(x, y);
4551 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4553 if (AmoebaCnt[i] == 0)
4561 AmoebaNr[x][y] = group_nr;
4562 AmoebaCnt[group_nr]++;
4563 AmoebaCnt2[group_nr]++;
4566 static void PlayerWins(struct PlayerInfo *player)
4568 player->LevelSolved = TRUE;
4569 player->GameOver = TRUE;
4571 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4572 level.native_em_level->lev->score : player->score);
4574 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4575 player->LevelSolved_CountingScore = player->score_final;
4580 static int time, time_final;
4581 static int score, score_final;
4582 static int game_over_delay_1 = 0;
4583 static int game_over_delay_2 = 0;
4584 int game_over_delay_value_1 = 50;
4585 int game_over_delay_value_2 = 50;
4587 if (!local_player->LevelSolved_GameWon)
4591 /* do not start end game actions before the player stops moving (to exit) */
4592 if (local_player->MovPos)
4595 local_player->LevelSolved_GameWon = TRUE;
4596 local_player->LevelSolved_SaveTape = tape.recording;
4597 local_player->LevelSolved_SaveScore = !tape.playing;
4599 if (tape.auto_play) /* tape might already be stopped here */
4600 tape.auto_play_level_solved = TRUE;
4606 game_over_delay_1 = game_over_delay_value_1;
4607 game_over_delay_2 = game_over_delay_value_2;
4609 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4610 score = score_final = local_player->score_final;
4615 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4617 else if (level.time == 0 && TimePlayed < 999)
4620 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4623 local_player->score_final = score_final;
4625 if (level_editor_test_game)
4628 score = score_final;
4631 local_player->LevelSolved_CountingTime = time;
4632 local_player->LevelSolved_CountingScore = score;
4634 game_panel_controls[GAME_PANEL_TIME].value = time;
4635 game_panel_controls[GAME_PANEL_SCORE].value = score;
4637 DisplayGameControlValues();
4639 DrawGameValue_Time(time);
4640 DrawGameValue_Score(score);
4644 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4646 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4648 /* close exit door after last player */
4649 if ((AllPlayersGone &&
4650 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4651 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4652 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4653 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4654 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4656 int element = Feld[ExitX][ExitY];
4659 if (element == EL_EM_EXIT_OPEN ||
4660 element == EL_EM_STEEL_EXIT_OPEN)
4667 Feld[ExitX][ExitY] =
4668 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4669 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4670 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4671 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4672 EL_EM_STEEL_EXIT_CLOSING);
4674 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4678 /* player disappears */
4679 DrawLevelField(ExitX, ExitY);
4682 for (i = 0; i < MAX_PLAYERS; i++)
4684 struct PlayerInfo *player = &stored_player[i];
4686 if (player->present)
4688 RemovePlayer(player);
4690 /* player disappears */
4691 DrawLevelField(player->jx, player->jy);
4696 PlaySound(SND_GAME_WINNING);
4699 if (game_over_delay_1 > 0)
4701 game_over_delay_1--;
4706 if (time != time_final)
4708 int time_to_go = ABS(time_final - time);
4709 int time_count_dir = (time < time_final ? +1 : -1);
4710 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4712 time += time_count_steps * time_count_dir;
4713 score += time_count_steps * level.score[SC_TIME_BONUS];
4716 local_player->LevelSolved_CountingTime = time;
4717 local_player->LevelSolved_CountingScore = score;
4719 game_panel_controls[GAME_PANEL_TIME].value = time;
4720 game_panel_controls[GAME_PANEL_SCORE].value = score;
4722 DisplayGameControlValues();
4724 DrawGameValue_Time(time);
4725 DrawGameValue_Score(score);
4728 if (time == time_final)
4729 StopSound(SND_GAME_LEVELTIME_BONUS);
4730 else if (setup.sound_loops)
4731 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4733 PlaySound(SND_GAME_LEVELTIME_BONUS);
4738 local_player->LevelSolved_PanelOff = TRUE;
4740 if (game_over_delay_2 > 0)
4742 game_over_delay_2--;
4755 boolean raise_level = FALSE;
4757 local_player->LevelSolved_GameEnd = TRUE;
4759 CloseDoor(DOOR_CLOSE_1);
4761 if (local_player->LevelSolved_SaveTape)
4768 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4770 SaveTape(tape.level_nr); /* ask to save tape */
4774 if (level_editor_test_game)
4776 game_status = GAME_MODE_MAIN;
4779 DrawAndFadeInMainMenu(REDRAW_FIELD);
4787 if (!local_player->LevelSolved_SaveScore)
4790 FadeOut(REDRAW_FIELD);
4793 game_status = GAME_MODE_MAIN;
4795 DrawAndFadeInMainMenu(REDRAW_FIELD);
4800 if (level_nr == leveldir_current->handicap_level)
4802 leveldir_current->handicap_level++;
4803 SaveLevelSetup_SeriesInfo();
4806 if (level_nr < leveldir_current->last_level)
4807 raise_level = TRUE; /* advance to next level */
4809 if ((hi_pos = NewHiScore()) >= 0)
4811 game_status = GAME_MODE_SCORES;
4813 DrawHallOfFame(hi_pos);
4824 FadeOut(REDRAW_FIELD);
4827 game_status = GAME_MODE_MAIN;
4835 DrawAndFadeInMainMenu(REDRAW_FIELD);
4844 LoadScore(level_nr);
4846 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4847 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4850 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4852 if (local_player->score_final > highscore[k].Score)
4854 /* player has made it to the hall of fame */
4856 if (k < MAX_SCORE_ENTRIES - 1)
4858 int m = MAX_SCORE_ENTRIES - 1;
4861 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4862 if (strEqual(setup.player_name, highscore[l].Name))
4864 if (m == k) /* player's new highscore overwrites his old one */
4868 for (l = m; l > k; l--)
4870 strcpy(highscore[l].Name, highscore[l - 1].Name);
4871 highscore[l].Score = highscore[l - 1].Score;
4878 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4879 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4880 highscore[k].Score = local_player->score_final;
4886 else if (!strncmp(setup.player_name, highscore[k].Name,
4887 MAX_PLAYER_NAME_LEN))
4888 break; /* player already there with a higher score */
4894 SaveScore(level_nr);
4899 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4901 int element = Feld[x][y];
4902 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4903 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4904 int horiz_move = (dx != 0);
4905 int sign = (horiz_move ? dx : dy);
4906 int step = sign * element_info[element].move_stepsize;
4908 /* special values for move stepsize for spring and things on conveyor belt */
4911 if (CAN_FALL(element) &&
4912 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4913 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4914 else if (element == EL_SPRING)
4915 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4921 inline static int getElementMoveStepsize(int x, int y)
4923 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4926 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4928 if (player->GfxAction != action || player->GfxDir != dir)
4931 printf("Player frame reset! (%d => %d, %d => %d)\n",
4932 player->GfxAction, action, player->GfxDir, dir);
4935 player->GfxAction = action;
4936 player->GfxDir = dir;
4938 player->StepFrame = 0;
4942 #if USE_GFX_RESET_GFX_ANIMATION
4943 static void ResetGfxFrame(int x, int y, boolean redraw)
4945 int element = Feld[x][y];
4946 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4947 int last_gfx_frame = GfxFrame[x][y];
4949 if (graphic_info[graphic].anim_global_sync)
4950 GfxFrame[x][y] = FrameCounter;
4951 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4952 GfxFrame[x][y] = CustomValue[x][y];
4953 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4954 GfxFrame[x][y] = element_info[element].collect_score;
4955 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4956 GfxFrame[x][y] = ChangeDelay[x][y];
4958 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4959 DrawLevelGraphicAnimation(x, y, graphic);
4963 static void ResetGfxAnimation(int x, int y)
4965 GfxAction[x][y] = ACTION_DEFAULT;
4966 GfxDir[x][y] = MovDir[x][y];
4969 #if USE_GFX_RESET_GFX_ANIMATION
4970 ResetGfxFrame(x, y, FALSE);
4974 static void ResetRandomAnimationValue(int x, int y)
4976 GfxRandom[x][y] = INIT_GFX_RANDOM();
4979 void InitMovingField(int x, int y, int direction)
4981 int element = Feld[x][y];
4982 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4983 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4986 boolean is_moving_before, is_moving_after;
4988 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4991 /* check if element was/is moving or being moved before/after mode change */
4994 is_moving_before = (WasJustMoving[x][y] != 0);
4996 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4997 is_moving_before = WasJustMoving[x][y];
5000 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5002 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5004 /* reset animation only for moving elements which change direction of moving
5005 or which just started or stopped moving
5006 (else CEs with property "can move" / "not moving" are reset each frame) */
5007 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5009 if (is_moving_before != is_moving_after ||
5010 direction != MovDir[x][y])
5011 ResetGfxAnimation(x, y);
5013 if ((is_moving_before || is_moving_after) && !continues_moving)
5014 ResetGfxAnimation(x, y);
5017 if (!continues_moving)
5018 ResetGfxAnimation(x, y);
5021 MovDir[x][y] = direction;
5022 GfxDir[x][y] = direction;
5024 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5025 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5026 direction == MV_DOWN && CAN_FALL(element) ?
5027 ACTION_FALLING : ACTION_MOVING);
5029 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5030 ACTION_FALLING : ACTION_MOVING);
5033 /* this is needed for CEs with property "can move" / "not moving" */
5035 if (is_moving_after)
5037 if (Feld[newx][newy] == EL_EMPTY)
5038 Feld[newx][newy] = EL_BLOCKED;
5040 MovDir[newx][newy] = MovDir[x][y];
5042 #if USE_NEW_CUSTOM_VALUE
5043 CustomValue[newx][newy] = CustomValue[x][y];
5046 GfxFrame[newx][newy] = GfxFrame[x][y];
5047 GfxRandom[newx][newy] = GfxRandom[x][y];
5048 GfxAction[newx][newy] = GfxAction[x][y];
5049 GfxDir[newx][newy] = GfxDir[x][y];
5053 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5055 int direction = MovDir[x][y];
5056 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5057 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5063 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5065 int oldx = x, oldy = y;
5066 int direction = MovDir[x][y];
5068 if (direction == MV_LEFT)
5070 else if (direction == MV_RIGHT)
5072 else if (direction == MV_UP)
5074 else if (direction == MV_DOWN)
5077 *comes_from_x = oldx;
5078 *comes_from_y = oldy;
5081 int MovingOrBlocked2Element(int x, int y)
5083 int element = Feld[x][y];
5085 if (element == EL_BLOCKED)
5089 Blocked2Moving(x, y, &oldx, &oldy);
5090 return Feld[oldx][oldy];
5096 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5098 /* like MovingOrBlocked2Element(), but if element is moving
5099 and (x,y) is the field the moving element is just leaving,
5100 return EL_BLOCKED instead of the element value */
5101 int element = Feld[x][y];
5103 if (IS_MOVING(x, y))
5105 if (element == EL_BLOCKED)
5109 Blocked2Moving(x, y, &oldx, &oldy);
5110 return Feld[oldx][oldy];
5119 static void RemoveField(int x, int y)
5121 Feld[x][y] = EL_EMPTY;
5127 #if USE_NEW_CUSTOM_VALUE
5128 CustomValue[x][y] = 0;
5132 ChangeDelay[x][y] = 0;
5133 ChangePage[x][y] = -1;
5134 Pushed[x][y] = FALSE;
5137 ExplodeField[x][y] = EX_TYPE_NONE;
5140 GfxElement[x][y] = EL_UNDEFINED;
5141 GfxAction[x][y] = ACTION_DEFAULT;
5142 GfxDir[x][y] = MV_NONE;
5143 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5146 void RemoveMovingField(int x, int y)
5148 int oldx = x, oldy = y, newx = x, newy = y;
5149 int element = Feld[x][y];
5150 int next_element = EL_UNDEFINED;
5152 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5155 if (IS_MOVING(x, y))
5157 Moving2Blocked(x, y, &newx, &newy);
5159 if (Feld[newx][newy] != EL_BLOCKED)
5161 /* element is moving, but target field is not free (blocked), but
5162 already occupied by something different (example: acid pool);
5163 in this case, only remove the moving field, but not the target */
5165 RemoveField(oldx, oldy);
5167 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5169 TEST_DrawLevelField(oldx, oldy);
5174 else if (element == EL_BLOCKED)
5176 Blocked2Moving(x, y, &oldx, &oldy);
5177 if (!IS_MOVING(oldx, oldy))
5181 if (element == EL_BLOCKED &&
5182 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5183 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5184 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5185 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5186 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5187 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5188 next_element = get_next_element(Feld[oldx][oldy]);
5190 RemoveField(oldx, oldy);
5191 RemoveField(newx, newy);
5193 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5195 if (next_element != EL_UNDEFINED)
5196 Feld[oldx][oldy] = next_element;
5198 TEST_DrawLevelField(oldx, oldy);
5199 TEST_DrawLevelField(newx, newy);
5202 void DrawDynamite(int x, int y)
5204 int sx = SCREENX(x), sy = SCREENY(y);
5205 int graphic = el2img(Feld[x][y]);
5208 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5211 if (IS_WALKABLE_INSIDE(Back[x][y]))
5215 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5216 else if (Store[x][y])
5217 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5219 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5221 if (Back[x][y] || Store[x][y])
5222 DrawGraphicThruMask(sx, sy, graphic, frame);
5224 DrawGraphic(sx, sy, graphic, frame);
5227 void CheckDynamite(int x, int y)
5229 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5233 if (MovDelay[x][y] != 0)
5236 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5242 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5247 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5249 boolean num_checked_players = 0;
5252 for (i = 0; i < MAX_PLAYERS; i++)
5254 if (stored_player[i].active)
5256 int sx = stored_player[i].jx;
5257 int sy = stored_player[i].jy;
5259 if (num_checked_players == 0)
5266 *sx1 = MIN(*sx1, sx);
5267 *sy1 = MIN(*sy1, sy);
5268 *sx2 = MAX(*sx2, sx);
5269 *sy2 = MAX(*sy2, sy);
5272 num_checked_players++;
5277 static boolean checkIfAllPlayersFitToScreen_RND()
5279 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5281 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5283 return (sx2 - sx1 < SCR_FIELDX &&
5284 sy2 - sy1 < SCR_FIELDY);
5287 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5289 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5291 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5293 *sx = (sx1 + sx2) / 2;
5294 *sy = (sy1 + sy2) / 2;
5297 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5298 boolean center_screen, boolean quick_relocation)
5300 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5301 boolean no_delay = (tape.warp_forward);
5302 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5303 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5305 if (quick_relocation)
5307 int offset = game.scroll_delay_value;
5309 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5311 if (!level.shifted_relocation || center_screen)
5313 /* quick relocation (without scrolling), with centering of screen */
5315 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5316 x > SBX_Right + MIDPOSX ? SBX_Right :
5319 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5320 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5325 /* quick relocation (without scrolling), but do not center screen */
5327 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5328 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5331 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5332 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5335 int offset_x = x + (scroll_x - center_scroll_x);
5336 int offset_y = y + (scroll_y - center_scroll_y);
5338 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5339 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5340 offset_x - MIDPOSX);
5342 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5343 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5344 offset_y - MIDPOSY);
5349 /* quick relocation (without scrolling), inside visible screen area */
5351 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5352 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5353 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5355 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5356 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5357 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5359 /* don't scroll over playfield boundaries */
5360 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5361 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5363 /* don't scroll over playfield boundaries */
5364 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5365 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5368 RedrawPlayfield(TRUE, 0,0,0,0);
5373 int scroll_xx, scroll_yy;
5375 if (!level.shifted_relocation || center_screen)
5377 /* visible relocation (with scrolling), with centering of screen */
5379 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5380 x > SBX_Right + MIDPOSX ? SBX_Right :
5383 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5384 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5389 /* visible relocation (with scrolling), but do not center screen */
5391 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5392 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5395 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5396 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5399 int offset_x = x + (scroll_x - center_scroll_x);
5400 int offset_y = y + (scroll_y - center_scroll_y);
5402 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5403 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5404 offset_x - MIDPOSX);
5406 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5407 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5408 offset_y - MIDPOSY);
5413 /* visible relocation (with scrolling), with centering of screen */
5415 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5416 x > SBX_Right + MIDPOSX ? SBX_Right :
5419 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5420 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5424 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5426 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5429 int fx = FX, fy = FY;
5431 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5432 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5434 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5440 fx += dx * TILEX / 2;
5441 fy += dy * TILEY / 2;
5443 ScrollLevel(dx, dy);
5446 /* scroll in two steps of half tile size to make things smoother */
5447 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5449 Delay(wait_delay_value);
5451 /* scroll second step to align at full tile size */
5453 Delay(wait_delay_value);
5458 Delay(wait_delay_value);
5462 void RelocatePlayer(int jx, int jy, int el_player_raw)
5464 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5465 int player_nr = GET_PLAYER_NR(el_player);
5466 struct PlayerInfo *player = &stored_player[player_nr];
5467 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5468 boolean no_delay = (tape.warp_forward);
5469 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5470 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5471 int old_jx = player->jx;
5472 int old_jy = player->jy;
5473 int old_element = Feld[old_jx][old_jy];
5474 int element = Feld[jx][jy];
5475 boolean player_relocated = (old_jx != jx || old_jy != jy);
5477 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5478 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5479 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5480 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5481 int leave_side_horiz = move_dir_horiz;
5482 int leave_side_vert = move_dir_vert;
5483 int enter_side = enter_side_horiz | enter_side_vert;
5484 int leave_side = leave_side_horiz | leave_side_vert;
5486 if (player->GameOver) /* do not reanimate dead player */
5489 if (!player_relocated) /* no need to relocate the player */
5492 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5494 RemoveField(jx, jy); /* temporarily remove newly placed player */
5495 DrawLevelField(jx, jy);
5498 if (player->present)
5500 while (player->MovPos)
5502 ScrollPlayer(player, SCROLL_GO_ON);
5503 ScrollScreen(NULL, SCROLL_GO_ON);
5505 AdvanceFrameAndPlayerCounters(player->index_nr);
5510 Delay(wait_delay_value);
5513 DrawPlayer(player); /* needed here only to cleanup last field */
5514 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5516 player->is_moving = FALSE;
5519 if (IS_CUSTOM_ELEMENT(old_element))
5520 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5522 player->index_bit, leave_side);
5524 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5526 player->index_bit, leave_side);
5528 Feld[jx][jy] = el_player;
5529 InitPlayerField(jx, jy, el_player, TRUE);
5531 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5533 Feld[jx][jy] = element;
5534 InitField(jx, jy, FALSE);
5537 /* only visually relocate centered player */
5538 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5539 FALSE, level.instant_relocation);
5541 TestIfPlayerTouchesBadThing(jx, jy);
5542 TestIfPlayerTouchesCustomElement(jx, jy);
5544 if (IS_CUSTOM_ELEMENT(element))
5545 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5546 player->index_bit, enter_side);
5548 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5549 player->index_bit, enter_side);
5552 void Explode(int ex, int ey, int phase, int mode)
5558 /* !!! eliminate this variable !!! */
5559 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5561 if (game.explosions_delayed)
5563 ExplodeField[ex][ey] = mode;
5567 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5569 int center_element = Feld[ex][ey];
5570 int artwork_element, explosion_element; /* set these values later */
5573 /* --- This is only really needed (and now handled) in "Impact()". --- */
5574 /* do not explode moving elements that left the explode field in time */
5575 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5576 center_element == EL_EMPTY &&
5577 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5582 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5583 if (mode == EX_TYPE_NORMAL ||
5584 mode == EX_TYPE_CENTER ||
5585 mode == EX_TYPE_CROSS)
5586 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5589 /* remove things displayed in background while burning dynamite */
5590 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5593 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5595 /* put moving element to center field (and let it explode there) */
5596 center_element = MovingOrBlocked2Element(ex, ey);
5597 RemoveMovingField(ex, ey);
5598 Feld[ex][ey] = center_element;
5601 /* now "center_element" is finally determined -- set related values now */
5602 artwork_element = center_element; /* for custom player artwork */
5603 explosion_element = center_element; /* for custom player artwork */
5605 if (IS_PLAYER(ex, ey))
5607 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5609 artwork_element = stored_player[player_nr].artwork_element;
5611 if (level.use_explosion_element[player_nr])
5613 explosion_element = level.explosion_element[player_nr];
5614 artwork_element = explosion_element;
5619 if (mode == EX_TYPE_NORMAL ||
5620 mode == EX_TYPE_CENTER ||
5621 mode == EX_TYPE_CROSS)
5622 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5625 last_phase = element_info[explosion_element].explosion_delay + 1;
5627 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5629 int xx = x - ex + 1;
5630 int yy = y - ey + 1;
5633 if (!IN_LEV_FIELD(x, y) ||
5634 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5635 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5638 element = Feld[x][y];
5640 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5642 element = MovingOrBlocked2Element(x, y);
5644 if (!IS_EXPLOSION_PROOF(element))
5645 RemoveMovingField(x, y);
5648 /* indestructible elements can only explode in center (but not flames) */
5649 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5650 mode == EX_TYPE_BORDER)) ||
5651 element == EL_FLAMES)
5654 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5655 behaviour, for example when touching a yamyam that explodes to rocks
5656 with active deadly shield, a rock is created under the player !!! */
5657 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5659 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5660 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5661 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5663 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5666 if (IS_ACTIVE_BOMB(element))
5668 /* re-activate things under the bomb like gate or penguin */
5669 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5676 /* save walkable background elements while explosion on same tile */
5677 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5678 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5679 Back[x][y] = element;
5681 /* ignite explodable elements reached by other explosion */
5682 if (element == EL_EXPLOSION)
5683 element = Store2[x][y];
5685 if (AmoebaNr[x][y] &&
5686 (element == EL_AMOEBA_FULL ||
5687 element == EL_BD_AMOEBA ||
5688 element == EL_AMOEBA_GROWING))
5690 AmoebaCnt[AmoebaNr[x][y]]--;
5691 AmoebaCnt2[AmoebaNr[x][y]]--;
5696 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5698 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5700 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5702 if (PLAYERINFO(ex, ey)->use_murphy)
5703 Store[x][y] = EL_EMPTY;
5706 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5707 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5708 else if (ELEM_IS_PLAYER(center_element))
5709 Store[x][y] = EL_EMPTY;
5710 else if (center_element == EL_YAMYAM)
5711 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5712 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5713 Store[x][y] = element_info[center_element].content.e[xx][yy];
5715 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5716 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5717 otherwise) -- FIX THIS !!! */
5718 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5719 Store[x][y] = element_info[element].content.e[1][1];
5721 else if (!CAN_EXPLODE(element))
5722 Store[x][y] = element_info[element].content.e[1][1];
5725 Store[x][y] = EL_EMPTY;
5727 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5728 center_element == EL_AMOEBA_TO_DIAMOND)
5729 Store2[x][y] = element;
5731 Feld[x][y] = EL_EXPLOSION;
5732 GfxElement[x][y] = artwork_element;
5734 ExplodePhase[x][y] = 1;
5735 ExplodeDelay[x][y] = last_phase;
5740 if (center_element == EL_YAMYAM)
5741 game.yamyam_content_nr =
5742 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5754 GfxFrame[x][y] = 0; /* restart explosion animation */
5756 last_phase = ExplodeDelay[x][y];
5758 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5762 /* activate this even in non-DEBUG version until cause for crash in
5763 getGraphicAnimationFrame() (see below) is found and eliminated */
5769 /* this can happen if the player leaves an explosion just in time */
5770 if (GfxElement[x][y] == EL_UNDEFINED)
5771 GfxElement[x][y] = EL_EMPTY;
5773 if (GfxElement[x][y] == EL_UNDEFINED)
5776 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5777 printf("Explode(): This should never happen!\n");
5780 GfxElement[x][y] = EL_EMPTY;
5786 border_element = Store2[x][y];
5787 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5788 border_element = StorePlayer[x][y];
5790 if (phase == element_info[border_element].ignition_delay ||
5791 phase == last_phase)
5793 boolean border_explosion = FALSE;
5795 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5796 !PLAYER_EXPLOSION_PROTECTED(x, y))
5798 KillPlayerUnlessExplosionProtected(x, y);
5799 border_explosion = TRUE;
5801 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5803 Feld[x][y] = Store2[x][y];
5806 border_explosion = TRUE;
5808 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5810 AmoebeUmwandeln(x, y);
5812 border_explosion = TRUE;
5815 /* if an element just explodes due to another explosion (chain-reaction),
5816 do not immediately end the new explosion when it was the last frame of
5817 the explosion (as it would be done in the following "if"-statement!) */
5818 if (border_explosion && phase == last_phase)
5822 if (phase == last_phase)
5826 element = Feld[x][y] = Store[x][y];
5827 Store[x][y] = Store2[x][y] = 0;
5828 GfxElement[x][y] = EL_UNDEFINED;
5830 /* player can escape from explosions and might therefore be still alive */
5831 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5832 element <= EL_PLAYER_IS_EXPLODING_4)
5834 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5835 int explosion_element = EL_PLAYER_1 + player_nr;
5836 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5837 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5839 if (level.use_explosion_element[player_nr])
5840 explosion_element = level.explosion_element[player_nr];
5842 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5843 element_info[explosion_element].content.e[xx][yy]);
5846 /* restore probably existing indestructible background element */
5847 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5848 element = Feld[x][y] = Back[x][y];
5851 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5852 GfxDir[x][y] = MV_NONE;
5853 ChangeDelay[x][y] = 0;
5854 ChangePage[x][y] = -1;
5856 #if USE_NEW_CUSTOM_VALUE
5857 CustomValue[x][y] = 0;
5860 InitField_WithBug2(x, y, FALSE);
5862 TEST_DrawLevelField(x, y);
5864 TestIfElementTouchesCustomElement(x, y);
5866 if (GFX_CRUMBLED(element))
5867 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5869 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5870 StorePlayer[x][y] = 0;
5872 if (ELEM_IS_PLAYER(element))
5873 RelocatePlayer(x, y, element);
5875 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5877 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5878 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5881 TEST_DrawLevelFieldCrumbledSand(x, y);
5883 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5885 DrawLevelElement(x, y, Back[x][y]);
5886 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5888 else if (IS_WALKABLE_UNDER(Back[x][y]))
5890 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5891 DrawLevelElementThruMask(x, y, Back[x][y]);
5893 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5894 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5898 void DynaExplode(int ex, int ey)
5901 int dynabomb_element = Feld[ex][ey];
5902 int dynabomb_size = 1;
5903 boolean dynabomb_xl = FALSE;
5904 struct PlayerInfo *player;
5905 static int xy[4][2] =
5913 if (IS_ACTIVE_BOMB(dynabomb_element))
5915 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5916 dynabomb_size = player->dynabomb_size;
5917 dynabomb_xl = player->dynabomb_xl;
5918 player->dynabombs_left++;
5921 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5923 for (i = 0; i < NUM_DIRECTIONS; i++)
5925 for (j = 1; j <= dynabomb_size; j++)
5927 int x = ex + j * xy[i][0];
5928 int y = ey + j * xy[i][1];
5931 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5934 element = Feld[x][y];
5936 /* do not restart explosions of fields with active bombs */
5937 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5940 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5942 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5943 !IS_DIGGABLE(element) && !dynabomb_xl)
5949 void Bang(int x, int y)
5951 int element = MovingOrBlocked2Element(x, y);
5952 int explosion_type = EX_TYPE_NORMAL;
5954 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5956 struct PlayerInfo *player = PLAYERINFO(x, y);
5958 #if USE_FIX_CE_ACTION_WITH_PLAYER
5959 element = Feld[x][y] = player->initial_element;
5961 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5962 player->element_nr);
5965 if (level.use_explosion_element[player->index_nr])
5967 int explosion_element = level.explosion_element[player->index_nr];
5969 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5970 explosion_type = EX_TYPE_CROSS;
5971 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5972 explosion_type = EX_TYPE_CENTER;
5980 case EL_BD_BUTTERFLY:
5983 case EL_DARK_YAMYAM:
5987 RaiseScoreElement(element);
5990 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5991 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5992 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5993 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5994 case EL_DYNABOMB_INCREASE_NUMBER:
5995 case EL_DYNABOMB_INCREASE_SIZE:
5996 case EL_DYNABOMB_INCREASE_POWER:
5997 explosion_type = EX_TYPE_DYNA;
6000 case EL_DC_LANDMINE:
6002 case EL_EM_EXIT_OPEN:
6003 case EL_EM_STEEL_EXIT_OPEN:
6005 explosion_type = EX_TYPE_CENTER;
6010 case EL_LAMP_ACTIVE:
6011 case EL_AMOEBA_TO_DIAMOND:
6012 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6013 explosion_type = EX_TYPE_CENTER;
6017 if (element_info[element].explosion_type == EXPLODES_CROSS)
6018 explosion_type = EX_TYPE_CROSS;
6019 else if (element_info[element].explosion_type == EXPLODES_1X1)
6020 explosion_type = EX_TYPE_CENTER;
6024 if (explosion_type == EX_TYPE_DYNA)
6027 Explode(x, y, EX_PHASE_START, explosion_type);
6029 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6032 void SplashAcid(int x, int y)
6034 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6035 (!IN_LEV_FIELD(x - 1, y - 2) ||
6036 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6037 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6039 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6040 (!IN_LEV_FIELD(x + 1, y - 2) ||
6041 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6042 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6044 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6047 static void InitBeltMovement()
6049 static int belt_base_element[4] =
6051 EL_CONVEYOR_BELT_1_LEFT,
6052 EL_CONVEYOR_BELT_2_LEFT,
6053 EL_CONVEYOR_BELT_3_LEFT,
6054 EL_CONVEYOR_BELT_4_LEFT
6056 static int belt_base_active_element[4] =
6058 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6059 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6060 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6061 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6066 /* set frame order for belt animation graphic according to belt direction */
6067 for (i = 0; i < NUM_BELTS; i++)
6071 for (j = 0; j < NUM_BELT_PARTS; j++)
6073 int element = belt_base_active_element[belt_nr] + j;
6074 int graphic_1 = el2img(element);
6075 int graphic_2 = el2panelimg(element);
6077 if (game.belt_dir[i] == MV_LEFT)
6079 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6080 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6084 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6085 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6090 SCAN_PLAYFIELD(x, y)
6092 int element = Feld[x][y];
6094 for (i = 0; i < NUM_BELTS; i++)
6096 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6098 int e_belt_nr = getBeltNrFromBeltElement(element);
6101 if (e_belt_nr == belt_nr)
6103 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6105 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6112 static void ToggleBeltSwitch(int x, int y)
6114 static int belt_base_element[4] =
6116 EL_CONVEYOR_BELT_1_LEFT,
6117 EL_CONVEYOR_BELT_2_LEFT,
6118 EL_CONVEYOR_BELT_3_LEFT,
6119 EL_CONVEYOR_BELT_4_LEFT
6121 static int belt_base_active_element[4] =
6123 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6124 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6125 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6126 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6128 static int belt_base_switch_element[4] =
6130 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6131 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6132 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6133 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6135 static int belt_move_dir[4] =
6143 int element = Feld[x][y];
6144 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6145 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6146 int belt_dir = belt_move_dir[belt_dir_nr];
6149 if (!IS_BELT_SWITCH(element))
6152 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6153 game.belt_dir[belt_nr] = belt_dir;
6155 if (belt_dir_nr == 3)
6158 /* set frame order for belt animation graphic according to belt direction */
6159 for (i = 0; i < NUM_BELT_PARTS; i++)
6161 int element = belt_base_active_element[belt_nr] + i;
6162 int graphic_1 = el2img(element);
6163 int graphic_2 = el2panelimg(element);
6165 if (belt_dir == MV_LEFT)
6167 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6168 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6172 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6173 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6177 SCAN_PLAYFIELD(xx, yy)
6179 int element = Feld[xx][yy];
6181 if (IS_BELT_SWITCH(element))
6183 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6185 if (e_belt_nr == belt_nr)
6187 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6188 TEST_DrawLevelField(xx, yy);
6191 else if (IS_BELT(element) && belt_dir != MV_NONE)
6193 int e_belt_nr = getBeltNrFromBeltElement(element);
6195 if (e_belt_nr == belt_nr)
6197 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6199 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6200 TEST_DrawLevelField(xx, yy);
6203 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6205 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6207 if (e_belt_nr == belt_nr)
6209 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6211 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6212 TEST_DrawLevelField(xx, yy);
6218 static void ToggleSwitchgateSwitch(int x, int y)
6222 game.switchgate_pos = !game.switchgate_pos;
6224 SCAN_PLAYFIELD(xx, yy)
6226 int element = Feld[xx][yy];
6228 #if !USE_BOTH_SWITCHGATE_SWITCHES
6229 if (element == EL_SWITCHGATE_SWITCH_UP ||
6230 element == EL_SWITCHGATE_SWITCH_DOWN)
6232 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6233 TEST_DrawLevelField(xx, yy);
6235 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6236 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6238 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6239 TEST_DrawLevelField(xx, yy);
6242 if (element == EL_SWITCHGATE_SWITCH_UP)
6244 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6245 TEST_DrawLevelField(xx, yy);
6247 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6249 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6250 TEST_DrawLevelField(xx, yy);
6252 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6254 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6255 TEST_DrawLevelField(xx, yy);
6257 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6259 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6260 TEST_DrawLevelField(xx, yy);
6263 else if (element == EL_SWITCHGATE_OPEN ||
6264 element == EL_SWITCHGATE_OPENING)
6266 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6268 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6270 else if (element == EL_SWITCHGATE_CLOSED ||
6271 element == EL_SWITCHGATE_CLOSING)
6273 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6275 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6280 static int getInvisibleActiveFromInvisibleElement(int element)
6282 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6283 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6284 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6288 static int getInvisibleFromInvisibleActiveElement(int element)
6290 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6291 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6292 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6296 static void RedrawAllLightSwitchesAndInvisibleElements()
6300 SCAN_PLAYFIELD(x, y)
6302 int element = Feld[x][y];
6304 if (element == EL_LIGHT_SWITCH &&
6305 game.light_time_left > 0)
6307 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6308 TEST_DrawLevelField(x, y);
6310 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6311 game.light_time_left == 0)
6313 Feld[x][y] = EL_LIGHT_SWITCH;
6314 TEST_DrawLevelField(x, y);
6316 else if (element == EL_EMC_DRIPPER &&
6317 game.light_time_left > 0)
6319 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6320 TEST_DrawLevelField(x, y);
6322 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6323 game.light_time_left == 0)
6325 Feld[x][y] = EL_EMC_DRIPPER;
6326 TEST_DrawLevelField(x, y);
6328 else if (element == EL_INVISIBLE_STEELWALL ||
6329 element == EL_INVISIBLE_WALL ||
6330 element == EL_INVISIBLE_SAND)
6332 if (game.light_time_left > 0)
6333 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6335 TEST_DrawLevelField(x, y);
6337 /* uncrumble neighbour fields, if needed */
6338 if (element == EL_INVISIBLE_SAND)
6339 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6341 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6342 element == EL_INVISIBLE_WALL_ACTIVE ||
6343 element == EL_INVISIBLE_SAND_ACTIVE)
6345 if (game.light_time_left == 0)
6346 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6348 TEST_DrawLevelField(x, y);
6350 /* re-crumble neighbour fields, if needed */
6351 if (element == EL_INVISIBLE_SAND)
6352 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6357 static void RedrawAllInvisibleElementsForLenses()
6361 SCAN_PLAYFIELD(x, y)
6363 int element = Feld[x][y];
6365 if (element == EL_EMC_DRIPPER &&
6366 game.lenses_time_left > 0)
6368 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6369 TEST_DrawLevelField(x, y);
6371 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6372 game.lenses_time_left == 0)
6374 Feld[x][y] = EL_EMC_DRIPPER;
6375 TEST_DrawLevelField(x, y);
6377 else if (element == EL_INVISIBLE_STEELWALL ||
6378 element == EL_INVISIBLE_WALL ||
6379 element == EL_INVISIBLE_SAND)
6381 if (game.lenses_time_left > 0)
6382 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6384 TEST_DrawLevelField(x, y);
6386 /* uncrumble neighbour fields, if needed */
6387 if (element == EL_INVISIBLE_SAND)
6388 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6390 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6391 element == EL_INVISIBLE_WALL_ACTIVE ||
6392 element == EL_INVISIBLE_SAND_ACTIVE)
6394 if (game.lenses_time_left == 0)
6395 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6397 TEST_DrawLevelField(x, y);
6399 /* re-crumble neighbour fields, if needed */
6400 if (element == EL_INVISIBLE_SAND)
6401 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6406 static void RedrawAllInvisibleElementsForMagnifier()
6410 SCAN_PLAYFIELD(x, y)
6412 int element = Feld[x][y];
6414 if (element == EL_EMC_FAKE_GRASS &&
6415 game.magnify_time_left > 0)
6417 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6418 TEST_DrawLevelField(x, y);
6420 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6421 game.magnify_time_left == 0)
6423 Feld[x][y] = EL_EMC_FAKE_GRASS;
6424 TEST_DrawLevelField(x, y);
6426 else if (IS_GATE_GRAY(element) &&
6427 game.magnify_time_left > 0)
6429 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6430 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6431 IS_EM_GATE_GRAY(element) ?
6432 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6433 IS_EMC_GATE_GRAY(element) ?
6434 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6436 TEST_DrawLevelField(x, y);
6438 else if (IS_GATE_GRAY_ACTIVE(element) &&
6439 game.magnify_time_left == 0)
6441 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6442 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6443 IS_EM_GATE_GRAY_ACTIVE(element) ?
6444 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6445 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6446 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6448 TEST_DrawLevelField(x, y);
6453 static void ToggleLightSwitch(int x, int y)
6455 int element = Feld[x][y];
6457 game.light_time_left =
6458 (element == EL_LIGHT_SWITCH ?
6459 level.time_light * FRAMES_PER_SECOND : 0);
6461 RedrawAllLightSwitchesAndInvisibleElements();
6464 static void ActivateTimegateSwitch(int x, int y)
6468 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6470 SCAN_PLAYFIELD(xx, yy)
6472 int element = Feld[xx][yy];
6474 if (element == EL_TIMEGATE_CLOSED ||
6475 element == EL_TIMEGATE_CLOSING)
6477 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6478 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6482 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6484 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6485 TEST_DrawLevelField(xx, yy);
6492 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6493 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6495 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6499 void Impact(int x, int y)
6501 boolean last_line = (y == lev_fieldy - 1);
6502 boolean object_hit = FALSE;
6503 boolean impact = (last_line || object_hit);
6504 int element = Feld[x][y];
6505 int smashed = EL_STEELWALL;
6507 if (!last_line) /* check if element below was hit */
6509 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6512 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6513 MovDir[x][y + 1] != MV_DOWN ||
6514 MovPos[x][y + 1] <= TILEY / 2));
6516 /* do not smash moving elements that left the smashed field in time */
6517 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6518 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6521 #if USE_QUICKSAND_IMPACT_BUGFIX
6522 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6524 RemoveMovingField(x, y + 1);
6525 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6526 Feld[x][y + 2] = EL_ROCK;
6527 TEST_DrawLevelField(x, y + 2);
6532 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6534 RemoveMovingField(x, y + 1);
6535 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6536 Feld[x][y + 2] = EL_ROCK;
6537 TEST_DrawLevelField(x, y + 2);
6544 smashed = MovingOrBlocked2Element(x, y + 1);
6546 impact = (last_line || object_hit);
6549 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6551 SplashAcid(x, y + 1);
6555 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6556 /* only reset graphic animation if graphic really changes after impact */
6558 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6560 ResetGfxAnimation(x, y);
6561 TEST_DrawLevelField(x, y);
6564 if (impact && CAN_EXPLODE_IMPACT(element))
6569 else if (impact && element == EL_PEARL &&
6570 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6572 ResetGfxAnimation(x, y);
6574 Feld[x][y] = EL_PEARL_BREAKING;
6575 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6578 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6580 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6585 if (impact && element == EL_AMOEBA_DROP)
6587 if (object_hit && IS_PLAYER(x, y + 1))
6588 KillPlayerUnlessEnemyProtected(x, y + 1);
6589 else if (object_hit && smashed == EL_PENGUIN)
6593 Feld[x][y] = EL_AMOEBA_GROWING;
6594 Store[x][y] = EL_AMOEBA_WET;
6596 ResetRandomAnimationValue(x, y);
6601 if (object_hit) /* check which object was hit */
6603 if ((CAN_PASS_MAGIC_WALL(element) &&
6604 (smashed == EL_MAGIC_WALL ||
6605 smashed == EL_BD_MAGIC_WALL)) ||
6606 (CAN_PASS_DC_MAGIC_WALL(element) &&
6607 smashed == EL_DC_MAGIC_WALL))
6610 int activated_magic_wall =
6611 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6612 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6613 EL_DC_MAGIC_WALL_ACTIVE);
6615 /* activate magic wall / mill */
6616 SCAN_PLAYFIELD(xx, yy)
6618 if (Feld[xx][yy] == smashed)
6619 Feld[xx][yy] = activated_magic_wall;
6622 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6623 game.magic_wall_active = TRUE;
6625 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6626 SND_MAGIC_WALL_ACTIVATING :
6627 smashed == EL_BD_MAGIC_WALL ?
6628 SND_BD_MAGIC_WALL_ACTIVATING :
6629 SND_DC_MAGIC_WALL_ACTIVATING));
6632 if (IS_PLAYER(x, y + 1))
6634 if (CAN_SMASH_PLAYER(element))
6636 KillPlayerUnlessEnemyProtected(x, y + 1);
6640 else if (smashed == EL_PENGUIN)
6642 if (CAN_SMASH_PLAYER(element))
6648 else if (element == EL_BD_DIAMOND)
6650 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6656 else if (((element == EL_SP_INFOTRON ||
6657 element == EL_SP_ZONK) &&
6658 (smashed == EL_SP_SNIKSNAK ||
6659 smashed == EL_SP_ELECTRON ||
6660 smashed == EL_SP_DISK_ORANGE)) ||
6661 (element == EL_SP_INFOTRON &&
6662 smashed == EL_SP_DISK_YELLOW))
6667 else if (CAN_SMASH_EVERYTHING(element))
6669 if (IS_CLASSIC_ENEMY(smashed) ||
6670 CAN_EXPLODE_SMASHED(smashed))
6675 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6677 if (smashed == EL_LAMP ||
6678 smashed == EL_LAMP_ACTIVE)
6683 else if (smashed == EL_NUT)
6685 Feld[x][y + 1] = EL_NUT_BREAKING;
6686 PlayLevelSound(x, y, SND_NUT_BREAKING);
6687 RaiseScoreElement(EL_NUT);
6690 else if (smashed == EL_PEARL)
6692 ResetGfxAnimation(x, y);
6694 Feld[x][y + 1] = EL_PEARL_BREAKING;
6695 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6698 else if (smashed == EL_DIAMOND)
6700 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6701 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6704 else if (IS_BELT_SWITCH(smashed))
6706 ToggleBeltSwitch(x, y + 1);
6708 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6709 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6710 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6711 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6713 ToggleSwitchgateSwitch(x, y + 1);
6715 else if (smashed == EL_LIGHT_SWITCH ||
6716 smashed == EL_LIGHT_SWITCH_ACTIVE)
6718 ToggleLightSwitch(x, y + 1);
6723 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6726 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6728 CheckElementChangeBySide(x, y + 1, smashed, element,
6729 CE_SWITCHED, CH_SIDE_TOP);
6730 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6736 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6741 /* play sound of magic wall / mill */
6743 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6744 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6745 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6747 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6748 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6749 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6750 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6751 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6752 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6757 /* play sound of object that hits the ground */
6758 if (last_line || object_hit)
6759 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6762 inline static void TurnRoundExt(int x, int y)
6774 { 0, 0 }, { 0, 0 }, { 0, 0 },
6779 int left, right, back;
6783 { MV_DOWN, MV_UP, MV_RIGHT },
6784 { MV_UP, MV_DOWN, MV_LEFT },
6786 { MV_LEFT, MV_RIGHT, MV_DOWN },
6790 { MV_RIGHT, MV_LEFT, MV_UP }
6793 int element = Feld[x][y];
6794 int move_pattern = element_info[element].move_pattern;
6796 int old_move_dir = MovDir[x][y];
6797 int left_dir = turn[old_move_dir].left;
6798 int right_dir = turn[old_move_dir].right;
6799 int back_dir = turn[old_move_dir].back;
6801 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6802 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6803 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6804 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6806 int left_x = x + left_dx, left_y = y + left_dy;
6807 int right_x = x + right_dx, right_y = y + right_dy;
6808 int move_x = x + move_dx, move_y = y + move_dy;
6812 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6814 TestIfBadThingTouchesOtherBadThing(x, y);
6816 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6817 MovDir[x][y] = right_dir;
6818 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6819 MovDir[x][y] = left_dir;
6821 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6823 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6826 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6828 TestIfBadThingTouchesOtherBadThing(x, y);
6830 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6831 MovDir[x][y] = left_dir;
6832 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6833 MovDir[x][y] = right_dir;
6835 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6837 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6840 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6842 TestIfBadThingTouchesOtherBadThing(x, y);
6844 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6845 MovDir[x][y] = left_dir;
6846 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6847 MovDir[x][y] = right_dir;
6849 if (MovDir[x][y] != old_move_dir)
6852 else if (element == EL_YAMYAM)
6854 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6855 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6857 if (can_turn_left && can_turn_right)
6858 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6859 else if (can_turn_left)
6860 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6861 else if (can_turn_right)
6862 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6864 MovDir[x][y] = back_dir;
6866 MovDelay[x][y] = 16 + 16 * RND(3);
6868 else if (element == EL_DARK_YAMYAM)
6870 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6872 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6875 if (can_turn_left && can_turn_right)
6876 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6877 else if (can_turn_left)
6878 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6879 else if (can_turn_right)
6880 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6882 MovDir[x][y] = back_dir;
6884 MovDelay[x][y] = 16 + 16 * RND(3);
6886 else if (element == EL_PACMAN)
6888 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6889 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6891 if (can_turn_left && can_turn_right)
6892 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6893 else if (can_turn_left)
6894 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6895 else if (can_turn_right)
6896 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6898 MovDir[x][y] = back_dir;
6900 MovDelay[x][y] = 6 + RND(40);
6902 else if (element == EL_PIG)
6904 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6905 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6906 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6907 boolean should_turn_left, should_turn_right, should_move_on;
6909 int rnd = RND(rnd_value);
6911 should_turn_left = (can_turn_left &&
6913 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6914 y + back_dy + left_dy)));
6915 should_turn_right = (can_turn_right &&
6917 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6918 y + back_dy + right_dy)));
6919 should_move_on = (can_move_on &&
6922 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6923 y + move_dy + left_dy) ||
6924 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6925 y + move_dy + right_dy)));
6927 if (should_turn_left || should_turn_right || should_move_on)
6929 if (should_turn_left && should_turn_right && should_move_on)
6930 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6931 rnd < 2 * rnd_value / 3 ? right_dir :
6933 else if (should_turn_left && should_turn_right)
6934 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6935 else if (should_turn_left && should_move_on)
6936 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6937 else if (should_turn_right && should_move_on)
6938 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6939 else if (should_turn_left)
6940 MovDir[x][y] = left_dir;
6941 else if (should_turn_right)
6942 MovDir[x][y] = right_dir;
6943 else if (should_move_on)
6944 MovDir[x][y] = old_move_dir;
6946 else if (can_move_on && rnd > rnd_value / 8)
6947 MovDir[x][y] = old_move_dir;
6948 else if (can_turn_left && can_turn_right)
6949 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6950 else if (can_turn_left && rnd > rnd_value / 8)
6951 MovDir[x][y] = left_dir;
6952 else if (can_turn_right && rnd > rnd_value/8)
6953 MovDir[x][y] = right_dir;
6955 MovDir[x][y] = back_dir;
6957 xx = x + move_xy[MovDir[x][y]].dx;
6958 yy = y + move_xy[MovDir[x][y]].dy;
6960 if (!IN_LEV_FIELD(xx, yy) ||
6961 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6962 MovDir[x][y] = old_move_dir;
6966 else if (element == EL_DRAGON)
6968 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6969 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6970 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6972 int rnd = RND(rnd_value);
6974 if (can_move_on && rnd > rnd_value / 8)
6975 MovDir[x][y] = old_move_dir;
6976 else if (can_turn_left && can_turn_right)
6977 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6978 else if (can_turn_left && rnd > rnd_value / 8)
6979 MovDir[x][y] = left_dir;
6980 else if (can_turn_right && rnd > rnd_value / 8)
6981 MovDir[x][y] = right_dir;
6983 MovDir[x][y] = back_dir;
6985 xx = x + move_xy[MovDir[x][y]].dx;
6986 yy = y + move_xy[MovDir[x][y]].dy;
6988 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6989 MovDir[x][y] = old_move_dir;
6993 else if (element == EL_MOLE)
6995 boolean can_move_on =
6996 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6997 IS_AMOEBOID(Feld[move_x][move_y]) ||
6998 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7001 boolean can_turn_left =
7002 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7003 IS_AMOEBOID(Feld[left_x][left_y])));
7005 boolean can_turn_right =
7006 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7007 IS_AMOEBOID(Feld[right_x][right_y])));
7009 if (can_turn_left && can_turn_right)
7010 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7011 else if (can_turn_left)
7012 MovDir[x][y] = left_dir;
7014 MovDir[x][y] = right_dir;
7017 if (MovDir[x][y] != old_move_dir)
7020 else if (element == EL_BALLOON)
7022 MovDir[x][y] = game.wind_direction;
7025 else if (element == EL_SPRING)
7027 #if USE_NEW_SPRING_BUMPER
7028 if (MovDir[x][y] & MV_HORIZONTAL)
7030 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7031 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7033 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7034 ResetGfxAnimation(move_x, move_y);
7035 TEST_DrawLevelField(move_x, move_y);
7037 MovDir[x][y] = back_dir;
7039 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7040 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7041 MovDir[x][y] = MV_NONE;
7044 if (MovDir[x][y] & MV_HORIZONTAL &&
7045 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7046 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7047 MovDir[x][y] = MV_NONE;
7052 else if (element == EL_ROBOT ||
7053 element == EL_SATELLITE ||
7054 element == EL_PENGUIN ||
7055 element == EL_EMC_ANDROID)
7057 int attr_x = -1, attr_y = -1;
7068 for (i = 0; i < MAX_PLAYERS; i++)
7070 struct PlayerInfo *player = &stored_player[i];
7071 int jx = player->jx, jy = player->jy;
7073 if (!player->active)
7077 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7085 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7086 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7087 game.engine_version < VERSION_IDENT(3,1,0,0)))
7093 if (element == EL_PENGUIN)
7096 static int xy[4][2] =
7104 for (i = 0; i < NUM_DIRECTIONS; i++)
7106 int ex = x + xy[i][0];
7107 int ey = y + xy[i][1];
7109 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7110 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7111 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7112 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7121 MovDir[x][y] = MV_NONE;
7123 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7124 else if (attr_x > x)
7125 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7127 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7128 else if (attr_y > y)
7129 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7131 if (element == EL_ROBOT)
7135 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7136 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7137 Moving2Blocked(x, y, &newx, &newy);
7139 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7140 MovDelay[x][y] = 8 + 8 * !RND(3);
7142 MovDelay[x][y] = 16;
7144 else if (element == EL_PENGUIN)
7150 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7152 boolean first_horiz = RND(2);
7153 int new_move_dir = MovDir[x][y];
7156 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7157 Moving2Blocked(x, y, &newx, &newy);
7159 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7163 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7164 Moving2Blocked(x, y, &newx, &newy);
7166 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7169 MovDir[x][y] = old_move_dir;
7173 else if (element == EL_SATELLITE)
7179 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7181 boolean first_horiz = RND(2);
7182 int new_move_dir = MovDir[x][y];
7185 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7186 Moving2Blocked(x, y, &newx, &newy);
7188 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7192 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7193 Moving2Blocked(x, y, &newx, &newy);
7195 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7198 MovDir[x][y] = old_move_dir;
7202 else if (element == EL_EMC_ANDROID)
7204 static int check_pos[16] =
7206 -1, /* 0 => (invalid) */
7207 7, /* 1 => MV_LEFT */
7208 3, /* 2 => MV_RIGHT */
7209 -1, /* 3 => (invalid) */
7211 0, /* 5 => MV_LEFT | MV_UP */
7212 2, /* 6 => MV_RIGHT | MV_UP */
7213 -1, /* 7 => (invalid) */
7214 5, /* 8 => MV_DOWN */
7215 6, /* 9 => MV_LEFT | MV_DOWN */
7216 4, /* 10 => MV_RIGHT | MV_DOWN */
7217 -1, /* 11 => (invalid) */
7218 -1, /* 12 => (invalid) */
7219 -1, /* 13 => (invalid) */
7220 -1, /* 14 => (invalid) */
7221 -1, /* 15 => (invalid) */
7229 { -1, -1, MV_LEFT | MV_UP },
7231 { +1, -1, MV_RIGHT | MV_UP },
7232 { +1, 0, MV_RIGHT },
7233 { +1, +1, MV_RIGHT | MV_DOWN },
7235 { -1, +1, MV_LEFT | MV_DOWN },
7238 int start_pos, check_order;
7239 boolean can_clone = FALSE;
7242 /* check if there is any free field around current position */
7243 for (i = 0; i < 8; i++)
7245 int newx = x + check_xy[i].dx;
7246 int newy = y + check_xy[i].dy;
7248 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7256 if (can_clone) /* randomly find an element to clone */
7260 start_pos = check_pos[RND(8)];
7261 check_order = (RND(2) ? -1 : +1);
7263 for (i = 0; i < 8; i++)
7265 int pos_raw = start_pos + i * check_order;
7266 int pos = (pos_raw + 8) % 8;
7267 int newx = x + check_xy[pos].dx;
7268 int newy = y + check_xy[pos].dy;
7270 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7272 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7273 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7275 Store[x][y] = Feld[newx][newy];
7284 if (can_clone) /* randomly find a direction to move */
7288 start_pos = check_pos[RND(8)];
7289 check_order = (RND(2) ? -1 : +1);
7291 for (i = 0; i < 8; i++)
7293 int pos_raw = start_pos + i * check_order;
7294 int pos = (pos_raw + 8) % 8;
7295 int newx = x + check_xy[pos].dx;
7296 int newy = y + check_xy[pos].dy;
7297 int new_move_dir = check_xy[pos].dir;
7299 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7301 MovDir[x][y] = new_move_dir;
7302 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7311 if (can_clone) /* cloning and moving successful */
7314 /* cannot clone -- try to move towards player */
7316 start_pos = check_pos[MovDir[x][y] & 0x0f];
7317 check_order = (RND(2) ? -1 : +1);
7319 for (i = 0; i < 3; i++)
7321 /* first check start_pos, then previous/next or (next/previous) pos */
7322 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7323 int pos = (pos_raw + 8) % 8;
7324 int newx = x + check_xy[pos].dx;
7325 int newy = y + check_xy[pos].dy;
7326 int new_move_dir = check_xy[pos].dir;
7328 if (IS_PLAYER(newx, newy))
7331 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7333 MovDir[x][y] = new_move_dir;
7334 MovDelay[x][y] = level.android_move_time * 8 + 1;
7341 else if (move_pattern == MV_TURNING_LEFT ||
7342 move_pattern == MV_TURNING_RIGHT ||
7343 move_pattern == MV_TURNING_LEFT_RIGHT ||
7344 move_pattern == MV_TURNING_RIGHT_LEFT ||
7345 move_pattern == MV_TURNING_RANDOM ||
7346 move_pattern == MV_ALL_DIRECTIONS)
7348 boolean can_turn_left =
7349 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7350 boolean can_turn_right =
7351 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7353 if (element_info[element].move_stepsize == 0) /* "not moving" */
7356 if (move_pattern == MV_TURNING_LEFT)
7357 MovDir[x][y] = left_dir;
7358 else if (move_pattern == MV_TURNING_RIGHT)
7359 MovDir[x][y] = right_dir;
7360 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7361 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7362 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7363 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7364 else if (move_pattern == MV_TURNING_RANDOM)
7365 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7366 can_turn_right && !can_turn_left ? right_dir :
7367 RND(2) ? left_dir : right_dir);
7368 else if (can_turn_left && can_turn_right)
7369 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7370 else if (can_turn_left)
7371 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7372 else if (can_turn_right)
7373 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7375 MovDir[x][y] = back_dir;
7377 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7379 else if (move_pattern == MV_HORIZONTAL ||
7380 move_pattern == MV_VERTICAL)
7382 if (move_pattern & old_move_dir)
7383 MovDir[x][y] = back_dir;
7384 else if (move_pattern == MV_HORIZONTAL)
7385 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7386 else if (move_pattern == MV_VERTICAL)
7387 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7389 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7391 else if (move_pattern & MV_ANY_DIRECTION)
7393 MovDir[x][y] = move_pattern;
7394 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7396 else if (move_pattern & MV_WIND_DIRECTION)
7398 MovDir[x][y] = game.wind_direction;
7399 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7401 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7403 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7404 MovDir[x][y] = left_dir;
7405 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7406 MovDir[x][y] = right_dir;
7408 if (MovDir[x][y] != old_move_dir)
7409 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7411 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7413 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7414 MovDir[x][y] = right_dir;
7415 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416 MovDir[x][y] = left_dir;
7418 if (MovDir[x][y] != old_move_dir)
7419 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7421 else if (move_pattern == MV_TOWARDS_PLAYER ||
7422 move_pattern == MV_AWAY_FROM_PLAYER)
7424 int attr_x = -1, attr_y = -1;
7426 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7437 for (i = 0; i < MAX_PLAYERS; i++)
7439 struct PlayerInfo *player = &stored_player[i];
7440 int jx = player->jx, jy = player->jy;
7442 if (!player->active)
7446 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7454 MovDir[x][y] = MV_NONE;
7456 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7457 else if (attr_x > x)
7458 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7460 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7461 else if (attr_y > y)
7462 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7464 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7466 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7468 boolean first_horiz = RND(2);
7469 int new_move_dir = MovDir[x][y];
7471 if (element_info[element].move_stepsize == 0) /* "not moving" */
7473 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7474 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7480 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7481 Moving2Blocked(x, y, &newx, &newy);
7483 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7487 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7488 Moving2Blocked(x, y, &newx, &newy);
7490 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7493 MovDir[x][y] = old_move_dir;
7496 else if (move_pattern == MV_WHEN_PUSHED ||
7497 move_pattern == MV_WHEN_DROPPED)
7499 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7500 MovDir[x][y] = MV_NONE;
7504 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7506 static int test_xy[7][2] =
7516 static int test_dir[7] =
7526 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7527 int move_preference = -1000000; /* start with very low preference */
7528 int new_move_dir = MV_NONE;
7529 int start_test = RND(4);
7532 for (i = 0; i < NUM_DIRECTIONS; i++)
7534 int move_dir = test_dir[start_test + i];
7535 int move_dir_preference;
7537 xx = x + test_xy[start_test + i][0];
7538 yy = y + test_xy[start_test + i][1];
7540 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7541 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7543 new_move_dir = move_dir;
7548 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7551 move_dir_preference = -1 * RunnerVisit[xx][yy];
7552 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7553 move_dir_preference = PlayerVisit[xx][yy];
7555 if (move_dir_preference > move_preference)
7557 /* prefer field that has not been visited for the longest time */
7558 move_preference = move_dir_preference;
7559 new_move_dir = move_dir;
7561 else if (move_dir_preference == move_preference &&
7562 move_dir == old_move_dir)
7564 /* prefer last direction when all directions are preferred equally */
7565 move_preference = move_dir_preference;
7566 new_move_dir = move_dir;
7570 MovDir[x][y] = new_move_dir;
7571 if (old_move_dir != new_move_dir)
7572 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7576 static void TurnRound(int x, int y)
7578 int direction = MovDir[x][y];
7582 GfxDir[x][y] = MovDir[x][y];
7584 if (direction != MovDir[x][y])
7588 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7590 ResetGfxFrame(x, y, FALSE);
7593 static boolean JustBeingPushed(int x, int y)
7597 for (i = 0; i < MAX_PLAYERS; i++)
7599 struct PlayerInfo *player = &stored_player[i];
7601 if (player->active && player->is_pushing && player->MovPos)
7603 int next_jx = player->jx + (player->jx - player->last_jx);
7604 int next_jy = player->jy + (player->jy - player->last_jy);
7606 if (x == next_jx && y == next_jy)
7614 void StartMoving(int x, int y)
7616 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7617 int element = Feld[x][y];
7622 if (MovDelay[x][y] == 0)
7623 GfxAction[x][y] = ACTION_DEFAULT;
7625 if (CAN_FALL(element) && y < lev_fieldy - 1)
7627 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7628 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7629 if (JustBeingPushed(x, y))
7632 if (element == EL_QUICKSAND_FULL)
7634 if (IS_FREE(x, y + 1))
7636 InitMovingField(x, y, MV_DOWN);
7637 started_moving = TRUE;
7639 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7640 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7641 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7642 Store[x][y] = EL_ROCK;
7644 Store[x][y] = EL_ROCK;
7647 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7649 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7651 if (!MovDelay[x][y])
7653 MovDelay[x][y] = TILEY + 1;
7655 ResetGfxAnimation(x, y);
7656 ResetGfxAnimation(x, y + 1);
7661 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7662 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7669 Feld[x][y] = EL_QUICKSAND_EMPTY;
7670 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7671 Store[x][y + 1] = Store[x][y];
7674 PlayLevelSoundAction(x, y, ACTION_FILLING);
7676 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7678 if (!MovDelay[x][y])
7680 MovDelay[x][y] = TILEY + 1;
7682 ResetGfxAnimation(x, y);
7683 ResetGfxAnimation(x, y + 1);
7688 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7689 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7696 Feld[x][y] = EL_QUICKSAND_EMPTY;
7697 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7698 Store[x][y + 1] = Store[x][y];
7701 PlayLevelSoundAction(x, y, ACTION_FILLING);
7704 else if (element == EL_QUICKSAND_FAST_FULL)
7706 if (IS_FREE(x, y + 1))
7708 InitMovingField(x, y, MV_DOWN);
7709 started_moving = TRUE;
7711 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7712 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7713 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7714 Store[x][y] = EL_ROCK;
7716 Store[x][y] = EL_ROCK;
7719 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7721 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7723 if (!MovDelay[x][y])
7725 MovDelay[x][y] = TILEY + 1;
7727 ResetGfxAnimation(x, y);
7728 ResetGfxAnimation(x, y + 1);
7733 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7734 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7741 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7742 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7743 Store[x][y + 1] = Store[x][y];
7746 PlayLevelSoundAction(x, y, ACTION_FILLING);
7748 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7750 if (!MovDelay[x][y])
7752 MovDelay[x][y] = TILEY + 1;
7754 ResetGfxAnimation(x, y);
7755 ResetGfxAnimation(x, y + 1);
7760 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7761 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7768 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7769 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7770 Store[x][y + 1] = Store[x][y];
7773 PlayLevelSoundAction(x, y, ACTION_FILLING);
7776 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7777 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7779 InitMovingField(x, y, MV_DOWN);
7780 started_moving = TRUE;
7782 Feld[x][y] = EL_QUICKSAND_FILLING;
7783 Store[x][y] = element;
7785 PlayLevelSoundAction(x, y, ACTION_FILLING);
7787 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7788 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7790 InitMovingField(x, y, MV_DOWN);
7791 started_moving = TRUE;
7793 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7794 Store[x][y] = element;
7796 PlayLevelSoundAction(x, y, ACTION_FILLING);
7798 else if (element == EL_MAGIC_WALL_FULL)
7800 if (IS_FREE(x, y + 1))
7802 InitMovingField(x, y, MV_DOWN);
7803 started_moving = TRUE;
7805 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7806 Store[x][y] = EL_CHANGED(Store[x][y]);
7808 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7810 if (!MovDelay[x][y])
7811 MovDelay[x][y] = TILEY/4 + 1;
7820 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7821 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7822 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7826 else if (element == EL_BD_MAGIC_WALL_FULL)
7828 if (IS_FREE(x, y + 1))
7830 InitMovingField(x, y, MV_DOWN);
7831 started_moving = TRUE;
7833 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7834 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7836 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7838 if (!MovDelay[x][y])
7839 MovDelay[x][y] = TILEY/4 + 1;
7848 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7849 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7850 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7854 else if (element == EL_DC_MAGIC_WALL_FULL)
7856 if (IS_FREE(x, y + 1))
7858 InitMovingField(x, y, MV_DOWN);
7859 started_moving = TRUE;
7861 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7862 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7864 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7866 if (!MovDelay[x][y])
7867 MovDelay[x][y] = TILEY/4 + 1;
7876 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7877 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7878 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7882 else if ((CAN_PASS_MAGIC_WALL(element) &&
7883 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7884 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7885 (CAN_PASS_DC_MAGIC_WALL(element) &&
7886 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7889 InitMovingField(x, y, MV_DOWN);
7890 started_moving = TRUE;
7893 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7894 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7895 EL_DC_MAGIC_WALL_FILLING);
7896 Store[x][y] = element;
7898 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7900 SplashAcid(x, y + 1);
7902 InitMovingField(x, y, MV_DOWN);
7903 started_moving = TRUE;
7905 Store[x][y] = EL_ACID;
7908 #if USE_FIX_IMPACT_COLLISION
7909 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7910 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7912 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7913 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7915 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7916 CAN_FALL(element) && WasJustFalling[x][y] &&
7917 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7919 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7920 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7921 (Feld[x][y + 1] == EL_BLOCKED)))
7923 /* this is needed for a special case not covered by calling "Impact()"
7924 from "ContinueMoving()": if an element moves to a tile directly below
7925 another element which was just falling on that tile (which was empty
7926 in the previous frame), the falling element above would just stop
7927 instead of smashing the element below (in previous version, the above
7928 element was just checked for "moving" instead of "falling", resulting
7929 in incorrect smashes caused by horizontal movement of the above
7930 element; also, the case of the player being the element to smash was
7931 simply not covered here... :-/ ) */
7933 CheckCollision[x][y] = 0;
7934 CheckImpact[x][y] = 0;
7938 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7940 if (MovDir[x][y] == MV_NONE)
7942 InitMovingField(x, y, MV_DOWN);
7943 started_moving = TRUE;
7946 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7948 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7949 MovDir[x][y] = MV_DOWN;
7951 InitMovingField(x, y, MV_DOWN);
7952 started_moving = TRUE;
7954 else if (element == EL_AMOEBA_DROP)
7956 Feld[x][y] = EL_AMOEBA_GROWING;
7957 Store[x][y] = EL_AMOEBA_WET;
7959 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7960 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7961 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7962 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7964 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7965 (IS_FREE(x - 1, y + 1) ||
7966 Feld[x - 1][y + 1] == EL_ACID));
7967 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7968 (IS_FREE(x + 1, y + 1) ||
7969 Feld[x + 1][y + 1] == EL_ACID));
7970 boolean can_fall_any = (can_fall_left || can_fall_right);
7971 boolean can_fall_both = (can_fall_left && can_fall_right);
7972 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7974 #if USE_NEW_ALL_SLIPPERY
7975 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7977 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7978 can_fall_right = FALSE;
7979 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7980 can_fall_left = FALSE;
7981 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7982 can_fall_right = FALSE;
7983 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7984 can_fall_left = FALSE;
7986 can_fall_any = (can_fall_left || can_fall_right);
7987 can_fall_both = FALSE;
7990 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7992 if (slippery_type == SLIPPERY_ONLY_LEFT)
7993 can_fall_right = FALSE;
7994 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7995 can_fall_left = FALSE;
7996 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7997 can_fall_right = FALSE;
7998 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7999 can_fall_left = FALSE;
8001 can_fall_any = (can_fall_left || can_fall_right);
8002 can_fall_both = (can_fall_left && can_fall_right);
8006 #if USE_NEW_ALL_SLIPPERY
8008 #if USE_NEW_SP_SLIPPERY
8009 /* !!! better use the same properties as for custom elements here !!! */
8010 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8011 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8013 can_fall_right = FALSE; /* slip down on left side */
8014 can_fall_both = FALSE;
8019 #if USE_NEW_ALL_SLIPPERY
8022 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8023 can_fall_right = FALSE; /* slip down on left side */
8025 can_fall_left = !(can_fall_right = RND(2));
8027 can_fall_both = FALSE;
8032 if (game.emulation == EMU_BOULDERDASH ||
8033 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8034 can_fall_right = FALSE; /* slip down on left side */
8036 can_fall_left = !(can_fall_right = RND(2));
8038 can_fall_both = FALSE;
8044 /* if not determined otherwise, prefer left side for slipping down */
8045 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8046 started_moving = TRUE;
8050 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8052 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8055 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8056 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8057 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8058 int belt_dir = game.belt_dir[belt_nr];
8060 if ((belt_dir == MV_LEFT && left_is_free) ||
8061 (belt_dir == MV_RIGHT && right_is_free))
8063 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8065 InitMovingField(x, y, belt_dir);
8066 started_moving = TRUE;
8068 Pushed[x][y] = TRUE;
8069 Pushed[nextx][y] = TRUE;
8071 GfxAction[x][y] = ACTION_DEFAULT;
8075 MovDir[x][y] = 0; /* if element was moving, stop it */
8080 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8082 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8084 if (CAN_MOVE(element) && !started_moving)
8087 int move_pattern = element_info[element].move_pattern;
8092 if (MovDir[x][y] == MV_NONE)
8094 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8095 x, y, element, element_info[element].token_name);
8096 printf("StartMoving(): This should never happen!\n");
8101 Moving2Blocked(x, y, &newx, &newy);
8103 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8106 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8107 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8109 WasJustMoving[x][y] = 0;
8110 CheckCollision[x][y] = 0;
8112 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8114 if (Feld[x][y] != element) /* element has changed */
8118 if (!MovDelay[x][y]) /* start new movement phase */
8120 /* all objects that can change their move direction after each step
8121 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8123 if (element != EL_YAMYAM &&
8124 element != EL_DARK_YAMYAM &&
8125 element != EL_PACMAN &&
8126 !(move_pattern & MV_ANY_DIRECTION) &&
8127 move_pattern != MV_TURNING_LEFT &&
8128 move_pattern != MV_TURNING_RIGHT &&
8129 move_pattern != MV_TURNING_LEFT_RIGHT &&
8130 move_pattern != MV_TURNING_RIGHT_LEFT &&
8131 move_pattern != MV_TURNING_RANDOM)
8135 if (MovDelay[x][y] && (element == EL_BUG ||
8136 element == EL_SPACESHIP ||
8137 element == EL_SP_SNIKSNAK ||
8138 element == EL_SP_ELECTRON ||
8139 element == EL_MOLE))
8140 TEST_DrawLevelField(x, y);
8144 if (MovDelay[x][y]) /* wait some time before next movement */
8148 if (element == EL_ROBOT ||
8149 element == EL_YAMYAM ||
8150 element == EL_DARK_YAMYAM)
8152 DrawLevelElementAnimationIfNeeded(x, y, element);
8153 PlayLevelSoundAction(x, y, ACTION_WAITING);
8155 else if (element == EL_SP_ELECTRON)
8156 DrawLevelElementAnimationIfNeeded(x, y, element);
8157 else if (element == EL_DRAGON)
8160 int dir = MovDir[x][y];
8161 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8162 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8163 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8164 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8165 dir == MV_UP ? IMG_FLAMES_1_UP :
8166 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8167 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8169 GfxAction[x][y] = ACTION_ATTACKING;
8171 if (IS_PLAYER(x, y))
8172 DrawPlayerField(x, y);
8174 TEST_DrawLevelField(x, y);
8176 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8178 for (i = 1; i <= 3; i++)
8180 int xx = x + i * dx;
8181 int yy = y + i * dy;
8182 int sx = SCREENX(xx);
8183 int sy = SCREENY(yy);
8184 int flame_graphic = graphic + (i - 1);
8186 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8191 int flamed = MovingOrBlocked2Element(xx, yy);
8195 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8197 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8198 RemoveMovingField(xx, yy);
8200 RemoveField(xx, yy);
8202 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8205 RemoveMovingField(xx, yy);
8208 ChangeDelay[xx][yy] = 0;
8210 Feld[xx][yy] = EL_FLAMES;
8212 if (IN_SCR_FIELD(sx, sy))
8214 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8215 DrawGraphic(sx, sy, flame_graphic, frame);
8220 if (Feld[xx][yy] == EL_FLAMES)
8221 Feld[xx][yy] = EL_EMPTY;
8222 TEST_DrawLevelField(xx, yy);
8227 if (MovDelay[x][y]) /* element still has to wait some time */
8229 PlayLevelSoundAction(x, y, ACTION_WAITING);
8235 /* now make next step */
8237 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8239 if (DONT_COLLIDE_WITH(element) &&
8240 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8241 !PLAYER_ENEMY_PROTECTED(newx, newy))
8243 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8248 else if (CAN_MOVE_INTO_ACID(element) &&
8249 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8250 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8251 (MovDir[x][y] == MV_DOWN ||
8252 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8254 SplashAcid(newx, newy);
8255 Store[x][y] = EL_ACID;
8257 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8259 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8260 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8261 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8262 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8265 TEST_DrawLevelField(x, y);
8267 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8268 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8269 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8271 local_player->friends_still_needed--;
8272 if (!local_player->friends_still_needed &&
8273 !local_player->GameOver && AllPlayersGone)
8274 PlayerWins(local_player);
8278 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8280 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8281 TEST_DrawLevelField(newx, newy);
8283 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8285 else if (!IS_FREE(newx, newy))
8287 GfxAction[x][y] = ACTION_WAITING;
8289 if (IS_PLAYER(x, y))
8290 DrawPlayerField(x, y);
8292 TEST_DrawLevelField(x, y);
8297 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8299 if (IS_FOOD_PIG(Feld[newx][newy]))
8301 if (IS_MOVING(newx, newy))
8302 RemoveMovingField(newx, newy);
8305 Feld[newx][newy] = EL_EMPTY;
8306 TEST_DrawLevelField(newx, newy);
8309 PlayLevelSound(x, y, SND_PIG_DIGGING);
8311 else if (!IS_FREE(newx, newy))
8313 if (IS_PLAYER(x, y))
8314 DrawPlayerField(x, y);
8316 TEST_DrawLevelField(x, y);
8321 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8323 if (Store[x][y] != EL_EMPTY)
8325 boolean can_clone = FALSE;
8328 /* check if element to clone is still there */
8329 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8331 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8339 /* cannot clone or target field not free anymore -- do not clone */
8340 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8341 Store[x][y] = EL_EMPTY;
8344 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8346 if (IS_MV_DIAGONAL(MovDir[x][y]))
8348 int diagonal_move_dir = MovDir[x][y];
8349 int stored = Store[x][y];
8350 int change_delay = 8;
8353 /* android is moving diagonally */
8355 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8357 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8358 GfxElement[x][y] = EL_EMC_ANDROID;
8359 GfxAction[x][y] = ACTION_SHRINKING;
8360 GfxDir[x][y] = diagonal_move_dir;
8361 ChangeDelay[x][y] = change_delay;
8363 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8366 DrawLevelGraphicAnimation(x, y, graphic);
8367 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8369 if (Feld[newx][newy] == EL_ACID)
8371 SplashAcid(newx, newy);
8376 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8378 Store[newx][newy] = EL_EMC_ANDROID;
8379 GfxElement[newx][newy] = EL_EMC_ANDROID;
8380 GfxAction[newx][newy] = ACTION_GROWING;
8381 GfxDir[newx][newy] = diagonal_move_dir;
8382 ChangeDelay[newx][newy] = change_delay;
8384 graphic = el_act_dir2img(GfxElement[newx][newy],
8385 GfxAction[newx][newy], GfxDir[newx][newy]);
8387 DrawLevelGraphicAnimation(newx, newy, graphic);
8388 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8394 Feld[newx][newy] = EL_EMPTY;
8395 TEST_DrawLevelField(newx, newy);
8397 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8400 else if (!IS_FREE(newx, newy))
8403 if (IS_PLAYER(x, y))
8404 DrawPlayerField(x, y);
8406 TEST_DrawLevelField(x, y);
8412 else if (IS_CUSTOM_ELEMENT(element) &&
8413 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8416 if (!DigFieldByCE(newx, newy, element))
8419 int new_element = Feld[newx][newy];
8421 if (!IS_FREE(newx, newy))
8423 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8424 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8427 /* no element can dig solid indestructible elements */
8428 if (IS_INDESTRUCTIBLE(new_element) &&
8429 !IS_DIGGABLE(new_element) &&
8430 !IS_COLLECTIBLE(new_element))
8433 if (AmoebaNr[newx][newy] &&
8434 (new_element == EL_AMOEBA_FULL ||
8435 new_element == EL_BD_AMOEBA ||
8436 new_element == EL_AMOEBA_GROWING))
8438 AmoebaCnt[AmoebaNr[newx][newy]]--;
8439 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8442 if (IS_MOVING(newx, newy))
8443 RemoveMovingField(newx, newy);
8446 RemoveField(newx, newy);
8447 TEST_DrawLevelField(newx, newy);
8450 /* if digged element was about to explode, prevent the explosion */
8451 ExplodeField[newx][newy] = EX_TYPE_NONE;
8453 PlayLevelSoundAction(x, y, action);
8456 Store[newx][newy] = EL_EMPTY;
8459 /* this makes it possible to leave the removed element again */
8460 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8461 Store[newx][newy] = new_element;
8463 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8465 int move_leave_element = element_info[element].move_leave_element;
8467 /* this makes it possible to leave the removed element again */
8468 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8469 new_element : move_leave_element);
8475 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8477 RunnerVisit[x][y] = FrameCounter;
8478 PlayerVisit[x][y] /= 8; /* expire player visit path */
8481 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8483 if (!IS_FREE(newx, newy))
8485 if (IS_PLAYER(x, y))
8486 DrawPlayerField(x, y);
8488 TEST_DrawLevelField(x, y);
8494 boolean wanna_flame = !RND(10);
8495 int dx = newx - x, dy = newy - y;
8496 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8497 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8498 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8499 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8500 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8501 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8504 IS_CLASSIC_ENEMY(element1) ||
8505 IS_CLASSIC_ENEMY(element2)) &&
8506 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8507 element1 != EL_FLAMES && element2 != EL_FLAMES)
8509 ResetGfxAnimation(x, y);
8510 GfxAction[x][y] = ACTION_ATTACKING;
8512 if (IS_PLAYER(x, y))
8513 DrawPlayerField(x, y);
8515 TEST_DrawLevelField(x, y);
8517 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8519 MovDelay[x][y] = 50;
8523 RemoveField(newx, newy);
8525 Feld[newx][newy] = EL_FLAMES;
8526 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8529 RemoveField(newx1, newy1);
8531 Feld[newx1][newy1] = EL_FLAMES;
8533 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8536 RemoveField(newx2, newy2);
8538 Feld[newx2][newy2] = EL_FLAMES;
8545 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8546 Feld[newx][newy] == EL_DIAMOND)
8548 if (IS_MOVING(newx, newy))
8549 RemoveMovingField(newx, newy);
8552 Feld[newx][newy] = EL_EMPTY;
8553 TEST_DrawLevelField(newx, newy);
8556 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8558 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8559 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8561 if (AmoebaNr[newx][newy])
8563 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8564 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8565 Feld[newx][newy] == EL_BD_AMOEBA)
8566 AmoebaCnt[AmoebaNr[newx][newy]]--;
8571 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8573 RemoveMovingField(newx, newy);
8576 if (IS_MOVING(newx, newy))
8578 RemoveMovingField(newx, newy);
8583 Feld[newx][newy] = EL_EMPTY;
8584 TEST_DrawLevelField(newx, newy);
8587 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8589 else if ((element == EL_PACMAN || element == EL_MOLE)
8590 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8592 if (AmoebaNr[newx][newy])
8594 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8595 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8596 Feld[newx][newy] == EL_BD_AMOEBA)
8597 AmoebaCnt[AmoebaNr[newx][newy]]--;
8600 if (element == EL_MOLE)
8602 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8603 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8605 ResetGfxAnimation(x, y);
8606 GfxAction[x][y] = ACTION_DIGGING;
8607 TEST_DrawLevelField(x, y);
8609 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8611 return; /* wait for shrinking amoeba */
8613 else /* element == EL_PACMAN */
8615 Feld[newx][newy] = EL_EMPTY;
8616 TEST_DrawLevelField(newx, newy);
8617 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8620 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8621 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8622 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8624 /* wait for shrinking amoeba to completely disappear */
8627 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8629 /* object was running against a wall */
8634 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8635 if (move_pattern & MV_ANY_DIRECTION &&
8636 move_pattern == MovDir[x][y])
8638 int blocking_element =
8639 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8641 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8644 element = Feld[x][y]; /* element might have changed */
8648 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8649 DrawLevelElementAnimation(x, y, element);
8651 if (DONT_TOUCH(element))
8652 TestIfBadThingTouchesPlayer(x, y);
8657 InitMovingField(x, y, MovDir[x][y]);
8659 PlayLevelSoundAction(x, y, ACTION_MOVING);
8663 ContinueMoving(x, y);
8666 void ContinueMoving(int x, int y)
8668 int element = Feld[x][y];
8669 struct ElementInfo *ei = &element_info[element];
8670 int direction = MovDir[x][y];
8671 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8672 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8673 int newx = x + dx, newy = y + dy;
8674 int stored = Store[x][y];
8675 int stored_new = Store[newx][newy];
8676 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8677 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8678 boolean last_line = (newy == lev_fieldy - 1);
8680 MovPos[x][y] += getElementMoveStepsize(x, y);
8682 if (pushed_by_player) /* special case: moving object pushed by player */
8683 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8685 if (ABS(MovPos[x][y]) < TILEX)
8688 int ee = Feld[x][y];
8689 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8690 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8692 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8693 x, y, ABS(MovPos[x][y]),
8695 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8698 TEST_DrawLevelField(x, y);
8700 return; /* element is still moving */
8703 /* element reached destination field */
8705 Feld[x][y] = EL_EMPTY;
8706 Feld[newx][newy] = element;
8707 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8709 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8711 element = Feld[newx][newy] = EL_ACID;
8713 else if (element == EL_MOLE)
8715 Feld[x][y] = EL_SAND;
8717 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8719 else if (element == EL_QUICKSAND_FILLING)
8721 element = Feld[newx][newy] = get_next_element(element);
8722 Store[newx][newy] = Store[x][y];
8724 else if (element == EL_QUICKSAND_EMPTYING)
8726 Feld[x][y] = get_next_element(element);
8727 element = Feld[newx][newy] = Store[x][y];
8729 else if (element == EL_QUICKSAND_FAST_FILLING)
8731 element = Feld[newx][newy] = get_next_element(element);
8732 Store[newx][newy] = Store[x][y];
8734 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8736 Feld[x][y] = get_next_element(element);
8737 element = Feld[newx][newy] = Store[x][y];
8739 else if (element == EL_MAGIC_WALL_FILLING)
8741 element = Feld[newx][newy] = get_next_element(element);
8742 if (!game.magic_wall_active)
8743 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8744 Store[newx][newy] = Store[x][y];
8746 else if (element == EL_MAGIC_WALL_EMPTYING)
8748 Feld[x][y] = get_next_element(element);
8749 if (!game.magic_wall_active)
8750 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8751 element = Feld[newx][newy] = Store[x][y];
8753 #if USE_NEW_CUSTOM_VALUE
8754 InitField(newx, newy, FALSE);
8757 else if (element == EL_BD_MAGIC_WALL_FILLING)
8759 element = Feld[newx][newy] = get_next_element(element);
8760 if (!game.magic_wall_active)
8761 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8762 Store[newx][newy] = Store[x][y];
8764 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8766 Feld[x][y] = get_next_element(element);
8767 if (!game.magic_wall_active)
8768 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8769 element = Feld[newx][newy] = Store[x][y];
8771 #if USE_NEW_CUSTOM_VALUE
8772 InitField(newx, newy, FALSE);
8775 else if (element == EL_DC_MAGIC_WALL_FILLING)
8777 element = Feld[newx][newy] = get_next_element(element);
8778 if (!game.magic_wall_active)
8779 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8780 Store[newx][newy] = Store[x][y];
8782 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8784 Feld[x][y] = get_next_element(element);
8785 if (!game.magic_wall_active)
8786 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8787 element = Feld[newx][newy] = Store[x][y];
8789 #if USE_NEW_CUSTOM_VALUE
8790 InitField(newx, newy, FALSE);
8793 else if (element == EL_AMOEBA_DROPPING)
8795 Feld[x][y] = get_next_element(element);
8796 element = Feld[newx][newy] = Store[x][y];
8798 else if (element == EL_SOKOBAN_OBJECT)
8801 Feld[x][y] = Back[x][y];
8803 if (Back[newx][newy])
8804 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8806 Back[x][y] = Back[newx][newy] = 0;
8809 Store[x][y] = EL_EMPTY;
8814 MovDelay[newx][newy] = 0;
8816 if (CAN_CHANGE_OR_HAS_ACTION(element))
8818 /* copy element change control values to new field */
8819 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8820 ChangePage[newx][newy] = ChangePage[x][y];
8821 ChangeCount[newx][newy] = ChangeCount[x][y];
8822 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8825 #if USE_NEW_CUSTOM_VALUE
8826 CustomValue[newx][newy] = CustomValue[x][y];
8829 ChangeDelay[x][y] = 0;
8830 ChangePage[x][y] = -1;
8831 ChangeCount[x][y] = 0;
8832 ChangeEvent[x][y] = -1;
8834 #if USE_NEW_CUSTOM_VALUE
8835 CustomValue[x][y] = 0;
8838 /* copy animation control values to new field */
8839 GfxFrame[newx][newy] = GfxFrame[x][y];
8840 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8841 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8842 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8844 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8846 /* some elements can leave other elements behind after moving */
8848 if (ei->move_leave_element != EL_EMPTY &&
8849 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8850 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8852 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8853 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8854 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8857 int move_leave_element = ei->move_leave_element;
8861 /* this makes it possible to leave the removed element again */
8862 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8863 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8865 /* this makes it possible to leave the removed element again */
8866 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8867 move_leave_element = stored;
8870 /* this makes it possible to leave the removed element again */
8871 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8872 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8873 move_leave_element = stored;
8876 Feld[x][y] = move_leave_element;
8878 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8879 MovDir[x][y] = direction;
8881 InitField(x, y, FALSE);
8883 if (GFX_CRUMBLED(Feld[x][y]))
8884 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8886 if (ELEM_IS_PLAYER(move_leave_element))
8887 RelocatePlayer(x, y, move_leave_element);
8890 /* do this after checking for left-behind element */
8891 ResetGfxAnimation(x, y); /* reset animation values for old field */
8893 if (!CAN_MOVE(element) ||
8894 (CAN_FALL(element) && direction == MV_DOWN &&
8895 (element == EL_SPRING ||
8896 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8897 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8898 GfxDir[x][y] = MovDir[newx][newy] = 0;
8900 TEST_DrawLevelField(x, y);
8901 TEST_DrawLevelField(newx, newy);
8903 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8905 /* prevent pushed element from moving on in pushed direction */
8906 if (pushed_by_player && CAN_MOVE(element) &&
8907 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8908 !(element_info[element].move_pattern & direction))
8909 TurnRound(newx, newy);
8911 /* prevent elements on conveyor belt from moving on in last direction */
8912 if (pushed_by_conveyor && CAN_FALL(element) &&
8913 direction & MV_HORIZONTAL)
8914 MovDir[newx][newy] = 0;
8916 if (!pushed_by_player)
8918 int nextx = newx + dx, nexty = newy + dy;
8919 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8921 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8923 if (CAN_FALL(element) && direction == MV_DOWN)
8924 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8926 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8927 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8929 #if USE_FIX_IMPACT_COLLISION
8930 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8931 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8935 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8937 TestIfBadThingTouchesPlayer(newx, newy);
8938 TestIfBadThingTouchesFriend(newx, newy);
8940 if (!IS_CUSTOM_ELEMENT(element))
8941 TestIfBadThingTouchesOtherBadThing(newx, newy);
8943 else if (element == EL_PENGUIN)
8944 TestIfFriendTouchesBadThing(newx, newy);
8946 /* give the player one last chance (one more frame) to move away */
8947 if (CAN_FALL(element) && direction == MV_DOWN &&
8948 (last_line || (!IS_FREE(x, newy + 1) &&
8949 (!IS_PLAYER(x, newy + 1) ||
8950 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8953 if (pushed_by_player && !game.use_change_when_pushing_bug)
8955 int push_side = MV_DIR_OPPOSITE(direction);
8956 struct PlayerInfo *player = PLAYERINFO(x, y);
8958 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8959 player->index_bit, push_side);
8960 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8961 player->index_bit, push_side);
8964 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8965 MovDelay[newx][newy] = 1;
8967 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8969 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8972 if (ChangePage[newx][newy] != -1) /* delayed change */
8974 int page = ChangePage[newx][newy];
8975 struct ElementChangeInfo *change = &ei->change_page[page];
8977 ChangePage[newx][newy] = -1;
8979 if (change->can_change)
8981 if (ChangeElement(newx, newy, element, page))
8983 if (change->post_change_function)
8984 change->post_change_function(newx, newy);
8988 if (change->has_action)
8989 ExecuteCustomElementAction(newx, newy, element, page);
8993 TestIfElementHitsCustomElement(newx, newy, direction);
8994 TestIfPlayerTouchesCustomElement(newx, newy);
8995 TestIfElementTouchesCustomElement(newx, newy);
8997 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8998 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8999 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9000 MV_DIR_OPPOSITE(direction));
9003 int AmoebeNachbarNr(int ax, int ay)
9006 int element = Feld[ax][ay];
9008 static int xy[4][2] =
9016 for (i = 0; i < NUM_DIRECTIONS; i++)
9018 int x = ax + xy[i][0];
9019 int y = ay + xy[i][1];
9021 if (!IN_LEV_FIELD(x, y))
9024 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9025 group_nr = AmoebaNr[x][y];
9031 void AmoebenVereinigen(int ax, int ay)
9033 int i, x, y, xx, yy;
9034 int new_group_nr = AmoebaNr[ax][ay];
9035 static int xy[4][2] =
9043 if (new_group_nr == 0)
9046 for (i = 0; i < NUM_DIRECTIONS; i++)
9051 if (!IN_LEV_FIELD(x, y))
9054 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9055 Feld[x][y] == EL_BD_AMOEBA ||
9056 Feld[x][y] == EL_AMOEBA_DEAD) &&
9057 AmoebaNr[x][y] != new_group_nr)
9059 int old_group_nr = AmoebaNr[x][y];
9061 if (old_group_nr == 0)
9064 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9065 AmoebaCnt[old_group_nr] = 0;
9066 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9067 AmoebaCnt2[old_group_nr] = 0;
9069 SCAN_PLAYFIELD(xx, yy)
9071 if (AmoebaNr[xx][yy] == old_group_nr)
9072 AmoebaNr[xx][yy] = new_group_nr;
9078 void AmoebeUmwandeln(int ax, int ay)
9082 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9084 int group_nr = AmoebaNr[ax][ay];
9089 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9090 printf("AmoebeUmwandeln(): This should never happen!\n");
9095 SCAN_PLAYFIELD(x, y)
9097 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9100 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9104 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9105 SND_AMOEBA_TURNING_TO_GEM :
9106 SND_AMOEBA_TURNING_TO_ROCK));
9111 static int xy[4][2] =
9119 for (i = 0; i < NUM_DIRECTIONS; i++)
9124 if (!IN_LEV_FIELD(x, y))
9127 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9129 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9130 SND_AMOEBA_TURNING_TO_GEM :
9131 SND_AMOEBA_TURNING_TO_ROCK));
9138 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9141 int group_nr = AmoebaNr[ax][ay];
9142 boolean done = FALSE;
9147 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9148 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9153 SCAN_PLAYFIELD(x, y)
9155 if (AmoebaNr[x][y] == group_nr &&
9156 (Feld[x][y] == EL_AMOEBA_DEAD ||
9157 Feld[x][y] == EL_BD_AMOEBA ||
9158 Feld[x][y] == EL_AMOEBA_GROWING))
9161 Feld[x][y] = new_element;
9162 InitField(x, y, FALSE);
9163 TEST_DrawLevelField(x, y);
9169 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9170 SND_BD_AMOEBA_TURNING_TO_ROCK :
9171 SND_BD_AMOEBA_TURNING_TO_GEM));
9174 void AmoebeWaechst(int x, int y)
9176 static unsigned long sound_delay = 0;
9177 static unsigned long sound_delay_value = 0;
9179 if (!MovDelay[x][y]) /* start new growing cycle */
9183 if (DelayReached(&sound_delay, sound_delay_value))
9185 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9186 sound_delay_value = 30;
9190 if (MovDelay[x][y]) /* wait some time before growing bigger */
9193 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9195 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9196 6 - MovDelay[x][y]);
9198 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9201 if (!MovDelay[x][y])
9203 Feld[x][y] = Store[x][y];
9205 TEST_DrawLevelField(x, y);
9210 void AmoebaDisappearing(int x, int y)
9212 static unsigned long sound_delay = 0;
9213 static unsigned long sound_delay_value = 0;
9215 if (!MovDelay[x][y]) /* start new shrinking cycle */
9219 if (DelayReached(&sound_delay, sound_delay_value))
9220 sound_delay_value = 30;
9223 if (MovDelay[x][y]) /* wait some time before shrinking */
9226 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9228 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9229 6 - MovDelay[x][y]);
9231 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9234 if (!MovDelay[x][y])
9236 Feld[x][y] = EL_EMPTY;
9237 TEST_DrawLevelField(x, y);
9239 /* don't let mole enter this field in this cycle;
9240 (give priority to objects falling to this field from above) */
9246 void AmoebeAbleger(int ax, int ay)
9249 int element = Feld[ax][ay];
9250 int graphic = el2img(element);
9251 int newax = ax, neway = ay;
9252 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9253 static int xy[4][2] =
9261 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9263 Feld[ax][ay] = EL_AMOEBA_DEAD;
9264 TEST_DrawLevelField(ax, ay);
9268 if (IS_ANIMATED(graphic))
9269 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9271 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9272 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9274 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9277 if (MovDelay[ax][ay])
9281 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9284 int x = ax + xy[start][0];
9285 int y = ay + xy[start][1];
9287 if (!IN_LEV_FIELD(x, y))
9290 if (IS_FREE(x, y) ||
9291 CAN_GROW_INTO(Feld[x][y]) ||
9292 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9293 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9299 if (newax == ax && neway == ay)
9302 else /* normal or "filled" (BD style) amoeba */
9305 boolean waiting_for_player = FALSE;
9307 for (i = 0; i < NUM_DIRECTIONS; i++)
9309 int j = (start + i) % 4;
9310 int x = ax + xy[j][0];
9311 int y = ay + xy[j][1];
9313 if (!IN_LEV_FIELD(x, y))
9316 if (IS_FREE(x, y) ||
9317 CAN_GROW_INTO(Feld[x][y]) ||
9318 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9319 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9325 else if (IS_PLAYER(x, y))
9326 waiting_for_player = TRUE;
9329 if (newax == ax && neway == ay) /* amoeba cannot grow */
9331 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9333 Feld[ax][ay] = EL_AMOEBA_DEAD;
9334 TEST_DrawLevelField(ax, ay);
9335 AmoebaCnt[AmoebaNr[ax][ay]]--;
9337 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9339 if (element == EL_AMOEBA_FULL)
9340 AmoebeUmwandeln(ax, ay);
9341 else if (element == EL_BD_AMOEBA)
9342 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9347 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9349 /* amoeba gets larger by growing in some direction */
9351 int new_group_nr = AmoebaNr[ax][ay];
9354 if (new_group_nr == 0)
9356 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9357 printf("AmoebeAbleger(): This should never happen!\n");
9362 AmoebaNr[newax][neway] = new_group_nr;
9363 AmoebaCnt[new_group_nr]++;
9364 AmoebaCnt2[new_group_nr]++;
9366 /* if amoeba touches other amoeba(s) after growing, unify them */
9367 AmoebenVereinigen(newax, neway);
9369 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9371 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9377 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9378 (neway == lev_fieldy - 1 && newax != ax))
9380 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9381 Store[newax][neway] = element;
9383 else if (neway == ay || element == EL_EMC_DRIPPER)
9385 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9387 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9391 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9392 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9393 Store[ax][ay] = EL_AMOEBA_DROP;
9394 ContinueMoving(ax, ay);
9398 TEST_DrawLevelField(newax, neway);
9401 void Life(int ax, int ay)
9405 int element = Feld[ax][ay];
9406 int graphic = el2img(element);
9407 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9409 boolean changed = FALSE;
9411 if (IS_ANIMATED(graphic))
9412 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9417 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9418 MovDelay[ax][ay] = life_time;
9420 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9423 if (MovDelay[ax][ay])
9427 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9429 int xx = ax+x1, yy = ay+y1;
9432 if (!IN_LEV_FIELD(xx, yy))
9435 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9437 int x = xx+x2, y = yy+y2;
9439 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9442 if (((Feld[x][y] == element ||
9443 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9445 (IS_FREE(x, y) && Stop[x][y]))
9449 if (xx == ax && yy == ay) /* field in the middle */
9451 if (nachbarn < life_parameter[0] ||
9452 nachbarn > life_parameter[1])
9454 Feld[xx][yy] = EL_EMPTY;
9456 TEST_DrawLevelField(xx, yy);
9457 Stop[xx][yy] = TRUE;
9461 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9462 { /* free border field */
9463 if (nachbarn >= life_parameter[2] &&
9464 nachbarn <= life_parameter[3])
9466 Feld[xx][yy] = element;
9467 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9469 TEST_DrawLevelField(xx, yy);
9470 Stop[xx][yy] = TRUE;
9477 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9478 SND_GAME_OF_LIFE_GROWING);
9481 static void InitRobotWheel(int x, int y)
9483 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9486 static void RunRobotWheel(int x, int y)
9488 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9491 static void StopRobotWheel(int x, int y)
9493 if (ZX == x && ZY == y)
9497 game.robot_wheel_active = FALSE;
9501 static void InitTimegateWheel(int x, int y)
9503 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9506 static void RunTimegateWheel(int x, int y)
9508 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9511 static void InitMagicBallDelay(int x, int y)
9514 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9516 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9520 static void ActivateMagicBall(int bx, int by)
9524 if (level.ball_random)
9526 int pos_border = RND(8); /* select one of the eight border elements */
9527 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9528 int xx = pos_content % 3;
9529 int yy = pos_content / 3;
9534 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9535 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9539 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9541 int xx = x - bx + 1;
9542 int yy = y - by + 1;
9544 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9545 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9549 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9552 void CheckExit(int x, int y)
9554 if (local_player->gems_still_needed > 0 ||
9555 local_player->sokobanfields_still_needed > 0 ||
9556 local_player->lights_still_needed > 0)
9558 int element = Feld[x][y];
9559 int graphic = el2img(element);
9561 if (IS_ANIMATED(graphic))
9562 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9567 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9570 Feld[x][y] = EL_EXIT_OPENING;
9572 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9575 void CheckExitEM(int x, int y)
9577 if (local_player->gems_still_needed > 0 ||
9578 local_player->sokobanfields_still_needed > 0 ||
9579 local_player->lights_still_needed > 0)
9581 int element = Feld[x][y];
9582 int graphic = el2img(element);
9584 if (IS_ANIMATED(graphic))
9585 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9590 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9593 Feld[x][y] = EL_EM_EXIT_OPENING;
9595 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9598 void CheckExitSteel(int x, int y)
9600 if (local_player->gems_still_needed > 0 ||
9601 local_player->sokobanfields_still_needed > 0 ||
9602 local_player->lights_still_needed > 0)
9604 int element = Feld[x][y];
9605 int graphic = el2img(element);
9607 if (IS_ANIMATED(graphic))
9608 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9613 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9616 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9618 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9621 void CheckExitSteelEM(int x, int y)
9623 if (local_player->gems_still_needed > 0 ||
9624 local_player->sokobanfields_still_needed > 0 ||
9625 local_player->lights_still_needed > 0)
9627 int element = Feld[x][y];
9628 int graphic = el2img(element);
9630 if (IS_ANIMATED(graphic))
9631 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9636 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9639 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9641 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9644 void CheckExitSP(int x, int y)
9646 if (local_player->gems_still_needed > 0)
9648 int element = Feld[x][y];
9649 int graphic = el2img(element);
9651 if (IS_ANIMATED(graphic))
9652 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9657 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9660 Feld[x][y] = EL_SP_EXIT_OPENING;
9662 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9665 static void CloseAllOpenTimegates()
9669 SCAN_PLAYFIELD(x, y)
9671 int element = Feld[x][y];
9673 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9675 Feld[x][y] = EL_TIMEGATE_CLOSING;
9677 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9682 void DrawTwinkleOnField(int x, int y)
9684 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9687 if (Feld[x][y] == EL_BD_DIAMOND)
9690 if (MovDelay[x][y] == 0) /* next animation frame */
9691 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9693 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9697 DrawLevelElementAnimation(x, y, Feld[x][y]);
9699 if (MovDelay[x][y] != 0)
9701 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9702 10 - MovDelay[x][y]);
9704 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9709 void MauerWaechst(int x, int y)
9713 if (!MovDelay[x][y]) /* next animation frame */
9714 MovDelay[x][y] = 3 * delay;
9716 if (MovDelay[x][y]) /* wait some time before next frame */
9720 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9722 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9723 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9725 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9728 if (!MovDelay[x][y])
9730 if (MovDir[x][y] == MV_LEFT)
9732 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9733 TEST_DrawLevelField(x - 1, y);
9735 else if (MovDir[x][y] == MV_RIGHT)
9737 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9738 TEST_DrawLevelField(x + 1, y);
9740 else if (MovDir[x][y] == MV_UP)
9742 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9743 TEST_DrawLevelField(x, y - 1);
9747 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9748 TEST_DrawLevelField(x, y + 1);
9751 Feld[x][y] = Store[x][y];
9753 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9754 TEST_DrawLevelField(x, y);
9759 void MauerAbleger(int ax, int ay)
9761 int element = Feld[ax][ay];
9762 int graphic = el2img(element);
9763 boolean oben_frei = FALSE, unten_frei = FALSE;
9764 boolean links_frei = FALSE, rechts_frei = FALSE;
9765 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9766 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9767 boolean new_wall = FALSE;
9769 if (IS_ANIMATED(graphic))
9770 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9772 if (!MovDelay[ax][ay]) /* start building new wall */
9773 MovDelay[ax][ay] = 6;
9775 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9778 if (MovDelay[ax][ay])
9782 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9784 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9786 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9788 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9791 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9792 element == EL_EXPANDABLE_WALL_ANY)
9796 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9797 Store[ax][ay-1] = element;
9798 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9799 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9800 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9801 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9806 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9807 Store[ax][ay+1] = element;
9808 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9809 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9810 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9811 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9816 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9817 element == EL_EXPANDABLE_WALL_ANY ||
9818 element == EL_EXPANDABLE_WALL ||
9819 element == EL_BD_EXPANDABLE_WALL)
9823 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9824 Store[ax-1][ay] = element;
9825 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9826 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9827 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9828 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9834 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9835 Store[ax+1][ay] = element;
9836 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9837 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9838 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9839 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9844 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9845 TEST_DrawLevelField(ax, ay);
9847 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9849 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9850 unten_massiv = TRUE;
9851 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9852 links_massiv = TRUE;
9853 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9854 rechts_massiv = TRUE;
9856 if (((oben_massiv && unten_massiv) ||
9857 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9858 element == EL_EXPANDABLE_WALL) &&
9859 ((links_massiv && rechts_massiv) ||
9860 element == EL_EXPANDABLE_WALL_VERTICAL))
9861 Feld[ax][ay] = EL_WALL;
9864 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9867 void MauerAblegerStahl(int ax, int ay)
9869 int element = Feld[ax][ay];
9870 int graphic = el2img(element);
9871 boolean oben_frei = FALSE, unten_frei = FALSE;
9872 boolean links_frei = FALSE, rechts_frei = FALSE;
9873 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9874 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9875 boolean new_wall = FALSE;
9877 if (IS_ANIMATED(graphic))
9878 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9880 if (!MovDelay[ax][ay]) /* start building new wall */
9881 MovDelay[ax][ay] = 6;
9883 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9886 if (MovDelay[ax][ay])
9890 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9892 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9894 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9896 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9899 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9900 element == EL_EXPANDABLE_STEELWALL_ANY)
9904 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9905 Store[ax][ay-1] = element;
9906 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9907 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9908 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9909 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9914 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9915 Store[ax][ay+1] = element;
9916 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9917 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9918 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9919 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9924 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9925 element == EL_EXPANDABLE_STEELWALL_ANY)
9929 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9930 Store[ax-1][ay] = element;
9931 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9932 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9933 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9934 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9940 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9941 Store[ax+1][ay] = element;
9942 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9943 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9944 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9945 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9950 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9952 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9953 unten_massiv = TRUE;
9954 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9955 links_massiv = TRUE;
9956 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9957 rechts_massiv = TRUE;
9959 if (((oben_massiv && unten_massiv) ||
9960 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9961 ((links_massiv && rechts_massiv) ||
9962 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9963 Feld[ax][ay] = EL_STEELWALL;
9966 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9969 void CheckForDragon(int x, int y)
9972 boolean dragon_found = FALSE;
9973 static int xy[4][2] =
9981 for (i = 0; i < NUM_DIRECTIONS; i++)
9983 for (j = 0; j < 4; j++)
9985 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9987 if (IN_LEV_FIELD(xx, yy) &&
9988 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9990 if (Feld[xx][yy] == EL_DRAGON)
9991 dragon_found = TRUE;
10000 for (i = 0; i < NUM_DIRECTIONS; i++)
10002 for (j = 0; j < 3; j++)
10004 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10006 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10008 Feld[xx][yy] = EL_EMPTY;
10009 TEST_DrawLevelField(xx, yy);
10018 static void InitBuggyBase(int x, int y)
10020 int element = Feld[x][y];
10021 int activating_delay = FRAMES_PER_SECOND / 4;
10023 ChangeDelay[x][y] =
10024 (element == EL_SP_BUGGY_BASE ?
10025 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10026 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10028 element == EL_SP_BUGGY_BASE_ACTIVE ?
10029 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10032 static void WarnBuggyBase(int x, int y)
10035 static int xy[4][2] =
10043 for (i = 0; i < NUM_DIRECTIONS; i++)
10045 int xx = x + xy[i][0];
10046 int yy = y + xy[i][1];
10048 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10050 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10057 static void InitTrap(int x, int y)
10059 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10062 static void ActivateTrap(int x, int y)
10064 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10067 static void ChangeActiveTrap(int x, int y)
10069 int graphic = IMG_TRAP_ACTIVE;
10071 /* if new animation frame was drawn, correct crumbled sand border */
10072 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10073 TEST_DrawLevelFieldCrumbledSand(x, y);
10076 static int getSpecialActionElement(int element, int number, int base_element)
10078 return (element != EL_EMPTY ? element :
10079 number != -1 ? base_element + number - 1 :
10083 static int getModifiedActionNumber(int value_old, int operator, int operand,
10084 int value_min, int value_max)
10086 int value_new = (operator == CA_MODE_SET ? operand :
10087 operator == CA_MODE_ADD ? value_old + operand :
10088 operator == CA_MODE_SUBTRACT ? value_old - operand :
10089 operator == CA_MODE_MULTIPLY ? value_old * operand :
10090 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10091 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10094 return (value_new < value_min ? value_min :
10095 value_new > value_max ? value_max :
10099 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10101 struct ElementInfo *ei = &element_info[element];
10102 struct ElementChangeInfo *change = &ei->change_page[page];
10103 int target_element = change->target_element;
10104 int action_type = change->action_type;
10105 int action_mode = change->action_mode;
10106 int action_arg = change->action_arg;
10109 if (!change->has_action)
10112 /* ---------- determine action paramater values -------------------------- */
10114 int level_time_value =
10115 (level.time > 0 ? TimeLeft :
10118 int action_arg_element =
10119 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10120 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10121 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10124 int action_arg_direction =
10125 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10126 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10127 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10128 change->actual_trigger_side :
10129 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10130 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10133 int action_arg_number_min =
10134 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10137 int action_arg_number_max =
10138 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10139 action_type == CA_SET_LEVEL_GEMS ? 999 :
10140 action_type == CA_SET_LEVEL_TIME ? 9999 :
10141 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10142 action_type == CA_SET_CE_VALUE ? 9999 :
10143 action_type == CA_SET_CE_SCORE ? 9999 :
10146 int action_arg_number_reset =
10147 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10148 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10149 action_type == CA_SET_LEVEL_TIME ? level.time :
10150 action_type == CA_SET_LEVEL_SCORE ? 0 :
10151 #if USE_NEW_CUSTOM_VALUE
10152 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10154 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10156 action_type == CA_SET_CE_SCORE ? 0 :
10159 int action_arg_number =
10160 (action_arg <= CA_ARG_MAX ? action_arg :
10161 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10162 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10163 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10164 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10165 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10166 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10167 #if USE_NEW_CUSTOM_VALUE
10168 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10170 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10172 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10173 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10174 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10175 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10176 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10177 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10178 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10179 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10180 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10181 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10182 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10185 int action_arg_number_old =
10186 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10187 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10188 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10189 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10190 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10193 int action_arg_number_new =
10194 getModifiedActionNumber(action_arg_number_old,
10195 action_mode, action_arg_number,
10196 action_arg_number_min, action_arg_number_max);
10199 int trigger_player_bits = change->actual_trigger_player_bits;
10201 int trigger_player_bits =
10202 (change->actual_trigger_player >= EL_PLAYER_1 &&
10203 change->actual_trigger_player <= EL_PLAYER_4 ?
10204 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10208 int action_arg_player_bits =
10209 (action_arg >= CA_ARG_PLAYER_1 &&
10210 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10211 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10214 /* ---------- execute action -------------------------------------------- */
10216 switch (action_type)
10223 /* ---------- level actions ------------------------------------------- */
10225 case CA_RESTART_LEVEL:
10227 game.restart_level = TRUE;
10232 case CA_SHOW_ENVELOPE:
10234 int element = getSpecialActionElement(action_arg_element,
10235 action_arg_number, EL_ENVELOPE_1);
10237 if (IS_ENVELOPE(element))
10238 local_player->show_envelope = element;
10243 case CA_SET_LEVEL_TIME:
10245 if (level.time > 0) /* only modify limited time value */
10247 TimeLeft = action_arg_number_new;
10250 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10252 DisplayGameControlValues();
10254 DrawGameValue_Time(TimeLeft);
10257 if (!TimeLeft && setup.time_limit)
10258 for (i = 0; i < MAX_PLAYERS; i++)
10259 KillPlayer(&stored_player[i]);
10265 case CA_SET_LEVEL_SCORE:
10267 local_player->score = action_arg_number_new;
10270 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10272 DisplayGameControlValues();
10274 DrawGameValue_Score(local_player->score);
10280 case CA_SET_LEVEL_GEMS:
10282 local_player->gems_still_needed = action_arg_number_new;
10285 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10287 DisplayGameControlValues();
10289 DrawGameValue_Emeralds(local_player->gems_still_needed);
10295 #if !USE_PLAYER_GRAVITY
10296 case CA_SET_LEVEL_GRAVITY:
10298 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10299 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10300 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10306 case CA_SET_LEVEL_WIND:
10308 game.wind_direction = action_arg_direction;
10313 /* ---------- player actions ------------------------------------------ */
10315 case CA_MOVE_PLAYER:
10317 /* automatically move to the next field in specified direction */
10318 for (i = 0; i < MAX_PLAYERS; i++)
10319 if (trigger_player_bits & (1 << i))
10320 stored_player[i].programmed_action = action_arg_direction;
10325 case CA_EXIT_PLAYER:
10327 for (i = 0; i < MAX_PLAYERS; i++)
10328 if (action_arg_player_bits & (1 << i))
10329 PlayerWins(&stored_player[i]);
10334 case CA_KILL_PLAYER:
10336 for (i = 0; i < MAX_PLAYERS; i++)
10337 if (action_arg_player_bits & (1 << i))
10338 KillPlayer(&stored_player[i]);
10343 case CA_SET_PLAYER_KEYS:
10345 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10346 int element = getSpecialActionElement(action_arg_element,
10347 action_arg_number, EL_KEY_1);
10349 if (IS_KEY(element))
10351 for (i = 0; i < MAX_PLAYERS; i++)
10353 if (trigger_player_bits & (1 << i))
10355 stored_player[i].key[KEY_NR(element)] = key_state;
10357 DrawGameDoorValues();
10365 case CA_SET_PLAYER_SPEED:
10367 for (i = 0; i < MAX_PLAYERS; i++)
10369 if (trigger_player_bits & (1 << i))
10371 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10373 if (action_arg == CA_ARG_SPEED_FASTER &&
10374 stored_player[i].cannot_move)
10376 action_arg_number = STEPSIZE_VERY_SLOW;
10378 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10379 action_arg == CA_ARG_SPEED_FASTER)
10381 action_arg_number = 2;
10382 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10385 else if (action_arg == CA_ARG_NUMBER_RESET)
10387 action_arg_number = level.initial_player_stepsize[i];
10391 getModifiedActionNumber(move_stepsize,
10394 action_arg_number_min,
10395 action_arg_number_max);
10397 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10404 case CA_SET_PLAYER_SHIELD:
10406 for (i = 0; i < MAX_PLAYERS; i++)
10408 if (trigger_player_bits & (1 << i))
10410 if (action_arg == CA_ARG_SHIELD_OFF)
10412 stored_player[i].shield_normal_time_left = 0;
10413 stored_player[i].shield_deadly_time_left = 0;
10415 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10417 stored_player[i].shield_normal_time_left = 999999;
10419 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10421 stored_player[i].shield_normal_time_left = 999999;
10422 stored_player[i].shield_deadly_time_left = 999999;
10430 #if USE_PLAYER_GRAVITY
10431 case CA_SET_PLAYER_GRAVITY:
10433 for (i = 0; i < MAX_PLAYERS; i++)
10435 if (trigger_player_bits & (1 << i))
10437 stored_player[i].gravity =
10438 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10439 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10440 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10441 stored_player[i].gravity);
10449 case CA_SET_PLAYER_ARTWORK:
10451 for (i = 0; i < MAX_PLAYERS; i++)
10453 if (trigger_player_bits & (1 << i))
10455 int artwork_element = action_arg_element;
10457 if (action_arg == CA_ARG_ELEMENT_RESET)
10459 (level.use_artwork_element[i] ? level.artwork_element[i] :
10460 stored_player[i].element_nr);
10462 #if USE_GFX_RESET_PLAYER_ARTWORK
10463 if (stored_player[i].artwork_element != artwork_element)
10464 stored_player[i].Frame = 0;
10467 stored_player[i].artwork_element = artwork_element;
10469 SetPlayerWaiting(&stored_player[i], FALSE);
10471 /* set number of special actions for bored and sleeping animation */
10472 stored_player[i].num_special_action_bored =
10473 get_num_special_action(artwork_element,
10474 ACTION_BORING_1, ACTION_BORING_LAST);
10475 stored_player[i].num_special_action_sleeping =
10476 get_num_special_action(artwork_element,
10477 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10484 /* ---------- CE actions ---------------------------------------------- */
10486 case CA_SET_CE_VALUE:
10488 #if USE_NEW_CUSTOM_VALUE
10489 int last_ce_value = CustomValue[x][y];
10491 CustomValue[x][y] = action_arg_number_new;
10493 if (CustomValue[x][y] != last_ce_value)
10495 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10496 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10498 if (CustomValue[x][y] == 0)
10500 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10501 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10509 case CA_SET_CE_SCORE:
10511 #if USE_NEW_CUSTOM_VALUE
10512 int last_ce_score = ei->collect_score;
10514 ei->collect_score = action_arg_number_new;
10516 if (ei->collect_score != last_ce_score)
10518 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10519 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10521 if (ei->collect_score == 0)
10525 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10526 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10529 This is a very special case that seems to be a mixture between
10530 CheckElementChange() and CheckTriggeredElementChange(): while
10531 the first one only affects single elements that are triggered
10532 directly, the second one affects multiple elements in the playfield
10533 that are triggered indirectly by another element. This is a third
10534 case: Changing the CE score always affects multiple identical CEs,
10535 so every affected CE must be checked, not only the single CE for
10536 which the CE score was changed in the first place (as every instance
10537 of that CE shares the same CE score, and therefore also can change)!
10539 SCAN_PLAYFIELD(xx, yy)
10541 if (Feld[xx][yy] == element)
10542 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10543 CE_SCORE_GETS_ZERO);
10552 /* ---------- engine actions ------------------------------------------ */
10554 case CA_SET_ENGINE_SCAN_MODE:
10556 InitPlayfieldScanMode(action_arg);
10566 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10568 int old_element = Feld[x][y];
10569 int new_element = GetElementFromGroupElement(element);
10570 int previous_move_direction = MovDir[x][y];
10571 #if USE_NEW_CUSTOM_VALUE
10572 int last_ce_value = CustomValue[x][y];
10574 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10575 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10576 boolean add_player_onto_element = (new_element_is_player &&
10577 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10578 /* this breaks SnakeBite when a snake is
10579 halfway through a door that closes */
10580 /* NOW FIXED AT LEVEL INIT IN files.c */
10581 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10583 IS_WALKABLE(old_element));
10586 /* check if element under the player changes from accessible to unaccessible
10587 (needed for special case of dropping element which then changes) */
10588 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10589 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10597 if (!add_player_onto_element)
10599 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10600 RemoveMovingField(x, y);
10604 Feld[x][y] = new_element;
10606 #if !USE_GFX_RESET_GFX_ANIMATION
10607 ResetGfxAnimation(x, y);
10608 ResetRandomAnimationValue(x, y);
10611 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10612 MovDir[x][y] = previous_move_direction;
10614 #if USE_NEW_CUSTOM_VALUE
10615 if (element_info[new_element].use_last_ce_value)
10616 CustomValue[x][y] = last_ce_value;
10619 InitField_WithBug1(x, y, FALSE);
10621 new_element = Feld[x][y]; /* element may have changed */
10623 #if USE_GFX_RESET_GFX_ANIMATION
10624 ResetGfxAnimation(x, y);
10625 ResetRandomAnimationValue(x, y);
10628 TEST_DrawLevelField(x, y);
10630 if (GFX_CRUMBLED(new_element))
10631 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10635 /* check if element under the player changes from accessible to unaccessible
10636 (needed for special case of dropping element which then changes) */
10637 /* (must be checked after creating new element for walkable group elements) */
10638 #if USE_FIX_KILLED_BY_NON_WALKABLE
10639 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10640 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10647 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10648 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10657 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10658 if (new_element_is_player)
10659 RelocatePlayer(x, y, new_element);
10662 ChangeCount[x][y]++; /* count number of changes in the same frame */
10664 TestIfBadThingTouchesPlayer(x, y);
10665 TestIfPlayerTouchesCustomElement(x, y);
10666 TestIfElementTouchesCustomElement(x, y);
10669 static void CreateField(int x, int y, int element)
10671 CreateFieldExt(x, y, element, FALSE);
10674 static void CreateElementFromChange(int x, int y, int element)
10676 element = GET_VALID_RUNTIME_ELEMENT(element);
10678 #if USE_STOP_CHANGED_ELEMENTS
10679 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10681 int old_element = Feld[x][y];
10683 /* prevent changed element from moving in same engine frame
10684 unless both old and new element can either fall or move */
10685 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10686 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10691 CreateFieldExt(x, y, element, TRUE);
10694 static boolean ChangeElement(int x, int y, int element, int page)
10696 struct ElementInfo *ei = &element_info[element];
10697 struct ElementChangeInfo *change = &ei->change_page[page];
10698 int ce_value = CustomValue[x][y];
10699 int ce_score = ei->collect_score;
10700 int target_element;
10701 int old_element = Feld[x][y];
10703 /* always use default change event to prevent running into a loop */
10704 if (ChangeEvent[x][y] == -1)
10705 ChangeEvent[x][y] = CE_DELAY;
10707 if (ChangeEvent[x][y] == CE_DELAY)
10709 /* reset actual trigger element, trigger player and action element */
10710 change->actual_trigger_element = EL_EMPTY;
10711 change->actual_trigger_player = EL_PLAYER_1;
10712 change->actual_trigger_player_bits = CH_PLAYER_1;
10713 change->actual_trigger_side = CH_SIDE_NONE;
10714 change->actual_trigger_ce_value = 0;
10715 change->actual_trigger_ce_score = 0;
10718 /* do not change elements more than a specified maximum number of changes */
10719 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10722 ChangeCount[x][y]++; /* count number of changes in the same frame */
10724 if (change->explode)
10731 if (change->use_target_content)
10733 boolean complete_replace = TRUE;
10734 boolean can_replace[3][3];
10737 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10740 boolean is_walkable;
10741 boolean is_diggable;
10742 boolean is_collectible;
10743 boolean is_removable;
10744 boolean is_destructible;
10745 int ex = x + xx - 1;
10746 int ey = y + yy - 1;
10747 int content_element = change->target_content.e[xx][yy];
10750 can_replace[xx][yy] = TRUE;
10752 if (ex == x && ey == y) /* do not check changing element itself */
10755 if (content_element == EL_EMPTY_SPACE)
10757 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10762 if (!IN_LEV_FIELD(ex, ey))
10764 can_replace[xx][yy] = FALSE;
10765 complete_replace = FALSE;
10772 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10773 e = MovingOrBlocked2Element(ex, ey);
10775 is_empty = (IS_FREE(ex, ey) ||
10776 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10778 is_walkable = (is_empty || IS_WALKABLE(e));
10779 is_diggable = (is_empty || IS_DIGGABLE(e));
10780 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10781 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10782 is_removable = (is_diggable || is_collectible);
10784 can_replace[xx][yy] =
10785 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10786 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10787 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10788 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10789 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10790 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10791 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10793 if (!can_replace[xx][yy])
10794 complete_replace = FALSE;
10797 if (!change->only_if_complete || complete_replace)
10799 boolean something_has_changed = FALSE;
10801 if (change->only_if_complete && change->use_random_replace &&
10802 RND(100) < change->random_percentage)
10805 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10807 int ex = x + xx - 1;
10808 int ey = y + yy - 1;
10809 int content_element;
10811 if (can_replace[xx][yy] && (!change->use_random_replace ||
10812 RND(100) < change->random_percentage))
10814 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10815 RemoveMovingField(ex, ey);
10817 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10819 content_element = change->target_content.e[xx][yy];
10820 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10821 ce_value, ce_score);
10823 CreateElementFromChange(ex, ey, target_element);
10825 something_has_changed = TRUE;
10827 /* for symmetry reasons, freeze newly created border elements */
10828 if (ex != x || ey != y)
10829 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10833 if (something_has_changed)
10835 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10836 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10842 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10843 ce_value, ce_score);
10845 if (element == EL_DIAGONAL_GROWING ||
10846 element == EL_DIAGONAL_SHRINKING)
10848 target_element = Store[x][y];
10850 Store[x][y] = EL_EMPTY;
10853 CreateElementFromChange(x, y, target_element);
10855 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10856 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10859 /* this uses direct change before indirect change */
10860 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10865 #if USE_NEW_DELAYED_ACTION
10867 static void HandleElementChange(int x, int y, int page)
10869 int element = MovingOrBlocked2Element(x, y);
10870 struct ElementInfo *ei = &element_info[element];
10871 struct ElementChangeInfo *change = &ei->change_page[page];
10874 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10875 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10878 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10879 x, y, element, element_info[element].token_name);
10880 printf("HandleElementChange(): This should never happen!\n");
10885 /* this can happen with classic bombs on walkable, changing elements */
10886 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10889 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10890 ChangeDelay[x][y] = 0;
10896 if (ChangeDelay[x][y] == 0) /* initialize element change */
10898 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10900 if (change->can_change)
10903 /* !!! not clear why graphic animation should be reset at all here !!! */
10904 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10905 #if USE_GFX_RESET_WHEN_NOT_MOVING
10906 /* when a custom element is about to change (for example by change delay),
10907 do not reset graphic animation when the custom element is moving */
10908 if (!IS_MOVING(x, y))
10911 ResetGfxAnimation(x, y);
10912 ResetRandomAnimationValue(x, y);
10916 if (change->pre_change_function)
10917 change->pre_change_function(x, y);
10921 ChangeDelay[x][y]--;
10923 if (ChangeDelay[x][y] != 0) /* continue element change */
10925 if (change->can_change)
10927 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10929 if (IS_ANIMATED(graphic))
10930 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10932 if (change->change_function)
10933 change->change_function(x, y);
10936 else /* finish element change */
10938 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10940 page = ChangePage[x][y];
10941 ChangePage[x][y] = -1;
10943 change = &ei->change_page[page];
10946 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10948 ChangeDelay[x][y] = 1; /* try change after next move step */
10949 ChangePage[x][y] = page; /* remember page to use for change */
10954 if (change->can_change)
10956 if (ChangeElement(x, y, element, page))
10958 if (change->post_change_function)
10959 change->post_change_function(x, y);
10963 if (change->has_action)
10964 ExecuteCustomElementAction(x, y, element, page);
10970 static void HandleElementChange(int x, int y, int page)
10972 int element = MovingOrBlocked2Element(x, y);
10973 struct ElementInfo *ei = &element_info[element];
10974 struct ElementChangeInfo *change = &ei->change_page[page];
10977 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10980 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10981 x, y, element, element_info[element].token_name);
10982 printf("HandleElementChange(): This should never happen!\n");
10987 /* this can happen with classic bombs on walkable, changing elements */
10988 if (!CAN_CHANGE(element))
10991 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10992 ChangeDelay[x][y] = 0;
10998 if (ChangeDelay[x][y] == 0) /* initialize element change */
11000 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11002 ResetGfxAnimation(x, y);
11003 ResetRandomAnimationValue(x, y);
11005 if (change->pre_change_function)
11006 change->pre_change_function(x, y);
11009 ChangeDelay[x][y]--;
11011 if (ChangeDelay[x][y] != 0) /* continue element change */
11013 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11015 if (IS_ANIMATED(graphic))
11016 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11018 if (change->change_function)
11019 change->change_function(x, y);
11021 else /* finish element change */
11023 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11025 page = ChangePage[x][y];
11026 ChangePage[x][y] = -1;
11028 change = &ei->change_page[page];
11031 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11033 ChangeDelay[x][y] = 1; /* try change after next move step */
11034 ChangePage[x][y] = page; /* remember page to use for change */
11039 if (ChangeElement(x, y, element, page))
11041 if (change->post_change_function)
11042 change->post_change_function(x, y);
11049 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11050 int trigger_element,
11052 int trigger_player,
11056 boolean change_done_any = FALSE;
11057 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11060 if (!(trigger_events[trigger_element][trigger_event]))
11064 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11065 trigger_event, recursion_loop_depth, recursion_loop_detected,
11066 recursion_loop_element, EL_NAME(recursion_loop_element));
11069 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11071 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11073 int element = EL_CUSTOM_START + i;
11074 boolean change_done = FALSE;
11077 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11078 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11081 for (p = 0; p < element_info[element].num_change_pages; p++)
11083 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11085 if (change->can_change_or_has_action &&
11086 change->has_event[trigger_event] &&
11087 change->trigger_side & trigger_side &&
11088 change->trigger_player & trigger_player &&
11089 change->trigger_page & trigger_page_bits &&
11090 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11092 change->actual_trigger_element = trigger_element;
11093 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11094 change->actual_trigger_player_bits = trigger_player;
11095 change->actual_trigger_side = trigger_side;
11096 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11097 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11099 if ((change->can_change && !change_done) || change->has_action)
11103 SCAN_PLAYFIELD(x, y)
11105 if (Feld[x][y] == element)
11107 if (change->can_change && !change_done)
11109 ChangeDelay[x][y] = 1;
11110 ChangeEvent[x][y] = trigger_event;
11112 HandleElementChange(x, y, p);
11114 #if USE_NEW_DELAYED_ACTION
11115 else if (change->has_action)
11117 ExecuteCustomElementAction(x, y, element, p);
11118 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11121 if (change->has_action)
11123 ExecuteCustomElementAction(x, y, element, p);
11124 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11130 if (change->can_change)
11132 change_done = TRUE;
11133 change_done_any = TRUE;
11140 RECURSION_LOOP_DETECTION_END();
11142 return change_done_any;
11145 static boolean CheckElementChangeExt(int x, int y,
11147 int trigger_element,
11149 int trigger_player,
11152 boolean change_done = FALSE;
11155 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11156 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11159 if (Feld[x][y] == EL_BLOCKED)
11161 Blocked2Moving(x, y, &x, &y);
11162 element = Feld[x][y];
11166 /* check if element has already changed */
11167 if (Feld[x][y] != element)
11170 /* check if element has already changed or is about to change after moving */
11171 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11172 Feld[x][y] != element) ||
11174 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11175 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11176 ChangePage[x][y] != -1)))
11181 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11182 trigger_event, recursion_loop_depth, recursion_loop_detected,
11183 recursion_loop_element, EL_NAME(recursion_loop_element));
11186 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11188 for (p = 0; p < element_info[element].num_change_pages; p++)
11190 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11192 /* check trigger element for all events where the element that is checked
11193 for changing interacts with a directly adjacent element -- this is
11194 different to element changes that affect other elements to change on the
11195 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11196 boolean check_trigger_element =
11197 (trigger_event == CE_TOUCHING_X ||
11198 trigger_event == CE_HITTING_X ||
11199 trigger_event == CE_HIT_BY_X ||
11201 /* this one was forgotten until 3.2.3 */
11202 trigger_event == CE_DIGGING_X);
11205 if (change->can_change_or_has_action &&
11206 change->has_event[trigger_event] &&
11207 change->trigger_side & trigger_side &&
11208 change->trigger_player & trigger_player &&
11209 (!check_trigger_element ||
11210 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11212 change->actual_trigger_element = trigger_element;
11213 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11214 change->actual_trigger_player_bits = trigger_player;
11215 change->actual_trigger_side = trigger_side;
11216 change->actual_trigger_ce_value = CustomValue[x][y];
11217 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11219 /* special case: trigger element not at (x,y) position for some events */
11220 if (check_trigger_element)
11232 { 0, 0 }, { 0, 0 }, { 0, 0 },
11236 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11237 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11239 change->actual_trigger_ce_value = CustomValue[xx][yy];
11240 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11243 if (change->can_change && !change_done)
11245 ChangeDelay[x][y] = 1;
11246 ChangeEvent[x][y] = trigger_event;
11248 HandleElementChange(x, y, p);
11250 change_done = TRUE;
11252 #if USE_NEW_DELAYED_ACTION
11253 else if (change->has_action)
11255 ExecuteCustomElementAction(x, y, element, p);
11256 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11259 if (change->has_action)
11261 ExecuteCustomElementAction(x, y, element, p);
11262 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11268 RECURSION_LOOP_DETECTION_END();
11270 return change_done;
11273 static void PlayPlayerSound(struct PlayerInfo *player)
11275 int jx = player->jx, jy = player->jy;
11276 int sound_element = player->artwork_element;
11277 int last_action = player->last_action_waiting;
11278 int action = player->action_waiting;
11280 if (player->is_waiting)
11282 if (action != last_action)
11283 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11285 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11289 if (action != last_action)
11290 StopSound(element_info[sound_element].sound[last_action]);
11292 if (last_action == ACTION_SLEEPING)
11293 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11297 static void PlayAllPlayersSound()
11301 for (i = 0; i < MAX_PLAYERS; i++)
11302 if (stored_player[i].active)
11303 PlayPlayerSound(&stored_player[i]);
11306 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11308 boolean last_waiting = player->is_waiting;
11309 int move_dir = player->MovDir;
11311 player->dir_waiting = move_dir;
11312 player->last_action_waiting = player->action_waiting;
11316 if (!last_waiting) /* not waiting -> waiting */
11318 player->is_waiting = TRUE;
11320 player->frame_counter_bored =
11322 game.player_boring_delay_fixed +
11323 GetSimpleRandom(game.player_boring_delay_random);
11324 player->frame_counter_sleeping =
11326 game.player_sleeping_delay_fixed +
11327 GetSimpleRandom(game.player_sleeping_delay_random);
11329 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11332 if (game.player_sleeping_delay_fixed +
11333 game.player_sleeping_delay_random > 0 &&
11334 player->anim_delay_counter == 0 &&
11335 player->post_delay_counter == 0 &&
11336 FrameCounter >= player->frame_counter_sleeping)
11337 player->is_sleeping = TRUE;
11338 else if (game.player_boring_delay_fixed +
11339 game.player_boring_delay_random > 0 &&
11340 FrameCounter >= player->frame_counter_bored)
11341 player->is_bored = TRUE;
11343 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11344 player->is_bored ? ACTION_BORING :
11347 if (player->is_sleeping && player->use_murphy)
11349 /* special case for sleeping Murphy when leaning against non-free tile */
11351 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11352 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11353 !IS_MOVING(player->jx - 1, player->jy)))
11354 move_dir = MV_LEFT;
11355 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11356 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11357 !IS_MOVING(player->jx + 1, player->jy)))
11358 move_dir = MV_RIGHT;
11360 player->is_sleeping = FALSE;
11362 player->dir_waiting = move_dir;
11365 if (player->is_sleeping)
11367 if (player->num_special_action_sleeping > 0)
11369 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11371 int last_special_action = player->special_action_sleeping;
11372 int num_special_action = player->num_special_action_sleeping;
11373 int special_action =
11374 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11375 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11376 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11377 last_special_action + 1 : ACTION_SLEEPING);
11378 int special_graphic =
11379 el_act_dir2img(player->artwork_element, special_action, move_dir);
11381 player->anim_delay_counter =
11382 graphic_info[special_graphic].anim_delay_fixed +
11383 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11384 player->post_delay_counter =
11385 graphic_info[special_graphic].post_delay_fixed +
11386 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11388 player->special_action_sleeping = special_action;
11391 if (player->anim_delay_counter > 0)
11393 player->action_waiting = player->special_action_sleeping;
11394 player->anim_delay_counter--;
11396 else if (player->post_delay_counter > 0)
11398 player->post_delay_counter--;
11402 else if (player->is_bored)
11404 if (player->num_special_action_bored > 0)
11406 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11408 int special_action =
11409 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11410 int special_graphic =
11411 el_act_dir2img(player->artwork_element, special_action, move_dir);
11413 player->anim_delay_counter =
11414 graphic_info[special_graphic].anim_delay_fixed +
11415 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11416 player->post_delay_counter =
11417 graphic_info[special_graphic].post_delay_fixed +
11418 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11420 player->special_action_bored = special_action;
11423 if (player->anim_delay_counter > 0)
11425 player->action_waiting = player->special_action_bored;
11426 player->anim_delay_counter--;
11428 else if (player->post_delay_counter > 0)
11430 player->post_delay_counter--;
11435 else if (last_waiting) /* waiting -> not waiting */
11437 player->is_waiting = FALSE;
11438 player->is_bored = FALSE;
11439 player->is_sleeping = FALSE;
11441 player->frame_counter_bored = -1;
11442 player->frame_counter_sleeping = -1;
11444 player->anim_delay_counter = 0;
11445 player->post_delay_counter = 0;
11447 player->dir_waiting = player->MovDir;
11448 player->action_waiting = ACTION_DEFAULT;
11450 player->special_action_bored = ACTION_DEFAULT;
11451 player->special_action_sleeping = ACTION_DEFAULT;
11455 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11457 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11458 int left = player_action & JOY_LEFT;
11459 int right = player_action & JOY_RIGHT;
11460 int up = player_action & JOY_UP;
11461 int down = player_action & JOY_DOWN;
11462 int button1 = player_action & JOY_BUTTON_1;
11463 int button2 = player_action & JOY_BUTTON_2;
11464 int dx = (left ? -1 : right ? 1 : 0);
11465 int dy = (up ? -1 : down ? 1 : 0);
11467 if (!player->active || tape.pausing)
11473 snapped = SnapField(player, dx, dy);
11477 dropped = DropElement(player);
11479 moved = MovePlayer(player, dx, dy);
11482 if (tape.single_step && tape.recording && !tape.pausing)
11484 if (button1 || (dropped && !moved))
11486 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11487 SnapField(player, 0, 0); /* stop snapping */
11491 SetPlayerWaiting(player, FALSE);
11493 return player_action;
11497 /* no actions for this player (no input at player's configured device) */
11499 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11500 SnapField(player, 0, 0);
11501 CheckGravityMovementWhenNotMoving(player);
11503 if (player->MovPos == 0)
11504 SetPlayerWaiting(player, TRUE);
11506 if (player->MovPos == 0) /* needed for tape.playing */
11507 player->is_moving = FALSE;
11509 player->is_dropping = FALSE;
11510 player->is_dropping_pressed = FALSE;
11511 player->drop_pressed_delay = 0;
11517 static void CheckLevelTime()
11521 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11523 if (level.native_em_level->lev->home == 0) /* all players at home */
11525 PlayerWins(local_player);
11527 AllPlayersGone = TRUE;
11529 level.native_em_level->lev->home = -1;
11532 if (level.native_em_level->ply[0]->alive == 0 &&
11533 level.native_em_level->ply[1]->alive == 0 &&
11534 level.native_em_level->ply[2]->alive == 0 &&
11535 level.native_em_level->ply[3]->alive == 0) /* all dead */
11536 AllPlayersGone = TRUE;
11539 if (TimeFrames >= FRAMES_PER_SECOND)
11544 for (i = 0; i < MAX_PLAYERS; i++)
11546 struct PlayerInfo *player = &stored_player[i];
11548 if (SHIELD_ON(player))
11550 player->shield_normal_time_left--;
11552 if (player->shield_deadly_time_left > 0)
11553 player->shield_deadly_time_left--;
11557 if (!local_player->LevelSolved && !level.use_step_counter)
11565 if (TimeLeft <= 10 && setup.time_limit)
11566 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11569 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11571 DisplayGameControlValues();
11573 DrawGameValue_Time(TimeLeft);
11576 if (!TimeLeft && setup.time_limit)
11578 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11579 level.native_em_level->lev->killed_out_of_time = TRUE;
11581 for (i = 0; i < MAX_PLAYERS; i++)
11582 KillPlayer(&stored_player[i]);
11586 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11588 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11590 DisplayGameControlValues();
11593 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11594 DrawGameValue_Time(TimePlayed);
11597 level.native_em_level->lev->time =
11598 (level.time == 0 ? TimePlayed : TimeLeft);
11601 if (tape.recording || tape.playing)
11602 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11606 UpdateAndDisplayGameControlValues();
11608 UpdateGameDoorValues();
11609 DrawGameDoorValues();
11613 void AdvanceFrameAndPlayerCounters(int player_nr)
11617 /* advance frame counters (global frame counter and time frame counter) */
11621 /* advance player counters (counters for move delay, move animation etc.) */
11622 for (i = 0; i < MAX_PLAYERS; i++)
11624 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11625 int move_delay_value = stored_player[i].move_delay_value;
11626 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11628 if (!advance_player_counters) /* not all players may be affected */
11631 #if USE_NEW_PLAYER_ANIM
11632 if (move_frames == 0) /* less than one move per game frame */
11634 int stepsize = TILEX / move_delay_value;
11635 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11636 int count = (stored_player[i].is_moving ?
11637 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11639 if (count % delay == 0)
11644 stored_player[i].Frame += move_frames;
11646 if (stored_player[i].MovPos != 0)
11647 stored_player[i].StepFrame += move_frames;
11649 if (stored_player[i].move_delay > 0)
11650 stored_player[i].move_delay--;
11652 /* due to bugs in previous versions, counter must count up, not down */
11653 if (stored_player[i].push_delay != -1)
11654 stored_player[i].push_delay++;
11656 if (stored_player[i].drop_delay > 0)
11657 stored_player[i].drop_delay--;
11659 if (stored_player[i].is_dropping_pressed)
11660 stored_player[i].drop_pressed_delay++;
11664 void StartGameActions(boolean init_network_game, boolean record_tape,
11667 unsigned long new_random_seed = InitRND(random_seed);
11670 TapeStartRecording(new_random_seed);
11672 #if defined(NETWORK_AVALIABLE)
11673 if (init_network_game)
11675 SendToServer_StartPlaying();
11686 static unsigned long game_frame_delay = 0;
11687 unsigned long game_frame_delay_value;
11688 byte *recorded_player_action;
11689 byte summarized_player_action = 0;
11690 byte tape_action[MAX_PLAYERS];
11693 /* detect endless loops, caused by custom element programming */
11694 if (recursion_loop_detected && recursion_loop_depth == 0)
11696 char *message = getStringCat3("Internal Error ! Element ",
11697 EL_NAME(recursion_loop_element),
11698 " caused endless loop ! Quit the game ?");
11700 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11701 EL_NAME(recursion_loop_element));
11703 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11705 recursion_loop_detected = FALSE; /* if game should be continued */
11712 if (game.restart_level)
11713 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11715 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11717 if (level.native_em_level->lev->home == 0) /* all players at home */
11719 PlayerWins(local_player);
11721 AllPlayersGone = TRUE;
11723 level.native_em_level->lev->home = -1;
11726 if (level.native_em_level->ply[0]->alive == 0 &&
11727 level.native_em_level->ply[1]->alive == 0 &&
11728 level.native_em_level->ply[2]->alive == 0 &&
11729 level.native_em_level->ply[3]->alive == 0) /* all dead */
11730 AllPlayersGone = TRUE;
11733 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11736 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11739 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11742 game_frame_delay_value =
11743 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11745 if (tape.playing && tape.warp_forward && !tape.pausing)
11746 game_frame_delay_value = 0;
11748 /* ---------- main game synchronization point ---------- */
11750 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11752 if (network_playing && !network_player_action_received)
11754 /* try to get network player actions in time */
11756 #if defined(NETWORK_AVALIABLE)
11757 /* last chance to get network player actions without main loop delay */
11758 HandleNetworking();
11761 /* game was quit by network peer */
11762 if (game_status != GAME_MODE_PLAYING)
11765 if (!network_player_action_received)
11766 return; /* failed to get network player actions in time */
11768 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11774 /* at this point we know that we really continue executing the game */
11776 network_player_action_received = FALSE;
11778 /* when playing tape, read previously recorded player input from tape data */
11779 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11782 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11787 if (tape.set_centered_player)
11789 game.centered_player_nr_next = tape.centered_player_nr_next;
11790 game.set_centered_player = TRUE;
11793 for (i = 0; i < MAX_PLAYERS; i++)
11795 summarized_player_action |= stored_player[i].action;
11797 if (!network_playing)
11798 stored_player[i].effective_action = stored_player[i].action;
11801 #if defined(NETWORK_AVALIABLE)
11802 if (network_playing)
11803 SendToServer_MovePlayer(summarized_player_action);
11806 if (!options.network && !setup.team_mode)
11807 local_player->effective_action = summarized_player_action;
11809 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11811 for (i = 0; i < MAX_PLAYERS; i++)
11812 stored_player[i].effective_action =
11813 (i == game.centered_player_nr ? summarized_player_action : 0);
11816 if (recorded_player_action != NULL)
11817 for (i = 0; i < MAX_PLAYERS; i++)
11818 stored_player[i].effective_action = recorded_player_action[i];
11820 for (i = 0; i < MAX_PLAYERS; i++)
11822 tape_action[i] = stored_player[i].effective_action;
11824 /* (this can only happen in the R'n'D game engine) */
11825 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11826 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11829 /* only record actions from input devices, but not programmed actions */
11830 if (tape.recording)
11831 TapeRecordAction(tape_action);
11833 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11835 GameActions_EM_Main();
11843 void GameActions_EM_Main()
11845 byte effective_action[MAX_PLAYERS];
11846 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11849 for (i = 0; i < MAX_PLAYERS; i++)
11850 effective_action[i] = stored_player[i].effective_action;
11852 GameActions_EM(effective_action, warp_mode);
11856 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11859 void GameActions_RND()
11861 int magic_wall_x = 0, magic_wall_y = 0;
11862 int i, x, y, element, graphic;
11864 InitPlayfieldScanModeVars();
11866 #if USE_ONE_MORE_CHANGE_PER_FRAME
11867 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11869 SCAN_PLAYFIELD(x, y)
11871 ChangeCount[x][y] = 0;
11872 ChangeEvent[x][y] = -1;
11877 if (game.set_centered_player)
11879 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11881 /* switching to "all players" only possible if all players fit to screen */
11882 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11884 game.centered_player_nr_next = game.centered_player_nr;
11885 game.set_centered_player = FALSE;
11888 /* do not switch focus to non-existing (or non-active) player */
11889 if (game.centered_player_nr_next >= 0 &&
11890 !stored_player[game.centered_player_nr_next].active)
11892 game.centered_player_nr_next = game.centered_player_nr;
11893 game.set_centered_player = FALSE;
11897 if (game.set_centered_player &&
11898 ScreenMovPos == 0) /* screen currently aligned at tile position */
11902 if (game.centered_player_nr_next == -1)
11904 setScreenCenteredToAllPlayers(&sx, &sy);
11908 sx = stored_player[game.centered_player_nr_next].jx;
11909 sy = stored_player[game.centered_player_nr_next].jy;
11912 game.centered_player_nr = game.centered_player_nr_next;
11913 game.set_centered_player = FALSE;
11915 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11916 DrawGameDoorValues();
11919 for (i = 0; i < MAX_PLAYERS; i++)
11921 int actual_player_action = stored_player[i].effective_action;
11924 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11925 - rnd_equinox_tetrachloride 048
11926 - rnd_equinox_tetrachloride_ii 096
11927 - rnd_emanuel_schmieg 002
11928 - doctor_sloan_ww 001, 020
11930 if (stored_player[i].MovPos == 0)
11931 CheckGravityMovement(&stored_player[i]);
11934 /* overwrite programmed action with tape action */
11935 if (stored_player[i].programmed_action)
11936 actual_player_action = stored_player[i].programmed_action;
11938 PlayerActions(&stored_player[i], actual_player_action);
11940 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11943 ScrollScreen(NULL, SCROLL_GO_ON);
11945 /* for backwards compatibility, the following code emulates a fixed bug that
11946 occured when pushing elements (causing elements that just made their last
11947 pushing step to already (if possible) make their first falling step in the
11948 same game frame, which is bad); this code is also needed to use the famous
11949 "spring push bug" which is used in older levels and might be wanted to be
11950 used also in newer levels, but in this case the buggy pushing code is only
11951 affecting the "spring" element and no other elements */
11953 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11955 for (i = 0; i < MAX_PLAYERS; i++)
11957 struct PlayerInfo *player = &stored_player[i];
11958 int x = player->jx;
11959 int y = player->jy;
11961 if (player->active && player->is_pushing && player->is_moving &&
11963 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11964 Feld[x][y] == EL_SPRING))
11966 ContinueMoving(x, y);
11968 /* continue moving after pushing (this is actually a bug) */
11969 if (!IS_MOVING(x, y))
11970 Stop[x][y] = FALSE;
11976 debug_print_timestamp(0, "start main loop profiling");
11979 SCAN_PLAYFIELD(x, y)
11981 ChangeCount[x][y] = 0;
11982 ChangeEvent[x][y] = -1;
11984 /* this must be handled before main playfield loop */
11985 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11988 if (MovDelay[x][y] <= 0)
11992 #if USE_NEW_SNAP_DELAY
11993 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11996 if (MovDelay[x][y] <= 0)
11999 TEST_DrawLevelField(x, y);
12001 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12007 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12009 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12010 printf("GameActions(): This should never happen!\n");
12012 ChangePage[x][y] = -1;
12016 Stop[x][y] = FALSE;
12017 if (WasJustMoving[x][y] > 0)
12018 WasJustMoving[x][y]--;
12019 if (WasJustFalling[x][y] > 0)
12020 WasJustFalling[x][y]--;
12021 if (CheckCollision[x][y] > 0)
12022 CheckCollision[x][y]--;
12023 if (CheckImpact[x][y] > 0)
12024 CheckImpact[x][y]--;
12028 /* reset finished pushing action (not done in ContinueMoving() to allow
12029 continuous pushing animation for elements with zero push delay) */
12030 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12032 ResetGfxAnimation(x, y);
12033 TEST_DrawLevelField(x, y);
12037 if (IS_BLOCKED(x, y))
12041 Blocked2Moving(x, y, &oldx, &oldy);
12042 if (!IS_MOVING(oldx, oldy))
12044 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12045 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12046 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12047 printf("GameActions(): This should never happen!\n");
12054 debug_print_timestamp(0, "- time for pre-main loop:");
12057 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12058 SCAN_PLAYFIELD(x, y)
12060 element = Feld[x][y];
12061 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12066 int element2 = element;
12067 int graphic2 = graphic;
12069 int element2 = Feld[x][y];
12070 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12072 int last_gfx_frame = GfxFrame[x][y];
12074 if (graphic_info[graphic2].anim_global_sync)
12075 GfxFrame[x][y] = FrameCounter;
12076 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12077 GfxFrame[x][y] = CustomValue[x][y];
12078 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12079 GfxFrame[x][y] = element_info[element2].collect_score;
12080 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12081 GfxFrame[x][y] = ChangeDelay[x][y];
12083 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12084 DrawLevelGraphicAnimation(x, y, graphic2);
12087 ResetGfxFrame(x, y, TRUE);
12091 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12092 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12093 ResetRandomAnimationValue(x, y);
12097 SetRandomAnimationValue(x, y);
12101 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12104 #endif // -------------------- !!! TEST ONLY !!! --------------------
12107 debug_print_timestamp(0, "- time for TEST loop: -->");
12110 SCAN_PLAYFIELD(x, y)
12112 element = Feld[x][y];
12113 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12115 ResetGfxFrame(x, y, TRUE);
12117 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12118 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12119 ResetRandomAnimationValue(x, y);
12121 SetRandomAnimationValue(x, y);
12123 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12125 if (IS_INACTIVE(element))
12127 if (IS_ANIMATED(graphic))
12128 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12133 /* this may take place after moving, so 'element' may have changed */
12134 if (IS_CHANGING(x, y) &&
12135 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12137 int page = element_info[element].event_page_nr[CE_DELAY];
12140 HandleElementChange(x, y, page);
12142 if (CAN_CHANGE(element))
12143 HandleElementChange(x, y, page);
12145 if (HAS_ACTION(element))
12146 ExecuteCustomElementAction(x, y, element, page);
12149 element = Feld[x][y];
12150 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12153 #if 0 // ---------------------------------------------------------------------
12155 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12159 element = Feld[x][y];
12160 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12162 if (IS_ANIMATED(graphic) &&
12163 !IS_MOVING(x, y) &&
12165 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12167 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12168 TEST_DrawTwinkleOnField(x, y);
12170 else if (IS_MOVING(x, y))
12171 ContinueMoving(x, y);
12178 case EL_EM_EXIT_OPEN:
12179 case EL_SP_EXIT_OPEN:
12180 case EL_STEEL_EXIT_OPEN:
12181 case EL_EM_STEEL_EXIT_OPEN:
12182 case EL_SP_TERMINAL:
12183 case EL_SP_TERMINAL_ACTIVE:
12184 case EL_EXTRA_TIME:
12185 case EL_SHIELD_NORMAL:
12186 case EL_SHIELD_DEADLY:
12187 if (IS_ANIMATED(graphic))
12188 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12191 case EL_DYNAMITE_ACTIVE:
12192 case EL_EM_DYNAMITE_ACTIVE:
12193 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12194 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12195 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12196 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12197 case EL_SP_DISK_RED_ACTIVE:
12198 CheckDynamite(x, y);
12201 case EL_AMOEBA_GROWING:
12202 AmoebeWaechst(x, y);
12205 case EL_AMOEBA_SHRINKING:
12206 AmoebaDisappearing(x, y);
12209 #if !USE_NEW_AMOEBA_CODE
12210 case EL_AMOEBA_WET:
12211 case EL_AMOEBA_DRY:
12212 case EL_AMOEBA_FULL:
12214 case EL_EMC_DRIPPER:
12215 AmoebeAbleger(x, y);
12219 case EL_GAME_OF_LIFE:
12224 case EL_EXIT_CLOSED:
12228 case EL_EM_EXIT_CLOSED:
12232 case EL_STEEL_EXIT_CLOSED:
12233 CheckExitSteel(x, y);
12236 case EL_EM_STEEL_EXIT_CLOSED:
12237 CheckExitSteelEM(x, y);
12240 case EL_SP_EXIT_CLOSED:
12244 case EL_EXPANDABLE_WALL_GROWING:
12245 case EL_EXPANDABLE_STEELWALL_GROWING:
12246 MauerWaechst(x, y);
12249 case EL_EXPANDABLE_WALL:
12250 case EL_EXPANDABLE_WALL_HORIZONTAL:
12251 case EL_EXPANDABLE_WALL_VERTICAL:
12252 case EL_EXPANDABLE_WALL_ANY:
12253 case EL_BD_EXPANDABLE_WALL:
12254 MauerAbleger(x, y);
12257 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12258 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12259 case EL_EXPANDABLE_STEELWALL_ANY:
12260 MauerAblegerStahl(x, y);
12264 CheckForDragon(x, y);
12270 case EL_ELEMENT_SNAPPING:
12271 case EL_DIAGONAL_SHRINKING:
12272 case EL_DIAGONAL_GROWING:
12275 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12277 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12282 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12283 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12288 #else // ---------------------------------------------------------------------
12290 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12294 element = Feld[x][y];
12295 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12297 if (IS_ANIMATED(graphic) &&
12298 !IS_MOVING(x, y) &&
12300 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12302 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12303 TEST_DrawTwinkleOnField(x, y);
12305 else if ((element == EL_ACID ||
12306 element == EL_EXIT_OPEN ||
12307 element == EL_EM_EXIT_OPEN ||
12308 element == EL_SP_EXIT_OPEN ||
12309 element == EL_STEEL_EXIT_OPEN ||
12310 element == EL_EM_STEEL_EXIT_OPEN ||
12311 element == EL_SP_TERMINAL ||
12312 element == EL_SP_TERMINAL_ACTIVE ||
12313 element == EL_EXTRA_TIME ||
12314 element == EL_SHIELD_NORMAL ||
12315 element == EL_SHIELD_DEADLY) &&
12316 IS_ANIMATED(graphic))
12317 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12318 else if (IS_MOVING(x, y))
12319 ContinueMoving(x, y);
12320 else if (IS_ACTIVE_BOMB(element))
12321 CheckDynamite(x, y);
12322 else if (element == EL_AMOEBA_GROWING)
12323 AmoebeWaechst(x, y);
12324 else if (element == EL_AMOEBA_SHRINKING)
12325 AmoebaDisappearing(x, y);
12327 #if !USE_NEW_AMOEBA_CODE
12328 else if (IS_AMOEBALIVE(element))
12329 AmoebeAbleger(x, y);
12332 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12334 else if (element == EL_EXIT_CLOSED)
12336 else if (element == EL_EM_EXIT_CLOSED)
12338 else if (element == EL_STEEL_EXIT_CLOSED)
12339 CheckExitSteel(x, y);
12340 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12341 CheckExitSteelEM(x, y);
12342 else if (element == EL_SP_EXIT_CLOSED)
12344 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12345 element == EL_EXPANDABLE_STEELWALL_GROWING)
12346 MauerWaechst(x, y);
12347 else if (element == EL_EXPANDABLE_WALL ||
12348 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12349 element == EL_EXPANDABLE_WALL_VERTICAL ||
12350 element == EL_EXPANDABLE_WALL_ANY ||
12351 element == EL_BD_EXPANDABLE_WALL)
12352 MauerAbleger(x, y);
12353 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12354 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12355 element == EL_EXPANDABLE_STEELWALL_ANY)
12356 MauerAblegerStahl(x, y);
12357 else if (element == EL_FLAMES)
12358 CheckForDragon(x, y);
12359 else if (element == EL_EXPLOSION)
12360 ; /* drawing of correct explosion animation is handled separately */
12361 else if (element == EL_ELEMENT_SNAPPING ||
12362 element == EL_DIAGONAL_SHRINKING ||
12363 element == EL_DIAGONAL_GROWING)
12365 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12367 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12369 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12370 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12372 #endif // ---------------------------------------------------------------------
12374 if (IS_BELT_ACTIVE(element))
12375 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12377 if (game.magic_wall_active)
12379 int jx = local_player->jx, jy = local_player->jy;
12381 /* play the element sound at the position nearest to the player */
12382 if ((element == EL_MAGIC_WALL_FULL ||
12383 element == EL_MAGIC_WALL_ACTIVE ||
12384 element == EL_MAGIC_WALL_EMPTYING ||
12385 element == EL_BD_MAGIC_WALL_FULL ||
12386 element == EL_BD_MAGIC_WALL_ACTIVE ||
12387 element == EL_BD_MAGIC_WALL_EMPTYING ||
12388 element == EL_DC_MAGIC_WALL_FULL ||
12389 element == EL_DC_MAGIC_WALL_ACTIVE ||
12390 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12391 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12400 debug_print_timestamp(0, "- time for MAIN loop: -->");
12403 #if USE_NEW_AMOEBA_CODE
12404 /* new experimental amoeba growth stuff */
12405 if (!(FrameCounter % 8))
12407 static unsigned long random = 1684108901;
12409 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12411 x = RND(lev_fieldx);
12412 y = RND(lev_fieldy);
12413 element = Feld[x][y];
12415 if (!IS_PLAYER(x,y) &&
12416 (element == EL_EMPTY ||
12417 CAN_GROW_INTO(element) ||
12418 element == EL_QUICKSAND_EMPTY ||
12419 element == EL_QUICKSAND_FAST_EMPTY ||
12420 element == EL_ACID_SPLASH_LEFT ||
12421 element == EL_ACID_SPLASH_RIGHT))
12423 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12424 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12425 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12426 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12427 Feld[x][y] = EL_AMOEBA_DROP;
12430 random = random * 129 + 1;
12436 if (game.explosions_delayed)
12439 game.explosions_delayed = FALSE;
12441 SCAN_PLAYFIELD(x, y)
12443 element = Feld[x][y];
12445 if (ExplodeField[x][y])
12446 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12447 else if (element == EL_EXPLOSION)
12448 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12450 ExplodeField[x][y] = EX_TYPE_NONE;
12453 game.explosions_delayed = TRUE;
12456 if (game.magic_wall_active)
12458 if (!(game.magic_wall_time_left % 4))
12460 int element = Feld[magic_wall_x][magic_wall_y];
12462 if (element == EL_BD_MAGIC_WALL_FULL ||
12463 element == EL_BD_MAGIC_WALL_ACTIVE ||
12464 element == EL_BD_MAGIC_WALL_EMPTYING)
12465 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12466 else if (element == EL_DC_MAGIC_WALL_FULL ||
12467 element == EL_DC_MAGIC_WALL_ACTIVE ||
12468 element == EL_DC_MAGIC_WALL_EMPTYING)
12469 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12471 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12474 if (game.magic_wall_time_left > 0)
12476 game.magic_wall_time_left--;
12478 if (!game.magic_wall_time_left)
12480 SCAN_PLAYFIELD(x, y)
12482 element = Feld[x][y];
12484 if (element == EL_MAGIC_WALL_ACTIVE ||
12485 element == EL_MAGIC_WALL_FULL)
12487 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12488 TEST_DrawLevelField(x, y);
12490 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12491 element == EL_BD_MAGIC_WALL_FULL)
12493 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12494 TEST_DrawLevelField(x, y);
12496 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12497 element == EL_DC_MAGIC_WALL_FULL)
12499 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12500 TEST_DrawLevelField(x, y);
12504 game.magic_wall_active = FALSE;
12509 if (game.light_time_left > 0)
12511 game.light_time_left--;
12513 if (game.light_time_left == 0)
12514 RedrawAllLightSwitchesAndInvisibleElements();
12517 if (game.timegate_time_left > 0)
12519 game.timegate_time_left--;
12521 if (game.timegate_time_left == 0)
12522 CloseAllOpenTimegates();
12525 if (game.lenses_time_left > 0)
12527 game.lenses_time_left--;
12529 if (game.lenses_time_left == 0)
12530 RedrawAllInvisibleElementsForLenses();
12533 if (game.magnify_time_left > 0)
12535 game.magnify_time_left--;
12537 if (game.magnify_time_left == 0)
12538 RedrawAllInvisibleElementsForMagnifier();
12541 for (i = 0; i < MAX_PLAYERS; i++)
12543 struct PlayerInfo *player = &stored_player[i];
12545 if (SHIELD_ON(player))
12547 if (player->shield_deadly_time_left)
12548 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12549 else if (player->shield_normal_time_left)
12550 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12554 #if USE_DELAYED_GFX_REDRAW
12555 SCAN_PLAYFIELD(x, y)
12558 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12560 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12561 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12564 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12565 DrawLevelField(x, y);
12567 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12568 DrawLevelFieldCrumbledSand(x, y);
12570 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12571 DrawLevelFieldCrumbledSandNeighbours(x, y);
12573 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12574 DrawTwinkleOnField(x, y);
12577 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12584 PlayAllPlayersSound();
12586 if (options.debug) /* calculate frames per second */
12588 static unsigned long fps_counter = 0;
12589 static int fps_frames = 0;
12590 unsigned long fps_delay_ms = Counter() - fps_counter;
12594 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12596 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12599 fps_counter = Counter();
12602 redraw_mask |= REDRAW_FPS;
12605 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12607 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12609 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12611 local_player->show_envelope = 0;
12615 debug_print_timestamp(0, "stop main loop profiling ");
12616 printf("----------------------------------------------------------\n");
12619 /* use random number generator in every frame to make it less predictable */
12620 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12624 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12626 int min_x = x, min_y = y, max_x = x, max_y = y;
12629 for (i = 0; i < MAX_PLAYERS; i++)
12631 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12633 if (!stored_player[i].active || &stored_player[i] == player)
12636 min_x = MIN(min_x, jx);
12637 min_y = MIN(min_y, jy);
12638 max_x = MAX(max_x, jx);
12639 max_y = MAX(max_y, jy);
12642 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12645 static boolean AllPlayersInVisibleScreen()
12649 for (i = 0; i < MAX_PLAYERS; i++)
12651 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12653 if (!stored_player[i].active)
12656 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12663 void ScrollLevel(int dx, int dy)
12666 static Bitmap *bitmap_db_field2 = NULL;
12667 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12674 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12675 /* only horizontal XOR vertical scroll direction allowed */
12676 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12681 if (bitmap_db_field2 == NULL)
12682 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12684 /* needed when blitting directly to same bitmap -- should not be needed with
12685 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12686 BlitBitmap(drawto_field, bitmap_db_field2,
12687 FX + TILEX * (dx == -1) - softscroll_offset,
12688 FY + TILEY * (dy == -1) - softscroll_offset,
12689 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12690 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12691 FX + TILEX * (dx == 1) - softscroll_offset,
12692 FY + TILEY * (dy == 1) - softscroll_offset);
12693 BlitBitmap(bitmap_db_field2, drawto_field,
12694 FX + TILEX * (dx == 1) - softscroll_offset,
12695 FY + TILEY * (dy == 1) - softscroll_offset,
12696 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12697 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12698 FX + TILEX * (dx == 1) - softscroll_offset,
12699 FY + TILEY * (dy == 1) - softscroll_offset);
12704 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12705 int xsize = (BX2 - BX1 + 1);
12706 int ysize = (BY2 - BY1 + 1);
12707 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12708 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12709 int step = (start < end ? +1 : -1);
12711 for (i = start; i != end; i += step)
12713 BlitBitmap(drawto_field, drawto_field,
12714 FX + TILEX * (dx != 0 ? i + step : 0),
12715 FY + TILEY * (dy != 0 ? i + step : 0),
12716 TILEX * (dx != 0 ? 1 : xsize),
12717 TILEY * (dy != 0 ? 1 : ysize),
12718 FX + TILEX * (dx != 0 ? i : 0),
12719 FY + TILEY * (dy != 0 ? i : 0));
12724 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12726 BlitBitmap(drawto_field, drawto_field,
12727 FX + TILEX * (dx == -1) - softscroll_offset,
12728 FY + TILEY * (dy == -1) - softscroll_offset,
12729 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12730 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12731 FX + TILEX * (dx == 1) - softscroll_offset,
12732 FY + TILEY * (dy == 1) - softscroll_offset);
12738 x = (dx == 1 ? BX1 : BX2);
12739 for (y = BY1; y <= BY2; y++)
12740 DrawScreenField(x, y);
12745 y = (dy == 1 ? BY1 : BY2);
12746 for (x = BX1; x <= BX2; x++)
12747 DrawScreenField(x, y);
12750 redraw_mask |= REDRAW_FIELD;
12753 static boolean canFallDown(struct PlayerInfo *player)
12755 int jx = player->jx, jy = player->jy;
12757 return (IN_LEV_FIELD(jx, jy + 1) &&
12758 (IS_FREE(jx, jy + 1) ||
12759 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12760 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12761 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12764 static boolean canPassField(int x, int y, int move_dir)
12766 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12767 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12768 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12769 int nextx = x + dx;
12770 int nexty = y + dy;
12771 int element = Feld[x][y];
12773 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12774 !CAN_MOVE(element) &&
12775 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12776 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12777 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12780 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12782 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12783 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12784 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12788 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12789 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12790 (IS_DIGGABLE(Feld[newx][newy]) ||
12791 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12792 canPassField(newx, newy, move_dir)));
12795 static void CheckGravityMovement(struct PlayerInfo *player)
12797 #if USE_PLAYER_GRAVITY
12798 if (player->gravity && !player->programmed_action)
12800 if (game.gravity && !player->programmed_action)
12803 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12804 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12805 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12806 int jx = player->jx, jy = player->jy;
12807 boolean player_is_moving_to_valid_field =
12808 (!player_is_snapping &&
12809 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12810 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12811 boolean player_can_fall_down = canFallDown(player);
12813 if (player_can_fall_down &&
12814 !player_is_moving_to_valid_field)
12815 player->programmed_action = MV_DOWN;
12819 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12821 return CheckGravityMovement(player);
12823 #if USE_PLAYER_GRAVITY
12824 if (player->gravity && !player->programmed_action)
12826 if (game.gravity && !player->programmed_action)
12829 int jx = player->jx, jy = player->jy;
12830 boolean field_under_player_is_free =
12831 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12832 boolean player_is_standing_on_valid_field =
12833 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12834 (IS_WALKABLE(Feld[jx][jy]) &&
12835 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12837 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12838 player->programmed_action = MV_DOWN;
12843 MovePlayerOneStep()
12844 -----------------------------------------------------------------------------
12845 dx, dy: direction (non-diagonal) to try to move the player to
12846 real_dx, real_dy: direction as read from input device (can be diagonal)
12849 boolean MovePlayerOneStep(struct PlayerInfo *player,
12850 int dx, int dy, int real_dx, int real_dy)
12852 int jx = player->jx, jy = player->jy;
12853 int new_jx = jx + dx, new_jy = jy + dy;
12854 #if !USE_FIXED_DONT_RUN_INTO
12858 boolean player_can_move = !player->cannot_move;
12860 if (!player->active || (!dx && !dy))
12861 return MP_NO_ACTION;
12863 player->MovDir = (dx < 0 ? MV_LEFT :
12864 dx > 0 ? MV_RIGHT :
12866 dy > 0 ? MV_DOWN : MV_NONE);
12868 if (!IN_LEV_FIELD(new_jx, new_jy))
12869 return MP_NO_ACTION;
12871 if (!player_can_move)
12873 if (player->MovPos == 0)
12875 player->is_moving = FALSE;
12876 player->is_digging = FALSE;
12877 player->is_collecting = FALSE;
12878 player->is_snapping = FALSE;
12879 player->is_pushing = FALSE;
12884 if (!options.network && game.centered_player_nr == -1 &&
12885 !AllPlayersInSight(player, new_jx, new_jy))
12886 return MP_NO_ACTION;
12888 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12889 return MP_NO_ACTION;
12892 #if !USE_FIXED_DONT_RUN_INTO
12893 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12895 /* (moved to DigField()) */
12896 if (player_can_move && DONT_RUN_INTO(element))
12898 if (element == EL_ACID && dx == 0 && dy == 1)
12900 SplashAcid(new_jx, new_jy);
12901 Feld[jx][jy] = EL_PLAYER_1;
12902 InitMovingField(jx, jy, MV_DOWN);
12903 Store[jx][jy] = EL_ACID;
12904 ContinueMoving(jx, jy);
12905 BuryPlayer(player);
12908 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12914 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12915 if (can_move != MP_MOVING)
12918 /* check if DigField() has caused relocation of the player */
12919 if (player->jx != jx || player->jy != jy)
12920 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12922 StorePlayer[jx][jy] = 0;
12923 player->last_jx = jx;
12924 player->last_jy = jy;
12925 player->jx = new_jx;
12926 player->jy = new_jy;
12927 StorePlayer[new_jx][new_jy] = player->element_nr;
12929 if (player->move_delay_value_next != -1)
12931 player->move_delay_value = player->move_delay_value_next;
12932 player->move_delay_value_next = -1;
12936 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12938 player->step_counter++;
12940 PlayerVisit[jx][jy] = FrameCounter;
12942 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12943 player->is_moving = TRUE;
12947 /* should better be called in MovePlayer(), but this breaks some tapes */
12948 ScrollPlayer(player, SCROLL_INIT);
12954 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12956 int jx = player->jx, jy = player->jy;
12957 int old_jx = jx, old_jy = jy;
12958 int moved = MP_NO_ACTION;
12960 if (!player->active)
12965 if (player->MovPos == 0)
12967 player->is_moving = FALSE;
12968 player->is_digging = FALSE;
12969 player->is_collecting = FALSE;
12970 player->is_snapping = FALSE;
12971 player->is_pushing = FALSE;
12977 if (player->move_delay > 0)
12980 player->move_delay = -1; /* set to "uninitialized" value */
12982 /* store if player is automatically moved to next field */
12983 player->is_auto_moving = (player->programmed_action != MV_NONE);
12985 /* remove the last programmed player action */
12986 player->programmed_action = 0;
12988 if (player->MovPos)
12990 /* should only happen if pre-1.2 tape recordings are played */
12991 /* this is only for backward compatibility */
12993 int original_move_delay_value = player->move_delay_value;
12996 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13000 /* scroll remaining steps with finest movement resolution */
13001 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13003 while (player->MovPos)
13005 ScrollPlayer(player, SCROLL_GO_ON);
13006 ScrollScreen(NULL, SCROLL_GO_ON);
13008 AdvanceFrameAndPlayerCounters(player->index_nr);
13014 player->move_delay_value = original_move_delay_value;
13017 player->is_active = FALSE;
13019 if (player->last_move_dir & MV_HORIZONTAL)
13021 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13022 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13026 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13027 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13030 #if USE_FIXED_BORDER_RUNNING_GFX
13031 if (!moved && !player->is_active)
13033 player->is_moving = FALSE;
13034 player->is_digging = FALSE;
13035 player->is_collecting = FALSE;
13036 player->is_snapping = FALSE;
13037 player->is_pushing = FALSE;
13045 if (moved & MP_MOVING && !ScreenMovPos &&
13046 (player->index_nr == game.centered_player_nr ||
13047 game.centered_player_nr == -1))
13049 if (moved & MP_MOVING && !ScreenMovPos &&
13050 (player == local_player || !options.network))
13053 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13054 int offset = game.scroll_delay_value;
13056 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13058 /* actual player has left the screen -- scroll in that direction */
13059 if (jx != old_jx) /* player has moved horizontally */
13060 scroll_x += (jx - old_jx);
13061 else /* player has moved vertically */
13062 scroll_y += (jy - old_jy);
13066 if (jx != old_jx) /* player has moved horizontally */
13068 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13069 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13070 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13072 /* don't scroll over playfield boundaries */
13073 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13074 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13076 /* don't scroll more than one field at a time */
13077 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13079 /* don't scroll against the player's moving direction */
13080 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13081 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13082 scroll_x = old_scroll_x;
13084 else /* player has moved vertically */
13086 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13087 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13088 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13090 /* don't scroll over playfield boundaries */
13091 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13092 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13094 /* don't scroll more than one field at a time */
13095 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13097 /* don't scroll against the player's moving direction */
13098 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13099 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13100 scroll_y = old_scroll_y;
13104 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13107 if (!options.network && game.centered_player_nr == -1 &&
13108 !AllPlayersInVisibleScreen())
13110 scroll_x = old_scroll_x;
13111 scroll_y = old_scroll_y;
13115 if (!options.network && !AllPlayersInVisibleScreen())
13117 scroll_x = old_scroll_x;
13118 scroll_y = old_scroll_y;
13123 ScrollScreen(player, SCROLL_INIT);
13124 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13129 player->StepFrame = 0;
13131 if (moved & MP_MOVING)
13133 if (old_jx != jx && old_jy == jy)
13134 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13135 else if (old_jx == jx && old_jy != jy)
13136 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13138 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13140 player->last_move_dir = player->MovDir;
13141 player->is_moving = TRUE;
13142 player->is_snapping = FALSE;
13143 player->is_switching = FALSE;
13144 player->is_dropping = FALSE;
13145 player->is_dropping_pressed = FALSE;
13146 player->drop_pressed_delay = 0;
13149 /* should better be called here than above, but this breaks some tapes */
13150 ScrollPlayer(player, SCROLL_INIT);
13155 CheckGravityMovementWhenNotMoving(player);
13157 player->is_moving = FALSE;
13159 /* at this point, the player is allowed to move, but cannot move right now
13160 (e.g. because of something blocking the way) -- ensure that the player
13161 is also allowed to move in the next frame (in old versions before 3.1.1,
13162 the player was forced to wait again for eight frames before next try) */
13164 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13165 player->move_delay = 0; /* allow direct movement in the next frame */
13168 if (player->move_delay == -1) /* not yet initialized by DigField() */
13169 player->move_delay = player->move_delay_value;
13171 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13173 TestIfPlayerTouchesBadThing(jx, jy);
13174 TestIfPlayerTouchesCustomElement(jx, jy);
13177 if (!player->active)
13178 RemovePlayer(player);
13183 void ScrollPlayer(struct PlayerInfo *player, int mode)
13185 int jx = player->jx, jy = player->jy;
13186 int last_jx = player->last_jx, last_jy = player->last_jy;
13187 int move_stepsize = TILEX / player->move_delay_value;
13189 #if USE_NEW_PLAYER_SPEED
13190 if (!player->active)
13193 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13196 if (!player->active || player->MovPos == 0)
13200 if (mode == SCROLL_INIT)
13202 player->actual_frame_counter = FrameCounter;
13203 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13205 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13206 Feld[last_jx][last_jy] == EL_EMPTY)
13208 int last_field_block_delay = 0; /* start with no blocking at all */
13209 int block_delay_adjustment = player->block_delay_adjustment;
13211 /* if player blocks last field, add delay for exactly one move */
13212 if (player->block_last_field)
13214 last_field_block_delay += player->move_delay_value;
13216 /* when blocking enabled, prevent moving up despite gravity */
13217 #if USE_PLAYER_GRAVITY
13218 if (player->gravity && player->MovDir == MV_UP)
13219 block_delay_adjustment = -1;
13221 if (game.gravity && player->MovDir == MV_UP)
13222 block_delay_adjustment = -1;
13226 /* add block delay adjustment (also possible when not blocking) */
13227 last_field_block_delay += block_delay_adjustment;
13229 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13230 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13233 #if USE_NEW_PLAYER_SPEED
13234 if (player->MovPos != 0) /* player has not yet reached destination */
13240 else if (!FrameReached(&player->actual_frame_counter, 1))
13243 #if USE_NEW_PLAYER_SPEED
13244 if (player->MovPos != 0)
13246 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13247 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13249 /* before DrawPlayer() to draw correct player graphic for this case */
13250 if (player->MovPos == 0)
13251 CheckGravityMovement(player);
13254 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13255 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13257 /* before DrawPlayer() to draw correct player graphic for this case */
13258 if (player->MovPos == 0)
13259 CheckGravityMovement(player);
13262 if (player->MovPos == 0) /* player reached destination field */
13264 if (player->move_delay_reset_counter > 0)
13266 player->move_delay_reset_counter--;
13268 if (player->move_delay_reset_counter == 0)
13270 /* continue with normal speed after quickly moving through gate */
13271 HALVE_PLAYER_SPEED(player);
13273 /* be able to make the next move without delay */
13274 player->move_delay = 0;
13278 player->last_jx = jx;
13279 player->last_jy = jy;
13281 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13282 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13283 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13284 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13285 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13286 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13288 DrawPlayer(player); /* needed here only to cleanup last field */
13289 RemovePlayer(player);
13291 if (local_player->friends_still_needed == 0 ||
13292 IS_SP_ELEMENT(Feld[jx][jy]))
13293 PlayerWins(player);
13296 /* this breaks one level: "machine", level 000 */
13298 int move_direction = player->MovDir;
13299 int enter_side = MV_DIR_OPPOSITE(move_direction);
13300 int leave_side = move_direction;
13301 int old_jx = last_jx;
13302 int old_jy = last_jy;
13303 int old_element = Feld[old_jx][old_jy];
13304 int new_element = Feld[jx][jy];
13306 if (IS_CUSTOM_ELEMENT(old_element))
13307 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13309 player->index_bit, leave_side);
13311 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13312 CE_PLAYER_LEAVES_X,
13313 player->index_bit, leave_side);
13315 if (IS_CUSTOM_ELEMENT(new_element))
13316 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13317 player->index_bit, enter_side);
13319 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13320 CE_PLAYER_ENTERS_X,
13321 player->index_bit, enter_side);
13323 #if USE_FIX_CE_ACTION_WITH_PLAYER
13324 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13325 CE_MOVE_OF_X, move_direction);
13327 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13328 CE_MOVE_OF_X, move_direction);
13332 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13334 TestIfPlayerTouchesBadThing(jx, jy);
13335 TestIfPlayerTouchesCustomElement(jx, jy);
13337 /* needed because pushed element has not yet reached its destination,
13338 so it would trigger a change event at its previous field location */
13339 if (!player->is_pushing)
13340 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13342 if (!player->active)
13343 RemovePlayer(player);
13346 if (!local_player->LevelSolved && level.use_step_counter)
13356 if (TimeLeft <= 10 && setup.time_limit)
13357 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13360 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13362 DisplayGameControlValues();
13364 DrawGameValue_Time(TimeLeft);
13367 if (!TimeLeft && setup.time_limit)
13368 for (i = 0; i < MAX_PLAYERS; i++)
13369 KillPlayer(&stored_player[i]);
13372 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13374 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13376 DisplayGameControlValues();
13379 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13380 DrawGameValue_Time(TimePlayed);
13384 if (tape.single_step && tape.recording && !tape.pausing &&
13385 !player->programmed_action)
13386 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13390 void ScrollScreen(struct PlayerInfo *player, int mode)
13392 static unsigned long screen_frame_counter = 0;
13394 if (mode == SCROLL_INIT)
13396 /* set scrolling step size according to actual player's moving speed */
13397 ScrollStepSize = TILEX / player->move_delay_value;
13399 screen_frame_counter = FrameCounter;
13400 ScreenMovDir = player->MovDir;
13401 ScreenMovPos = player->MovPos;
13402 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13405 else if (!FrameReached(&screen_frame_counter, 1))
13410 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13411 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13412 redraw_mask |= REDRAW_FIELD;
13415 ScreenMovDir = MV_NONE;
13418 void TestIfPlayerTouchesCustomElement(int x, int y)
13420 static int xy[4][2] =
13427 static int trigger_sides[4][2] =
13429 /* center side border side */
13430 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13431 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13432 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13433 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13435 static int touch_dir[4] =
13437 MV_LEFT | MV_RIGHT,
13442 int center_element = Feld[x][y]; /* should always be non-moving! */
13445 for (i = 0; i < NUM_DIRECTIONS; i++)
13447 int xx = x + xy[i][0];
13448 int yy = y + xy[i][1];
13449 int center_side = trigger_sides[i][0];
13450 int border_side = trigger_sides[i][1];
13451 int border_element;
13453 if (!IN_LEV_FIELD(xx, yy))
13456 if (IS_PLAYER(x, y)) /* player found at center element */
13458 struct PlayerInfo *player = PLAYERINFO(x, y);
13460 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13461 border_element = Feld[xx][yy]; /* may be moving! */
13462 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13463 border_element = Feld[xx][yy];
13464 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13465 border_element = MovingOrBlocked2Element(xx, yy);
13467 continue; /* center and border element do not touch */
13469 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13470 player->index_bit, border_side);
13471 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13472 CE_PLAYER_TOUCHES_X,
13473 player->index_bit, border_side);
13475 #if USE_FIX_CE_ACTION_WITH_PLAYER
13477 /* use player element that is initially defined in the level playfield,
13478 not the player element that corresponds to the runtime player number
13479 (example: a level that contains EL_PLAYER_3 as the only player would
13480 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13481 int player_element = PLAYERINFO(x, y)->initial_element;
13483 CheckElementChangeBySide(xx, yy, border_element, player_element,
13484 CE_TOUCHING_X, border_side);
13488 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13490 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13492 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13494 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13495 continue; /* center and border element do not touch */
13498 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13499 player->index_bit, center_side);
13500 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13501 CE_PLAYER_TOUCHES_X,
13502 player->index_bit, center_side);
13504 #if USE_FIX_CE_ACTION_WITH_PLAYER
13506 /* use player element that is initially defined in the level playfield,
13507 not the player element that corresponds to the runtime player number
13508 (example: a level that contains EL_PLAYER_3 as the only player would
13509 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13510 int player_element = PLAYERINFO(xx, yy)->initial_element;
13512 CheckElementChangeBySide(x, y, center_element, player_element,
13513 CE_TOUCHING_X, center_side);
13522 #if USE_ELEMENT_TOUCHING_BUGFIX
13524 void TestIfElementTouchesCustomElement(int x, int y)
13526 static int xy[4][2] =
13533 static int trigger_sides[4][2] =
13535 /* center side border side */
13536 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13537 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13538 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13539 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13541 static int touch_dir[4] =
13543 MV_LEFT | MV_RIGHT,
13548 boolean change_center_element = FALSE;
13549 int center_element = Feld[x][y]; /* should always be non-moving! */
13550 int border_element_old[NUM_DIRECTIONS];
13553 for (i = 0; i < NUM_DIRECTIONS; i++)
13555 int xx = x + xy[i][0];
13556 int yy = y + xy[i][1];
13557 int border_element;
13559 border_element_old[i] = -1;
13561 if (!IN_LEV_FIELD(xx, yy))
13564 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13565 border_element = Feld[xx][yy]; /* may be moving! */
13566 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13567 border_element = Feld[xx][yy];
13568 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13569 border_element = MovingOrBlocked2Element(xx, yy);
13571 continue; /* center and border element do not touch */
13573 border_element_old[i] = border_element;
13576 for (i = 0; i < NUM_DIRECTIONS; i++)
13578 int xx = x + xy[i][0];
13579 int yy = y + xy[i][1];
13580 int center_side = trigger_sides[i][0];
13581 int border_element = border_element_old[i];
13583 if (border_element == -1)
13586 /* check for change of border element */
13587 CheckElementChangeBySide(xx, yy, border_element, center_element,
13588 CE_TOUCHING_X, center_side);
13591 for (i = 0; i < NUM_DIRECTIONS; i++)
13593 int xx = x + xy[i][0];
13594 int yy = y + xy[i][1];
13595 int border_side = trigger_sides[i][1];
13596 int border_element = border_element_old[i];
13598 if (border_element == -1)
13601 /* check for change of center element (but change it only once) */
13602 if (!change_center_element)
13603 change_center_element =
13604 CheckElementChangeBySide(x, y, center_element, border_element,
13605 CE_TOUCHING_X, border_side);
13607 #if USE_FIX_CE_ACTION_WITH_PLAYER
13608 if (IS_PLAYER(xx, yy))
13610 /* use player element that is initially defined in the level playfield,
13611 not the player element that corresponds to the runtime player number
13612 (example: a level that contains EL_PLAYER_3 as the only player would
13613 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13614 int player_element = PLAYERINFO(xx, yy)->initial_element;
13616 CheckElementChangeBySide(x, y, center_element, player_element,
13617 CE_TOUCHING_X, border_side);
13625 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13627 static int xy[4][2] =
13634 static int trigger_sides[4][2] =
13636 /* center side border side */
13637 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13638 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13639 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13640 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13642 static int touch_dir[4] =
13644 MV_LEFT | MV_RIGHT,
13649 boolean change_center_element = FALSE;
13650 int center_element = Feld[x][y]; /* should always be non-moving! */
13653 for (i = 0; i < NUM_DIRECTIONS; i++)
13655 int xx = x + xy[i][0];
13656 int yy = y + xy[i][1];
13657 int center_side = trigger_sides[i][0];
13658 int border_side = trigger_sides[i][1];
13659 int border_element;
13661 if (!IN_LEV_FIELD(xx, yy))
13664 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13665 border_element = Feld[xx][yy]; /* may be moving! */
13666 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13667 border_element = Feld[xx][yy];
13668 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13669 border_element = MovingOrBlocked2Element(xx, yy);
13671 continue; /* center and border element do not touch */
13673 /* check for change of center element (but change it only once) */
13674 if (!change_center_element)
13675 change_center_element =
13676 CheckElementChangeBySide(x, y, center_element, border_element,
13677 CE_TOUCHING_X, border_side);
13679 /* check for change of border element */
13680 CheckElementChangeBySide(xx, yy, border_element, center_element,
13681 CE_TOUCHING_X, center_side);
13687 void TestIfElementHitsCustomElement(int x, int y, int direction)
13689 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13690 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13691 int hitx = x + dx, hity = y + dy;
13692 int hitting_element = Feld[x][y];
13693 int touched_element;
13695 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13698 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13699 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13701 if (IN_LEV_FIELD(hitx, hity))
13703 int opposite_direction = MV_DIR_OPPOSITE(direction);
13704 int hitting_side = direction;
13705 int touched_side = opposite_direction;
13706 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13707 MovDir[hitx][hity] != direction ||
13708 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13714 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13715 CE_HITTING_X, touched_side);
13717 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13718 CE_HIT_BY_X, hitting_side);
13720 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13721 CE_HIT_BY_SOMETHING, opposite_direction);
13723 #if USE_FIX_CE_ACTION_WITH_PLAYER
13724 if (IS_PLAYER(hitx, hity))
13726 /* use player element that is initially defined in the level playfield,
13727 not the player element that corresponds to the runtime player number
13728 (example: a level that contains EL_PLAYER_3 as the only player would
13729 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13730 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13732 CheckElementChangeBySide(x, y, hitting_element, player_element,
13733 CE_HITTING_X, touched_side);
13739 /* "hitting something" is also true when hitting the playfield border */
13740 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13741 CE_HITTING_SOMETHING, direction);
13745 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13747 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13748 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13749 int hitx = x + dx, hity = y + dy;
13750 int hitting_element = Feld[x][y];
13751 int touched_element;
13753 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13754 !IS_FREE(hitx, hity) &&
13755 (!IS_MOVING(hitx, hity) ||
13756 MovDir[hitx][hity] != direction ||
13757 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13760 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13764 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13768 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13769 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13771 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13772 EP_CAN_SMASH_EVERYTHING, direction);
13774 if (IN_LEV_FIELD(hitx, hity))
13776 int opposite_direction = MV_DIR_OPPOSITE(direction);
13777 int hitting_side = direction;
13778 int touched_side = opposite_direction;
13780 int touched_element = MovingOrBlocked2Element(hitx, hity);
13783 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13784 MovDir[hitx][hity] != direction ||
13785 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13794 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13795 CE_SMASHED_BY_SOMETHING, opposite_direction);
13797 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13798 CE_OTHER_IS_SMASHING, touched_side);
13800 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13801 CE_OTHER_GETS_SMASHED, hitting_side);
13807 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13809 int i, kill_x = -1, kill_y = -1;
13811 int bad_element = -1;
13812 static int test_xy[4][2] =
13819 static int test_dir[4] =
13827 for (i = 0; i < NUM_DIRECTIONS; i++)
13829 int test_x, test_y, test_move_dir, test_element;
13831 test_x = good_x + test_xy[i][0];
13832 test_y = good_y + test_xy[i][1];
13834 if (!IN_LEV_FIELD(test_x, test_y))
13838 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13840 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13842 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13843 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13845 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13846 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13850 bad_element = test_element;
13856 if (kill_x != -1 || kill_y != -1)
13858 if (IS_PLAYER(good_x, good_y))
13860 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13862 if (player->shield_deadly_time_left > 0 &&
13863 !IS_INDESTRUCTIBLE(bad_element))
13864 Bang(kill_x, kill_y);
13865 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13866 KillPlayer(player);
13869 Bang(good_x, good_y);
13873 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13875 int i, kill_x = -1, kill_y = -1;
13876 int bad_element = Feld[bad_x][bad_y];
13877 static int test_xy[4][2] =
13884 static int touch_dir[4] =
13886 MV_LEFT | MV_RIGHT,
13891 static int test_dir[4] =
13899 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13902 for (i = 0; i < NUM_DIRECTIONS; i++)
13904 int test_x, test_y, test_move_dir, test_element;
13906 test_x = bad_x + test_xy[i][0];
13907 test_y = bad_y + test_xy[i][1];
13908 if (!IN_LEV_FIELD(test_x, test_y))
13912 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13914 test_element = Feld[test_x][test_y];
13916 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13917 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13919 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13920 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13922 /* good thing is player or penguin that does not move away */
13923 if (IS_PLAYER(test_x, test_y))
13925 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13927 if (bad_element == EL_ROBOT && player->is_moving)
13928 continue; /* robot does not kill player if he is moving */
13930 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13932 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13933 continue; /* center and border element do not touch */
13940 else if (test_element == EL_PENGUIN)
13949 if (kill_x != -1 || kill_y != -1)
13951 if (IS_PLAYER(kill_x, kill_y))
13953 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13955 if (player->shield_deadly_time_left > 0 &&
13956 !IS_INDESTRUCTIBLE(bad_element))
13957 Bang(bad_x, bad_y);
13958 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13959 KillPlayer(player);
13962 Bang(kill_x, kill_y);
13966 void TestIfPlayerTouchesBadThing(int x, int y)
13968 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13971 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13973 TestIfGoodThingHitsBadThing(x, y, move_dir);
13976 void TestIfBadThingTouchesPlayer(int x, int y)
13978 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13981 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13983 TestIfBadThingHitsGoodThing(x, y, move_dir);
13986 void TestIfFriendTouchesBadThing(int x, int y)
13988 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13991 void TestIfBadThingTouchesFriend(int x, int y)
13993 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13996 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13998 int i, kill_x = bad_x, kill_y = bad_y;
13999 static int xy[4][2] =
14007 for (i = 0; i < NUM_DIRECTIONS; i++)
14011 x = bad_x + xy[i][0];
14012 y = bad_y + xy[i][1];
14013 if (!IN_LEV_FIELD(x, y))
14016 element = Feld[x][y];
14017 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14018 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14026 if (kill_x != bad_x || kill_y != bad_y)
14027 Bang(bad_x, bad_y);
14030 void KillPlayer(struct PlayerInfo *player)
14032 int jx = player->jx, jy = player->jy;
14034 if (!player->active)
14037 /* the following code was introduced to prevent an infinite loop when calling
14039 -> CheckTriggeredElementChangeExt()
14040 -> ExecuteCustomElementAction()
14042 -> (infinitely repeating the above sequence of function calls)
14043 which occurs when killing the player while having a CE with the setting
14044 "kill player X when explosion of <player X>"; the solution using a new
14045 field "player->killed" was chosen for backwards compatibility, although
14046 clever use of the fields "player->active" etc. would probably also work */
14048 if (player->killed)
14052 player->killed = TRUE;
14054 /* remove accessible field at the player's position */
14055 Feld[jx][jy] = EL_EMPTY;
14057 /* deactivate shield (else Bang()/Explode() would not work right) */
14058 player->shield_normal_time_left = 0;
14059 player->shield_deadly_time_left = 0;
14062 BuryPlayer(player);
14065 static void KillPlayerUnlessEnemyProtected(int x, int y)
14067 if (!PLAYER_ENEMY_PROTECTED(x, y))
14068 KillPlayer(PLAYERINFO(x, y));
14071 static void KillPlayerUnlessExplosionProtected(int x, int y)
14073 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14074 KillPlayer(PLAYERINFO(x, y));
14077 void BuryPlayer(struct PlayerInfo *player)
14079 int jx = player->jx, jy = player->jy;
14081 if (!player->active)
14084 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14085 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14087 player->GameOver = TRUE;
14088 RemovePlayer(player);
14091 void RemovePlayer(struct PlayerInfo *player)
14093 int jx = player->jx, jy = player->jy;
14094 int i, found = FALSE;
14096 player->present = FALSE;
14097 player->active = FALSE;
14099 if (!ExplodeField[jx][jy])
14100 StorePlayer[jx][jy] = 0;
14102 if (player->is_moving)
14103 TEST_DrawLevelField(player->last_jx, player->last_jy);
14105 for (i = 0; i < MAX_PLAYERS; i++)
14106 if (stored_player[i].active)
14110 AllPlayersGone = TRUE;
14116 #if USE_NEW_SNAP_DELAY
14117 static void setFieldForSnapping(int x, int y, int element, int direction)
14119 struct ElementInfo *ei = &element_info[element];
14120 int direction_bit = MV_DIR_TO_BIT(direction);
14121 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14122 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14123 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14125 Feld[x][y] = EL_ELEMENT_SNAPPING;
14126 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14128 ResetGfxAnimation(x, y);
14130 GfxElement[x][y] = element;
14131 GfxAction[x][y] = action;
14132 GfxDir[x][y] = direction;
14133 GfxFrame[x][y] = -1;
14138 =============================================================================
14139 checkDiagonalPushing()
14140 -----------------------------------------------------------------------------
14141 check if diagonal input device direction results in pushing of object
14142 (by checking if the alternative direction is walkable, diggable, ...)
14143 =============================================================================
14146 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14147 int x, int y, int real_dx, int real_dy)
14149 int jx, jy, dx, dy, xx, yy;
14151 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14154 /* diagonal direction: check alternative direction */
14159 xx = jx + (dx == 0 ? real_dx : 0);
14160 yy = jy + (dy == 0 ? real_dy : 0);
14162 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14166 =============================================================================
14168 -----------------------------------------------------------------------------
14169 x, y: field next to player (non-diagonal) to try to dig to
14170 real_dx, real_dy: direction as read from input device (can be diagonal)
14171 =============================================================================
14174 static int DigField(struct PlayerInfo *player,
14175 int oldx, int oldy, int x, int y,
14176 int real_dx, int real_dy, int mode)
14178 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14179 boolean player_was_pushing = player->is_pushing;
14180 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14181 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14182 int jx = oldx, jy = oldy;
14183 int dx = x - jx, dy = y - jy;
14184 int nextx = x + dx, nexty = y + dy;
14185 int move_direction = (dx == -1 ? MV_LEFT :
14186 dx == +1 ? MV_RIGHT :
14188 dy == +1 ? MV_DOWN : MV_NONE);
14189 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14190 int dig_side = MV_DIR_OPPOSITE(move_direction);
14191 int old_element = Feld[jx][jy];
14192 #if USE_FIXED_DONT_RUN_INTO
14193 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14199 if (is_player) /* function can also be called by EL_PENGUIN */
14201 if (player->MovPos == 0)
14203 player->is_digging = FALSE;
14204 player->is_collecting = FALSE;
14207 if (player->MovPos == 0) /* last pushing move finished */
14208 player->is_pushing = FALSE;
14210 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14212 player->is_switching = FALSE;
14213 player->push_delay = -1;
14215 return MP_NO_ACTION;
14219 #if !USE_FIXED_DONT_RUN_INTO
14220 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14221 return MP_NO_ACTION;
14224 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14225 old_element = Back[jx][jy];
14227 /* in case of element dropped at player position, check background */
14228 else if (Back[jx][jy] != EL_EMPTY &&
14229 game.engine_version >= VERSION_IDENT(2,2,0,0))
14230 old_element = Back[jx][jy];
14232 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14233 return MP_NO_ACTION; /* field has no opening in this direction */
14235 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14236 return MP_NO_ACTION; /* field has no opening in this direction */
14238 #if USE_FIXED_DONT_RUN_INTO
14239 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14243 Feld[jx][jy] = player->artwork_element;
14244 InitMovingField(jx, jy, MV_DOWN);
14245 Store[jx][jy] = EL_ACID;
14246 ContinueMoving(jx, jy);
14247 BuryPlayer(player);
14249 return MP_DONT_RUN_INTO;
14253 #if USE_FIXED_DONT_RUN_INTO
14254 if (player_can_move && DONT_RUN_INTO(element))
14256 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14258 return MP_DONT_RUN_INTO;
14262 #if USE_FIXED_DONT_RUN_INTO
14263 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14264 return MP_NO_ACTION;
14267 #if !USE_FIXED_DONT_RUN_INTO
14268 element = Feld[x][y];
14271 collect_count = element_info[element].collect_count_initial;
14273 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14274 return MP_NO_ACTION;
14276 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14277 player_can_move = player_can_move_or_snap;
14279 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14280 game.engine_version >= VERSION_IDENT(2,2,0,0))
14282 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14283 player->index_bit, dig_side);
14284 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14285 player->index_bit, dig_side);
14287 if (element == EL_DC_LANDMINE)
14290 if (Feld[x][y] != element) /* field changed by snapping */
14293 return MP_NO_ACTION;
14296 #if USE_PLAYER_GRAVITY
14297 if (player->gravity && is_player && !player->is_auto_moving &&
14298 canFallDown(player) && move_direction != MV_DOWN &&
14299 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14300 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14302 if (game.gravity && is_player && !player->is_auto_moving &&
14303 canFallDown(player) && move_direction != MV_DOWN &&
14304 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14305 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14308 if (player_can_move &&
14309 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14311 int sound_element = SND_ELEMENT(element);
14312 int sound_action = ACTION_WALKING;
14314 if (IS_RND_GATE(element))
14316 if (!player->key[RND_GATE_NR(element)])
14317 return MP_NO_ACTION;
14319 else if (IS_RND_GATE_GRAY(element))
14321 if (!player->key[RND_GATE_GRAY_NR(element)])
14322 return MP_NO_ACTION;
14324 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14326 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14327 return MP_NO_ACTION;
14329 else if (element == EL_EXIT_OPEN ||
14330 element == EL_EM_EXIT_OPEN ||
14331 element == EL_STEEL_EXIT_OPEN ||
14332 element == EL_EM_STEEL_EXIT_OPEN ||
14333 element == EL_SP_EXIT_OPEN ||
14334 element == EL_SP_EXIT_OPENING)
14336 sound_action = ACTION_PASSING; /* player is passing exit */
14338 else if (element == EL_EMPTY)
14340 sound_action = ACTION_MOVING; /* nothing to walk on */
14343 /* play sound from background or player, whatever is available */
14344 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14345 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14347 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14349 else if (player_can_move &&
14350 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14352 if (!ACCESS_FROM(element, opposite_direction))
14353 return MP_NO_ACTION; /* field not accessible from this direction */
14355 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14356 return MP_NO_ACTION;
14358 if (IS_EM_GATE(element))
14360 if (!player->key[EM_GATE_NR(element)])
14361 return MP_NO_ACTION;
14363 else if (IS_EM_GATE_GRAY(element))
14365 if (!player->key[EM_GATE_GRAY_NR(element)])
14366 return MP_NO_ACTION;
14368 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14370 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14371 return MP_NO_ACTION;
14373 else if (IS_EMC_GATE(element))
14375 if (!player->key[EMC_GATE_NR(element)])
14376 return MP_NO_ACTION;
14378 else if (IS_EMC_GATE_GRAY(element))
14380 if (!player->key[EMC_GATE_GRAY_NR(element)])
14381 return MP_NO_ACTION;
14383 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14385 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14386 return MP_NO_ACTION;
14388 else if (element == EL_DC_GATE_WHITE ||
14389 element == EL_DC_GATE_WHITE_GRAY ||
14390 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14392 if (player->num_white_keys == 0)
14393 return MP_NO_ACTION;
14395 player->num_white_keys--;
14397 else if (IS_SP_PORT(element))
14399 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14400 element == EL_SP_GRAVITY_PORT_RIGHT ||
14401 element == EL_SP_GRAVITY_PORT_UP ||
14402 element == EL_SP_GRAVITY_PORT_DOWN)
14403 #if USE_PLAYER_GRAVITY
14404 player->gravity = !player->gravity;
14406 game.gravity = !game.gravity;
14408 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14409 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14410 element == EL_SP_GRAVITY_ON_PORT_UP ||
14411 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14412 #if USE_PLAYER_GRAVITY
14413 player->gravity = TRUE;
14415 game.gravity = TRUE;
14417 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14418 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14419 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14420 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14421 #if USE_PLAYER_GRAVITY
14422 player->gravity = FALSE;
14424 game.gravity = FALSE;
14428 /* automatically move to the next field with double speed */
14429 player->programmed_action = move_direction;
14431 if (player->move_delay_reset_counter == 0)
14433 player->move_delay_reset_counter = 2; /* two double speed steps */
14435 DOUBLE_PLAYER_SPEED(player);
14438 PlayLevelSoundAction(x, y, ACTION_PASSING);
14440 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14444 if (mode != DF_SNAP)
14446 GfxElement[x][y] = GFX_ELEMENT(element);
14447 player->is_digging = TRUE;
14450 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14452 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14453 player->index_bit, dig_side);
14455 if (mode == DF_SNAP)
14457 #if USE_NEW_SNAP_DELAY
14458 if (level.block_snap_field)
14459 setFieldForSnapping(x, y, element, move_direction);
14461 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14463 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14466 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14467 player->index_bit, dig_side);
14470 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14474 if (is_player && mode != DF_SNAP)
14476 GfxElement[x][y] = element;
14477 player->is_collecting = TRUE;
14480 if (element == EL_SPEED_PILL)
14482 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14484 else if (element == EL_EXTRA_TIME && level.time > 0)
14486 TimeLeft += level.extra_time;
14489 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14491 DisplayGameControlValues();
14493 DrawGameValue_Time(TimeLeft);
14496 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14498 player->shield_normal_time_left += level.shield_normal_time;
14499 if (element == EL_SHIELD_DEADLY)
14500 player->shield_deadly_time_left += level.shield_deadly_time;
14502 else if (element == EL_DYNAMITE ||
14503 element == EL_EM_DYNAMITE ||
14504 element == EL_SP_DISK_RED)
14506 if (player->inventory_size < MAX_INVENTORY_SIZE)
14507 player->inventory_element[player->inventory_size++] = element;
14509 DrawGameDoorValues();
14511 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14513 player->dynabomb_count++;
14514 player->dynabombs_left++;
14516 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14518 player->dynabomb_size++;
14520 else if (element == EL_DYNABOMB_INCREASE_POWER)
14522 player->dynabomb_xl = TRUE;
14524 else if (IS_KEY(element))
14526 player->key[KEY_NR(element)] = TRUE;
14528 DrawGameDoorValues();
14530 else if (element == EL_DC_KEY_WHITE)
14532 player->num_white_keys++;
14534 /* display white keys? */
14535 /* DrawGameDoorValues(); */
14537 else if (IS_ENVELOPE(element))
14539 player->show_envelope = element;
14541 else if (element == EL_EMC_LENSES)
14543 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14545 RedrawAllInvisibleElementsForLenses();
14547 else if (element == EL_EMC_MAGNIFIER)
14549 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14551 RedrawAllInvisibleElementsForMagnifier();
14553 else if (IS_DROPPABLE(element) ||
14554 IS_THROWABLE(element)) /* can be collected and dropped */
14558 if (collect_count == 0)
14559 player->inventory_infinite_element = element;
14561 for (i = 0; i < collect_count; i++)
14562 if (player->inventory_size < MAX_INVENTORY_SIZE)
14563 player->inventory_element[player->inventory_size++] = element;
14565 DrawGameDoorValues();
14567 else if (collect_count > 0)
14569 local_player->gems_still_needed -= collect_count;
14570 if (local_player->gems_still_needed < 0)
14571 local_player->gems_still_needed = 0;
14574 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14576 DisplayGameControlValues();
14578 DrawGameValue_Emeralds(local_player->gems_still_needed);
14582 RaiseScoreElement(element);
14583 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14586 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14587 player->index_bit, dig_side);
14589 if (mode == DF_SNAP)
14591 #if USE_NEW_SNAP_DELAY
14592 if (level.block_snap_field)
14593 setFieldForSnapping(x, y, element, move_direction);
14595 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14597 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14600 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14601 player->index_bit, dig_side);
14604 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14606 if (mode == DF_SNAP && element != EL_BD_ROCK)
14607 return MP_NO_ACTION;
14609 if (CAN_FALL(element) && dy)
14610 return MP_NO_ACTION;
14612 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14613 !(element == EL_SPRING && level.use_spring_bug))
14614 return MP_NO_ACTION;
14616 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14617 ((move_direction & MV_VERTICAL &&
14618 ((element_info[element].move_pattern & MV_LEFT &&
14619 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14620 (element_info[element].move_pattern & MV_RIGHT &&
14621 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14622 (move_direction & MV_HORIZONTAL &&
14623 ((element_info[element].move_pattern & MV_UP &&
14624 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14625 (element_info[element].move_pattern & MV_DOWN &&
14626 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14627 return MP_NO_ACTION;
14629 /* do not push elements already moving away faster than player */
14630 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14631 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14632 return MP_NO_ACTION;
14634 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14636 if (player->push_delay_value == -1 || !player_was_pushing)
14637 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14639 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14641 if (player->push_delay_value == -1)
14642 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14644 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14646 if (!player->is_pushing)
14647 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14650 player->is_pushing = TRUE;
14651 player->is_active = TRUE;
14653 if (!(IN_LEV_FIELD(nextx, nexty) &&
14654 (IS_FREE(nextx, nexty) ||
14655 (IS_SB_ELEMENT(element) &&
14656 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14657 (IS_CUSTOM_ELEMENT(element) &&
14658 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14659 return MP_NO_ACTION;
14661 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14662 return MP_NO_ACTION;
14664 if (player->push_delay == -1) /* new pushing; restart delay */
14665 player->push_delay = 0;
14667 if (player->push_delay < player->push_delay_value &&
14668 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14669 element != EL_SPRING && element != EL_BALLOON)
14671 /* make sure that there is no move delay before next try to push */
14672 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14673 player->move_delay = 0;
14675 return MP_NO_ACTION;
14678 if (IS_CUSTOM_ELEMENT(element) &&
14679 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14681 if (!DigFieldByCE(nextx, nexty, element))
14682 return MP_NO_ACTION;
14685 if (IS_SB_ELEMENT(element))
14687 if (element == EL_SOKOBAN_FIELD_FULL)
14689 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14690 local_player->sokobanfields_still_needed++;
14693 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14695 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14696 local_player->sokobanfields_still_needed--;
14699 Feld[x][y] = EL_SOKOBAN_OBJECT;
14701 if (Back[x][y] == Back[nextx][nexty])
14702 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14703 else if (Back[x][y] != 0)
14704 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14707 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14710 if (local_player->sokobanfields_still_needed == 0 &&
14711 game.emulation == EMU_SOKOBAN)
14713 PlayerWins(player);
14715 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14719 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14721 InitMovingField(x, y, move_direction);
14722 GfxAction[x][y] = ACTION_PUSHING;
14724 if (mode == DF_SNAP)
14725 ContinueMoving(x, y);
14727 MovPos[x][y] = (dx != 0 ? dx : dy);
14729 Pushed[x][y] = TRUE;
14730 Pushed[nextx][nexty] = TRUE;
14732 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14733 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14735 player->push_delay_value = -1; /* get new value later */
14737 /* check for element change _after_ element has been pushed */
14738 if (game.use_change_when_pushing_bug)
14740 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14741 player->index_bit, dig_side);
14742 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14743 player->index_bit, dig_side);
14746 else if (IS_SWITCHABLE(element))
14748 if (PLAYER_SWITCHING(player, x, y))
14750 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14751 player->index_bit, dig_side);
14756 player->is_switching = TRUE;
14757 player->switch_x = x;
14758 player->switch_y = y;
14760 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14762 if (element == EL_ROBOT_WHEEL)
14764 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14768 game.robot_wheel_active = TRUE;
14770 TEST_DrawLevelField(x, y);
14772 else if (element == EL_SP_TERMINAL)
14776 SCAN_PLAYFIELD(xx, yy)
14778 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14780 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14781 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14784 else if (IS_BELT_SWITCH(element))
14786 ToggleBeltSwitch(x, y);
14788 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14789 element == EL_SWITCHGATE_SWITCH_DOWN ||
14790 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14791 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14793 ToggleSwitchgateSwitch(x, y);
14795 else if (element == EL_LIGHT_SWITCH ||
14796 element == EL_LIGHT_SWITCH_ACTIVE)
14798 ToggleLightSwitch(x, y);
14800 else if (element == EL_TIMEGATE_SWITCH ||
14801 element == EL_DC_TIMEGATE_SWITCH)
14803 ActivateTimegateSwitch(x, y);
14805 else if (element == EL_BALLOON_SWITCH_LEFT ||
14806 element == EL_BALLOON_SWITCH_RIGHT ||
14807 element == EL_BALLOON_SWITCH_UP ||
14808 element == EL_BALLOON_SWITCH_DOWN ||
14809 element == EL_BALLOON_SWITCH_NONE ||
14810 element == EL_BALLOON_SWITCH_ANY)
14812 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14813 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14814 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14815 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14816 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14819 else if (element == EL_LAMP)
14821 Feld[x][y] = EL_LAMP_ACTIVE;
14822 local_player->lights_still_needed--;
14824 ResetGfxAnimation(x, y);
14825 TEST_DrawLevelField(x, y);
14827 else if (element == EL_TIME_ORB_FULL)
14829 Feld[x][y] = EL_TIME_ORB_EMPTY;
14831 if (level.time > 0 || level.use_time_orb_bug)
14833 TimeLeft += level.time_orb_time;
14836 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14838 DisplayGameControlValues();
14840 DrawGameValue_Time(TimeLeft);
14844 ResetGfxAnimation(x, y);
14845 TEST_DrawLevelField(x, y);
14847 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14848 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14852 game.ball_state = !game.ball_state;
14854 SCAN_PLAYFIELD(xx, yy)
14856 int e = Feld[xx][yy];
14858 if (game.ball_state)
14860 if (e == EL_EMC_MAGIC_BALL)
14861 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14862 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14863 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14867 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14868 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14869 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14870 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14875 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14876 player->index_bit, dig_side);
14878 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14879 player->index_bit, dig_side);
14881 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14882 player->index_bit, dig_side);
14888 if (!PLAYER_SWITCHING(player, x, y))
14890 player->is_switching = TRUE;
14891 player->switch_x = x;
14892 player->switch_y = y;
14894 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14895 player->index_bit, dig_side);
14896 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14897 player->index_bit, dig_side);
14899 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14900 player->index_bit, dig_side);
14901 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14902 player->index_bit, dig_side);
14905 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14906 player->index_bit, dig_side);
14907 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14908 player->index_bit, dig_side);
14910 return MP_NO_ACTION;
14913 player->push_delay = -1;
14915 if (is_player) /* function can also be called by EL_PENGUIN */
14917 if (Feld[x][y] != element) /* really digged/collected something */
14919 player->is_collecting = !player->is_digging;
14920 player->is_active = TRUE;
14927 static boolean DigFieldByCE(int x, int y, int digging_element)
14929 int element = Feld[x][y];
14931 if (!IS_FREE(x, y))
14933 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14934 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14937 /* no element can dig solid indestructible elements */
14938 if (IS_INDESTRUCTIBLE(element) &&
14939 !IS_DIGGABLE(element) &&
14940 !IS_COLLECTIBLE(element))
14943 if (AmoebaNr[x][y] &&
14944 (element == EL_AMOEBA_FULL ||
14945 element == EL_BD_AMOEBA ||
14946 element == EL_AMOEBA_GROWING))
14948 AmoebaCnt[AmoebaNr[x][y]]--;
14949 AmoebaCnt2[AmoebaNr[x][y]]--;
14952 if (IS_MOVING(x, y))
14953 RemoveMovingField(x, y);
14957 TEST_DrawLevelField(x, y);
14960 /* if digged element was about to explode, prevent the explosion */
14961 ExplodeField[x][y] = EX_TYPE_NONE;
14963 PlayLevelSoundAction(x, y, action);
14966 Store[x][y] = EL_EMPTY;
14969 /* this makes it possible to leave the removed element again */
14970 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14971 Store[x][y] = element;
14973 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14975 int move_leave_element = element_info[digging_element].move_leave_element;
14977 /* this makes it possible to leave the removed element again */
14978 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
14979 element : move_leave_element);
14986 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14988 int jx = player->jx, jy = player->jy;
14989 int x = jx + dx, y = jy + dy;
14990 int snap_direction = (dx == -1 ? MV_LEFT :
14991 dx == +1 ? MV_RIGHT :
14993 dy == +1 ? MV_DOWN : MV_NONE);
14994 boolean can_continue_snapping = (level.continuous_snapping &&
14995 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14997 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15000 if (!player->active || !IN_LEV_FIELD(x, y))
15008 if (player->MovPos == 0)
15009 player->is_pushing = FALSE;
15011 player->is_snapping = FALSE;
15013 if (player->MovPos == 0)
15015 player->is_moving = FALSE;
15016 player->is_digging = FALSE;
15017 player->is_collecting = FALSE;
15023 #if USE_NEW_CONTINUOUS_SNAPPING
15024 /* prevent snapping with already pressed snap key when not allowed */
15025 if (player->is_snapping && !can_continue_snapping)
15028 if (player->is_snapping)
15032 player->MovDir = snap_direction;
15034 if (player->MovPos == 0)
15036 player->is_moving = FALSE;
15037 player->is_digging = FALSE;
15038 player->is_collecting = FALSE;
15041 player->is_dropping = FALSE;
15042 player->is_dropping_pressed = FALSE;
15043 player->drop_pressed_delay = 0;
15045 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15048 player->is_snapping = TRUE;
15049 player->is_active = TRUE;
15051 if (player->MovPos == 0)
15053 player->is_moving = FALSE;
15054 player->is_digging = FALSE;
15055 player->is_collecting = FALSE;
15058 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15059 TEST_DrawLevelField(player->last_jx, player->last_jy);
15061 TEST_DrawLevelField(x, y);
15066 static boolean DropElement(struct PlayerInfo *player)
15068 int old_element, new_element;
15069 int dropx = player->jx, dropy = player->jy;
15070 int drop_direction = player->MovDir;
15071 int drop_side = drop_direction;
15073 int drop_element = get_next_dropped_element(player);
15075 int drop_element = (player->inventory_size > 0 ?
15076 player->inventory_element[player->inventory_size - 1] :
15077 player->inventory_infinite_element != EL_UNDEFINED ?
15078 player->inventory_infinite_element :
15079 player->dynabombs_left > 0 ?
15080 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15084 player->is_dropping_pressed = TRUE;
15086 /* do not drop an element on top of another element; when holding drop key
15087 pressed without moving, dropped element must move away before the next
15088 element can be dropped (this is especially important if the next element
15089 is dynamite, which can be placed on background for historical reasons) */
15090 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15093 if (IS_THROWABLE(drop_element))
15095 dropx += GET_DX_FROM_DIR(drop_direction);
15096 dropy += GET_DY_FROM_DIR(drop_direction);
15098 if (!IN_LEV_FIELD(dropx, dropy))
15102 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15103 new_element = drop_element; /* default: no change when dropping */
15105 /* check if player is active, not moving and ready to drop */
15106 if (!player->active || player->MovPos || player->drop_delay > 0)
15109 /* check if player has anything that can be dropped */
15110 if (new_element == EL_UNDEFINED)
15113 /* check if drop key was pressed long enough for EM style dynamite */
15114 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15117 /* check if anything can be dropped at the current position */
15118 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15121 /* collected custom elements can only be dropped on empty fields */
15122 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15125 if (old_element != EL_EMPTY)
15126 Back[dropx][dropy] = old_element; /* store old element on this field */
15128 ResetGfxAnimation(dropx, dropy);
15129 ResetRandomAnimationValue(dropx, dropy);
15131 if (player->inventory_size > 0 ||
15132 player->inventory_infinite_element != EL_UNDEFINED)
15134 if (player->inventory_size > 0)
15136 player->inventory_size--;
15138 DrawGameDoorValues();
15140 if (new_element == EL_DYNAMITE)
15141 new_element = EL_DYNAMITE_ACTIVE;
15142 else if (new_element == EL_EM_DYNAMITE)
15143 new_element = EL_EM_DYNAMITE_ACTIVE;
15144 else if (new_element == EL_SP_DISK_RED)
15145 new_element = EL_SP_DISK_RED_ACTIVE;
15148 Feld[dropx][dropy] = new_element;
15150 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15151 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15152 el2img(Feld[dropx][dropy]), 0);
15154 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15156 /* needed if previous element just changed to "empty" in the last frame */
15157 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15159 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15160 player->index_bit, drop_side);
15161 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15163 player->index_bit, drop_side);
15165 TestIfElementTouchesCustomElement(dropx, dropy);
15167 else /* player is dropping a dyna bomb */
15169 player->dynabombs_left--;
15171 Feld[dropx][dropy] = new_element;
15173 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15174 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15175 el2img(Feld[dropx][dropy]), 0);
15177 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15180 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15181 InitField_WithBug1(dropx, dropy, FALSE);
15183 new_element = Feld[dropx][dropy]; /* element might have changed */
15185 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15186 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15188 int move_direction, nextx, nexty;
15190 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15191 MovDir[dropx][dropy] = drop_direction;
15193 move_direction = MovDir[dropx][dropy];
15194 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15195 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15197 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15199 #if USE_FIX_IMPACT_COLLISION
15200 /* do not cause impact style collision by dropping elements that can fall */
15201 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15203 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15207 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15208 player->is_dropping = TRUE;
15210 player->drop_pressed_delay = 0;
15211 player->is_dropping_pressed = FALSE;
15213 player->drop_x = dropx;
15214 player->drop_y = dropy;
15219 /* ------------------------------------------------------------------------- */
15220 /* game sound playing functions */
15221 /* ------------------------------------------------------------------------- */
15223 static int *loop_sound_frame = NULL;
15224 static int *loop_sound_volume = NULL;
15226 void InitPlayLevelSound()
15228 int num_sounds = getSoundListSize();
15230 checked_free(loop_sound_frame);
15231 checked_free(loop_sound_volume);
15233 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15234 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15237 static void PlayLevelSound(int x, int y, int nr)
15239 int sx = SCREENX(x), sy = SCREENY(y);
15240 int volume, stereo_position;
15241 int max_distance = 8;
15242 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15244 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15245 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15248 if (!IN_LEV_FIELD(x, y) ||
15249 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15250 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15253 volume = SOUND_MAX_VOLUME;
15255 if (!IN_SCR_FIELD(sx, sy))
15257 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15258 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15260 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15263 stereo_position = (SOUND_MAX_LEFT +
15264 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15265 (SCR_FIELDX + 2 * max_distance));
15267 if (IS_LOOP_SOUND(nr))
15269 /* This assures that quieter loop sounds do not overwrite louder ones,
15270 while restarting sound volume comparison with each new game frame. */
15272 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15275 loop_sound_volume[nr] = volume;
15276 loop_sound_frame[nr] = FrameCounter;
15279 PlaySoundExt(nr, volume, stereo_position, type);
15282 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15284 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15285 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15286 y < LEVELY(BY1) ? LEVELY(BY1) :
15287 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15291 static void PlayLevelSoundAction(int x, int y, int action)
15293 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15296 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15298 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15300 if (sound_effect != SND_UNDEFINED)
15301 PlayLevelSound(x, y, sound_effect);
15304 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15307 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15309 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15310 PlayLevelSound(x, y, sound_effect);
15313 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15315 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15317 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15318 PlayLevelSound(x, y, sound_effect);
15321 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15323 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15325 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15326 StopSound(sound_effect);
15329 static void PlayLevelMusic()
15331 if (levelset.music[level_nr] != MUS_UNDEFINED)
15332 PlayMusic(levelset.music[level_nr]); /* from config file */
15334 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15337 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15339 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15340 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15341 int x = xx - 1 - offset;
15342 int y = yy - 1 - offset;
15347 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15351 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15355 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15359 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15363 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15367 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15371 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15374 case SAMPLE_android_clone:
15375 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15378 case SAMPLE_android_move:
15379 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15382 case SAMPLE_spring:
15383 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15387 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15391 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15394 case SAMPLE_eater_eat:
15395 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15399 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15402 case SAMPLE_collect:
15403 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15406 case SAMPLE_diamond:
15407 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15410 case SAMPLE_squash:
15411 /* !!! CHECK THIS !!! */
15413 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15415 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15419 case SAMPLE_wonderfall:
15420 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15424 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15428 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15432 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15436 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15440 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15444 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15447 case SAMPLE_wonder:
15448 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15452 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15455 case SAMPLE_exit_open:
15456 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15459 case SAMPLE_exit_leave:
15460 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15463 case SAMPLE_dynamite:
15464 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15468 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15472 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15476 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15480 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15484 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15488 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15492 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15498 void ChangeTime(int value)
15500 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15504 /* EMC game engine uses value from time counter of RND game engine */
15505 level.native_em_level->lev->time = *time;
15507 DrawGameValue_Time(*time);
15510 void RaiseScore(int value)
15512 /* EMC game engine and RND game engine have separate score counters */
15513 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15514 &level.native_em_level->lev->score : &local_player->score);
15518 DrawGameValue_Score(*score);
15522 void RaiseScore(int value)
15524 local_player->score += value;
15527 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15529 DisplayGameControlValues();
15531 DrawGameValue_Score(local_player->score);
15535 void RaiseScoreElement(int element)
15540 case EL_BD_DIAMOND:
15541 case EL_EMERALD_YELLOW:
15542 case EL_EMERALD_RED:
15543 case EL_EMERALD_PURPLE:
15544 case EL_SP_INFOTRON:
15545 RaiseScore(level.score[SC_EMERALD]);
15548 RaiseScore(level.score[SC_DIAMOND]);
15551 RaiseScore(level.score[SC_CRYSTAL]);
15554 RaiseScore(level.score[SC_PEARL]);
15557 case EL_BD_BUTTERFLY:
15558 case EL_SP_ELECTRON:
15559 RaiseScore(level.score[SC_BUG]);
15562 case EL_BD_FIREFLY:
15563 case EL_SP_SNIKSNAK:
15564 RaiseScore(level.score[SC_SPACESHIP]);
15567 case EL_DARK_YAMYAM:
15568 RaiseScore(level.score[SC_YAMYAM]);
15571 RaiseScore(level.score[SC_ROBOT]);
15574 RaiseScore(level.score[SC_PACMAN]);
15577 RaiseScore(level.score[SC_NUT]);
15580 case EL_EM_DYNAMITE:
15581 case EL_SP_DISK_RED:
15582 case EL_DYNABOMB_INCREASE_NUMBER:
15583 case EL_DYNABOMB_INCREASE_SIZE:
15584 case EL_DYNABOMB_INCREASE_POWER:
15585 RaiseScore(level.score[SC_DYNAMITE]);
15587 case EL_SHIELD_NORMAL:
15588 case EL_SHIELD_DEADLY:
15589 RaiseScore(level.score[SC_SHIELD]);
15591 case EL_EXTRA_TIME:
15592 RaiseScore(level.extra_time_score);
15606 case EL_DC_KEY_WHITE:
15607 RaiseScore(level.score[SC_KEY]);
15610 RaiseScore(element_info[element].collect_score);
15615 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15617 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15619 #if defined(NETWORK_AVALIABLE)
15620 if (options.network)
15621 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15630 FadeSkipNextFadeIn();
15632 fading = fading_none;
15636 OpenDoor(DOOR_CLOSE_1);
15639 game_status = GAME_MODE_MAIN;
15642 DrawAndFadeInMainMenu(REDRAW_FIELD);
15650 FadeOut(REDRAW_FIELD);
15653 game_status = GAME_MODE_MAIN;
15655 DrawAndFadeInMainMenu(REDRAW_FIELD);
15659 else /* continue playing the game */
15661 if (tape.playing && tape.deactivate_display)
15662 TapeDeactivateDisplayOff(TRUE);
15664 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15666 if (tape.playing && tape.deactivate_display)
15667 TapeDeactivateDisplayOn();
15671 void RequestQuitGame(boolean ask_if_really_quit)
15673 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15674 boolean skip_request = AllPlayersGone || quick_quit;
15676 RequestQuitGameExt(skip_request, quick_quit,
15677 "Do you really want to quit the game ?");
15681 /* ------------------------------------------------------------------------- */
15682 /* random generator functions */
15683 /* ------------------------------------------------------------------------- */
15685 unsigned int InitEngineRandom_RND(long seed)
15687 game.num_random_calls = 0;
15690 unsigned int rnd_seed = InitEngineRandom(seed);
15692 printf("::: START RND: %d\n", rnd_seed);
15697 return InitEngineRandom(seed);
15703 unsigned int RND(int max)
15707 game.num_random_calls++;
15709 return GetEngineRandom(max);
15716 /* ------------------------------------------------------------------------- */
15717 /* game engine snapshot handling functions */
15718 /* ------------------------------------------------------------------------- */
15720 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15722 struct EngineSnapshotInfo
15724 /* runtime values for custom element collect score */
15725 int collect_score[NUM_CUSTOM_ELEMENTS];
15727 /* runtime values for group element choice position */
15728 int choice_pos[NUM_GROUP_ELEMENTS];
15730 /* runtime values for belt position animations */
15731 int belt_graphic[4 * NUM_BELT_PARTS];
15732 int belt_anim_mode[4 * NUM_BELT_PARTS];
15735 struct EngineSnapshotNodeInfo
15742 static struct EngineSnapshotInfo engine_snapshot_rnd;
15743 static ListNode *engine_snapshot_list = NULL;
15744 static char *snapshot_level_identifier = NULL;
15745 static int snapshot_level_nr = -1;
15747 void FreeEngineSnapshot()
15749 while (engine_snapshot_list != NULL)
15750 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15753 setString(&snapshot_level_identifier, NULL);
15754 snapshot_level_nr = -1;
15757 static void SaveEngineSnapshotValues_RND()
15759 static int belt_base_active_element[4] =
15761 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15762 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15763 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15764 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15768 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15770 int element = EL_CUSTOM_START + i;
15772 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15775 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15777 int element = EL_GROUP_START + i;
15779 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15782 for (i = 0; i < 4; i++)
15784 for (j = 0; j < NUM_BELT_PARTS; j++)
15786 int element = belt_base_active_element[i] + j;
15787 int graphic = el2img(element);
15788 int anim_mode = graphic_info[graphic].anim_mode;
15790 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15791 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15796 static void LoadEngineSnapshotValues_RND()
15798 unsigned long num_random_calls = game.num_random_calls;
15801 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15803 int element = EL_CUSTOM_START + i;
15805 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15808 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15810 int element = EL_GROUP_START + i;
15812 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15815 for (i = 0; i < 4; i++)
15817 for (j = 0; j < NUM_BELT_PARTS; j++)
15819 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15820 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15822 graphic_info[graphic].anim_mode = anim_mode;
15826 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15828 InitRND(tape.random_seed);
15829 for (i = 0; i < num_random_calls; i++)
15833 if (game.num_random_calls != num_random_calls)
15835 Error(ERR_INFO, "number of random calls out of sync");
15836 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15837 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15838 Error(ERR_EXIT, "this should not happen -- please debug");
15842 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15844 struct EngineSnapshotNodeInfo *bi =
15845 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15847 bi->buffer_orig = buffer;
15848 bi->buffer_copy = checked_malloc(size);
15851 memcpy(bi->buffer_copy, buffer, size);
15853 addNodeToList(&engine_snapshot_list, NULL, bi);
15856 void SaveEngineSnapshot()
15858 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15860 if (level_editor_test_game) /* do not save snapshots from editor */
15863 /* copy some special values to a structure better suited for the snapshot */
15865 SaveEngineSnapshotValues_RND();
15866 SaveEngineSnapshotValues_EM();
15868 /* save values stored in special snapshot structure */
15870 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15871 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15873 /* save further RND engine values */
15875 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15876 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15877 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15879 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15880 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15881 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15882 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15884 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15885 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15886 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15887 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15888 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15890 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15891 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15892 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15894 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15896 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15898 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15899 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15901 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15902 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15903 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15904 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15905 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15906 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15907 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15908 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15909 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15910 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15911 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15912 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15913 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15914 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15915 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15916 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15917 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15918 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15920 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15921 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15923 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15924 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15925 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15927 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15928 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15930 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15931 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15932 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15933 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15934 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15936 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15937 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15939 /* save level identification information */
15941 setString(&snapshot_level_identifier, leveldir_current->identifier);
15942 snapshot_level_nr = level_nr;
15945 ListNode *node = engine_snapshot_list;
15948 while (node != NULL)
15950 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15955 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15959 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15961 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15964 void LoadEngineSnapshot()
15966 ListNode *node = engine_snapshot_list;
15968 if (engine_snapshot_list == NULL)
15971 while (node != NULL)
15973 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15978 /* restore special values from snapshot structure */
15980 LoadEngineSnapshotValues_RND();
15981 LoadEngineSnapshotValues_EM();
15984 boolean CheckEngineSnapshot()
15986 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15987 snapshot_level_nr == level_nr);
15991 /* ---------- new game button stuff ---------------------------------------- */
15993 /* graphic position values for game buttons */
15994 #define GAME_BUTTON_XSIZE 30
15995 #define GAME_BUTTON_YSIZE 30
15996 #define GAME_BUTTON_XPOS 5
15997 #define GAME_BUTTON_YPOS 215
15998 #define SOUND_BUTTON_XPOS 5
15999 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16001 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16002 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16003 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16004 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16005 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16006 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16014 } gamebutton_info[NUM_GAME_BUTTONS] =
16018 &game.button.stop.x, &game.button.stop.y,
16019 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16024 &game.button.pause.x, &game.button.pause.y,
16025 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16026 GAME_CTRL_ID_PAUSE,
16030 &game.button.play.x, &game.button.play.y,
16031 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16036 &game.button.sound_music.x, &game.button.sound_music.y,
16037 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16038 SOUND_CTRL_ID_MUSIC,
16039 "background music on/off"
16042 &game.button.sound_loops.x, &game.button.sound_loops.y,
16043 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16044 SOUND_CTRL_ID_LOOPS,
16045 "sound loops on/off"
16048 &game.button.sound_simple.x,&game.button.sound_simple.y,
16049 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16050 SOUND_CTRL_ID_SIMPLE,
16051 "normal sounds on/off"
16055 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16060 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16061 GAME_CTRL_ID_PAUSE,
16065 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16070 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16071 SOUND_CTRL_ID_MUSIC,
16072 "background music on/off"
16075 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16076 SOUND_CTRL_ID_LOOPS,
16077 "sound loops on/off"
16080 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16081 SOUND_CTRL_ID_SIMPLE,
16082 "normal sounds on/off"
16087 void CreateGameButtons()
16091 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16093 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16094 struct GadgetInfo *gi;
16097 unsigned long event_mask;
16099 int gd_xoffset, gd_yoffset;
16100 int gd_x1, gd_x2, gd_y1, gd_y2;
16103 x = DX + *gamebutton_info[i].x;
16104 y = DY + *gamebutton_info[i].y;
16105 gd_xoffset = gamebutton_info[i].gd_x;
16106 gd_yoffset = gamebutton_info[i].gd_y;
16107 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16108 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16110 if (id == GAME_CTRL_ID_STOP ||
16111 id == GAME_CTRL_ID_PAUSE ||
16112 id == GAME_CTRL_ID_PLAY)
16114 button_type = GD_TYPE_NORMAL_BUTTON;
16116 event_mask = GD_EVENT_RELEASED;
16117 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16118 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16122 button_type = GD_TYPE_CHECK_BUTTON;
16124 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16125 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16126 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16127 event_mask = GD_EVENT_PRESSED;
16128 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16129 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16132 gi = CreateGadget(GDI_CUSTOM_ID, id,
16133 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16138 GDI_X, DX + gd_xoffset,
16139 GDI_Y, DY + gd_yoffset,
16141 GDI_WIDTH, GAME_BUTTON_XSIZE,
16142 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16143 GDI_TYPE, button_type,
16144 GDI_STATE, GD_BUTTON_UNPRESSED,
16145 GDI_CHECKED, checked,
16146 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16147 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16148 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16149 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16150 GDI_DIRECT_DRAW, FALSE,
16151 GDI_EVENT_MASK, event_mask,
16152 GDI_CALLBACK_ACTION, HandleGameButtons,
16156 Error(ERR_EXIT, "cannot create gadget");
16158 game_gadget[id] = gi;
16162 void FreeGameButtons()
16166 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16167 FreeGadget(game_gadget[i]);
16170 static void MapGameButtons()
16174 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16175 MapGadget(game_gadget[i]);
16178 void UnmapGameButtons()
16182 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16183 UnmapGadget(game_gadget[i]);
16186 void RedrawGameButtons()
16190 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16191 RedrawGadget(game_gadget[i]);
16194 static void HandleGameButtons(struct GadgetInfo *gi)
16196 int id = gi->custom_id;
16198 if (game_status != GAME_MODE_PLAYING)
16203 case GAME_CTRL_ID_STOP:
16207 RequestQuitGame(TRUE);
16210 case GAME_CTRL_ID_PAUSE:
16211 if (options.network)
16213 #if defined(NETWORK_AVALIABLE)
16215 SendToServer_ContinuePlaying();
16217 SendToServer_PausePlaying();
16221 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16224 case GAME_CTRL_ID_PLAY:
16227 #if defined(NETWORK_AVALIABLE)
16228 if (options.network)
16229 SendToServer_ContinuePlaying();
16233 tape.pausing = FALSE;
16234 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16239 case SOUND_CTRL_ID_MUSIC:
16240 if (setup.sound_music)
16242 setup.sound_music = FALSE;
16245 else if (audio.music_available)
16247 setup.sound = setup.sound_music = TRUE;
16249 SetAudioMode(setup.sound);
16255 case SOUND_CTRL_ID_LOOPS:
16256 if (setup.sound_loops)
16257 setup.sound_loops = FALSE;
16258 else if (audio.loops_available)
16260 setup.sound = setup.sound_loops = TRUE;
16261 SetAudioMode(setup.sound);
16265 case SOUND_CTRL_ID_SIMPLE:
16266 if (setup.sound_simple)
16267 setup.sound_simple = FALSE;
16268 else if (audio.sound_available)
16270 setup.sound = setup.sound_simple = TRUE;
16271 SetAudioMode(setup.sound);