1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* game panel display and control definitions */
124 #define GAME_PANEL_LEVEL_NUMBER 0
125 #define GAME_PANEL_GEMS 1
126 #define GAME_PANEL_INVENTORY_COUNT 2
127 #define GAME_PANEL_INVENTORY_FIRST_1 3
128 #define GAME_PANEL_INVENTORY_FIRST_2 4
129 #define GAME_PANEL_INVENTORY_FIRST_3 5
130 #define GAME_PANEL_INVENTORY_FIRST_4 6
131 #define GAME_PANEL_INVENTORY_FIRST_5 7
132 #define GAME_PANEL_INVENTORY_FIRST_6 8
133 #define GAME_PANEL_INVENTORY_FIRST_7 9
134 #define GAME_PANEL_INVENTORY_FIRST_8 10
135 #define GAME_PANEL_INVENTORY_LAST_1 11
136 #define GAME_PANEL_INVENTORY_LAST_2 12
137 #define GAME_PANEL_INVENTORY_LAST_3 13
138 #define GAME_PANEL_INVENTORY_LAST_4 14
139 #define GAME_PANEL_INVENTORY_LAST_5 15
140 #define GAME_PANEL_INVENTORY_LAST_6 16
141 #define GAME_PANEL_INVENTORY_LAST_7 17
142 #define GAME_PANEL_INVENTORY_LAST_8 18
143 #define GAME_PANEL_KEY_1 19
144 #define GAME_PANEL_KEY_2 20
145 #define GAME_PANEL_KEY_3 21
146 #define GAME_PANEL_KEY_4 22
147 #define GAME_PANEL_KEY_5 23
148 #define GAME_PANEL_KEY_6 24
149 #define GAME_PANEL_KEY_7 25
150 #define GAME_PANEL_KEY_8 26
151 #define GAME_PANEL_KEY_WHITE 27
152 #define GAME_PANEL_KEY_WHITE_COUNT 28
153 #define GAME_PANEL_SCORE 29
154 #define GAME_PANEL_HIGHSCORE 30
155 #define GAME_PANEL_TIME 31
156 #define GAME_PANEL_TIME_HH 32
157 #define GAME_PANEL_TIME_MM 33
158 #define GAME_PANEL_TIME_SS 34
159 #define GAME_PANEL_FRAME 35
160 #define GAME_PANEL_SHIELD_NORMAL 36
161 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
162 #define GAME_PANEL_SHIELD_DEADLY 38
163 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
164 #define GAME_PANEL_EXIT 40
165 #define GAME_PANEL_EMC_MAGIC_BALL 41
166 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
167 #define GAME_PANEL_LIGHT_SWITCH 43
168 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
169 #define GAME_PANEL_TIMEGATE_SWITCH 45
170 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
171 #define GAME_PANEL_SWITCHGATE_SWITCH 47
172 #define GAME_PANEL_EMC_LENSES 48
173 #define GAME_PANEL_EMC_LENSES_TIME 49
174 #define GAME_PANEL_EMC_MAGNIFIER 50
175 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
176 #define GAME_PANEL_BALLOON_SWITCH 52
177 #define GAME_PANEL_DYNABOMB_NUMBER 53
178 #define GAME_PANEL_DYNABOMB_SIZE 54
179 #define GAME_PANEL_DYNABOMB_POWER 55
180 #define GAME_PANEL_PENGUINS 56
181 #define GAME_PANEL_SOKOBAN_OBJECTS 57
182 #define GAME_PANEL_SOKOBAN_FIELDS 58
183 #define GAME_PANEL_ROBOT_WHEEL 59
184 #define GAME_PANEL_CONVEYOR_BELT_1 60
185 #define GAME_PANEL_CONVEYOR_BELT_2 61
186 #define GAME_PANEL_CONVEYOR_BELT_3 62
187 #define GAME_PANEL_CONVEYOR_BELT_4 63
188 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
189 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
190 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
191 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
192 #define GAME_PANEL_MAGIC_WALL 68
193 #define GAME_PANEL_MAGIC_WALL_TIME 69
194 #define GAME_PANEL_GRAVITY_STATE 70
195 #define GAME_PANEL_GRAPHIC_1 71
196 #define GAME_PANEL_GRAPHIC_2 72
197 #define GAME_PANEL_GRAPHIC_3 73
198 #define GAME_PANEL_GRAPHIC_4 74
199 #define GAME_PANEL_GRAPHIC_5 75
200 #define GAME_PANEL_GRAPHIC_6 76
201 #define GAME_PANEL_GRAPHIC_7 77
202 #define GAME_PANEL_GRAPHIC_8 78
203 #define GAME_PANEL_ELEMENT_1 79
204 #define GAME_PANEL_ELEMENT_2 80
205 #define GAME_PANEL_ELEMENT_3 81
206 #define GAME_PANEL_ELEMENT_4 82
207 #define GAME_PANEL_ELEMENT_5 83
208 #define GAME_PANEL_ELEMENT_6 84
209 #define GAME_PANEL_ELEMENT_7 85
210 #define GAME_PANEL_ELEMENT_8 86
211 #define GAME_PANEL_ELEMENT_COUNT_1 87
212 #define GAME_PANEL_ELEMENT_COUNT_2 88
213 #define GAME_PANEL_ELEMENT_COUNT_3 89
214 #define GAME_PANEL_ELEMENT_COUNT_4 90
215 #define GAME_PANEL_ELEMENT_COUNT_5 91
216 #define GAME_PANEL_ELEMENT_COUNT_6 92
217 #define GAME_PANEL_ELEMENT_COUNT_7 93
218 #define GAME_PANEL_ELEMENT_COUNT_8 94
219 #define GAME_PANEL_CE_SCORE_1 95
220 #define GAME_PANEL_CE_SCORE_2 96
221 #define GAME_PANEL_CE_SCORE_3 97
222 #define GAME_PANEL_CE_SCORE_4 98
223 #define GAME_PANEL_CE_SCORE_5 99
224 #define GAME_PANEL_CE_SCORE_6 100
225 #define GAME_PANEL_CE_SCORE_7 101
226 #define GAME_PANEL_CE_SCORE_8 102
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
235 #define GAME_PANEL_PLAYER_NAME 111
236 #define GAME_PANEL_LEVEL_NAME 112
237 #define GAME_PANEL_LEVEL_AUTHOR 113
239 #define NUM_GAME_PANEL_CONTROLS 114
241 struct GamePanelOrderInfo
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
249 struct GamePanelControlInfo
253 struct TextPosInfo *pos;
256 int value, last_value;
257 int frame, last_frame;
262 static struct GamePanelControlInfo game_panel_controls[] =
265 GAME_PANEL_LEVEL_NUMBER,
266 &game.panel.level_number,
275 GAME_PANEL_INVENTORY_COUNT,
276 &game.panel.inventory_count,
280 GAME_PANEL_INVENTORY_FIRST_1,
281 &game.panel.inventory_first[0],
285 GAME_PANEL_INVENTORY_FIRST_2,
286 &game.panel.inventory_first[1],
290 GAME_PANEL_INVENTORY_FIRST_3,
291 &game.panel.inventory_first[2],
295 GAME_PANEL_INVENTORY_FIRST_4,
296 &game.panel.inventory_first[3],
300 GAME_PANEL_INVENTORY_FIRST_5,
301 &game.panel.inventory_first[4],
305 GAME_PANEL_INVENTORY_FIRST_6,
306 &game.panel.inventory_first[5],
310 GAME_PANEL_INVENTORY_FIRST_7,
311 &game.panel.inventory_first[6],
315 GAME_PANEL_INVENTORY_FIRST_8,
316 &game.panel.inventory_first[7],
320 GAME_PANEL_INVENTORY_LAST_1,
321 &game.panel.inventory_last[0],
325 GAME_PANEL_INVENTORY_LAST_2,
326 &game.panel.inventory_last[1],
330 GAME_PANEL_INVENTORY_LAST_3,
331 &game.panel.inventory_last[2],
335 GAME_PANEL_INVENTORY_LAST_4,
336 &game.panel.inventory_last[3],
340 GAME_PANEL_INVENTORY_LAST_5,
341 &game.panel.inventory_last[4],
345 GAME_PANEL_INVENTORY_LAST_6,
346 &game.panel.inventory_last[5],
350 GAME_PANEL_INVENTORY_LAST_7,
351 &game.panel.inventory_last[6],
355 GAME_PANEL_INVENTORY_LAST_8,
356 &game.panel.inventory_last[7],
400 GAME_PANEL_KEY_WHITE,
401 &game.panel.key_white,
405 GAME_PANEL_KEY_WHITE_COUNT,
406 &game.panel.key_white_count,
415 GAME_PANEL_HIGHSCORE,
416 &game.panel.highscore,
445 GAME_PANEL_SHIELD_NORMAL,
446 &game.panel.shield_normal,
450 GAME_PANEL_SHIELD_NORMAL_TIME,
451 &game.panel.shield_normal_time,
455 GAME_PANEL_SHIELD_DEADLY,
456 &game.panel.shield_deadly,
460 GAME_PANEL_SHIELD_DEADLY_TIME,
461 &game.panel.shield_deadly_time,
470 GAME_PANEL_EMC_MAGIC_BALL,
471 &game.panel.emc_magic_ball,
475 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
476 &game.panel.emc_magic_ball_switch,
480 GAME_PANEL_LIGHT_SWITCH,
481 &game.panel.light_switch,
485 GAME_PANEL_LIGHT_SWITCH_TIME,
486 &game.panel.light_switch_time,
490 GAME_PANEL_TIMEGATE_SWITCH,
491 &game.panel.timegate_switch,
495 GAME_PANEL_TIMEGATE_SWITCH_TIME,
496 &game.panel.timegate_switch_time,
500 GAME_PANEL_SWITCHGATE_SWITCH,
501 &game.panel.switchgate_switch,
505 GAME_PANEL_EMC_LENSES,
506 &game.panel.emc_lenses,
510 GAME_PANEL_EMC_LENSES_TIME,
511 &game.panel.emc_lenses_time,
515 GAME_PANEL_EMC_MAGNIFIER,
516 &game.panel.emc_magnifier,
520 GAME_PANEL_EMC_MAGNIFIER_TIME,
521 &game.panel.emc_magnifier_time,
525 GAME_PANEL_BALLOON_SWITCH,
526 &game.panel.balloon_switch,
530 GAME_PANEL_DYNABOMB_NUMBER,
531 &game.panel.dynabomb_number,
535 GAME_PANEL_DYNABOMB_SIZE,
536 &game.panel.dynabomb_size,
540 GAME_PANEL_DYNABOMB_POWER,
541 &game.panel.dynabomb_power,
546 &game.panel.penguins,
550 GAME_PANEL_SOKOBAN_OBJECTS,
551 &game.panel.sokoban_objects,
555 GAME_PANEL_SOKOBAN_FIELDS,
556 &game.panel.sokoban_fields,
560 GAME_PANEL_ROBOT_WHEEL,
561 &game.panel.robot_wheel,
565 GAME_PANEL_CONVEYOR_BELT_1,
566 &game.panel.conveyor_belt[0],
570 GAME_PANEL_CONVEYOR_BELT_2,
571 &game.panel.conveyor_belt[1],
575 GAME_PANEL_CONVEYOR_BELT_3,
576 &game.panel.conveyor_belt[2],
580 GAME_PANEL_CONVEYOR_BELT_4,
581 &game.panel.conveyor_belt[3],
585 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
586 &game.panel.conveyor_belt_switch[0],
590 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
591 &game.panel.conveyor_belt_switch[1],
595 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
596 &game.panel.conveyor_belt_switch[2],
600 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
601 &game.panel.conveyor_belt_switch[3],
605 GAME_PANEL_MAGIC_WALL,
606 &game.panel.magic_wall,
610 GAME_PANEL_MAGIC_WALL_TIME,
611 &game.panel.magic_wall_time,
615 GAME_PANEL_GRAVITY_STATE,
616 &game.panel.gravity_state,
620 GAME_PANEL_GRAPHIC_1,
621 &game.panel.graphic[0],
625 GAME_PANEL_GRAPHIC_2,
626 &game.panel.graphic[1],
630 GAME_PANEL_GRAPHIC_3,
631 &game.panel.graphic[2],
635 GAME_PANEL_GRAPHIC_4,
636 &game.panel.graphic[3],
640 GAME_PANEL_GRAPHIC_5,
641 &game.panel.graphic[4],
645 GAME_PANEL_GRAPHIC_6,
646 &game.panel.graphic[5],
650 GAME_PANEL_GRAPHIC_7,
651 &game.panel.graphic[6],
655 GAME_PANEL_GRAPHIC_8,
656 &game.panel.graphic[7],
660 GAME_PANEL_ELEMENT_1,
661 &game.panel.element[0],
665 GAME_PANEL_ELEMENT_2,
666 &game.panel.element[1],
670 GAME_PANEL_ELEMENT_3,
671 &game.panel.element[2],
675 GAME_PANEL_ELEMENT_4,
676 &game.panel.element[3],
680 GAME_PANEL_ELEMENT_5,
681 &game.panel.element[4],
685 GAME_PANEL_ELEMENT_6,
686 &game.panel.element[5],
690 GAME_PANEL_ELEMENT_7,
691 &game.panel.element[6],
695 GAME_PANEL_ELEMENT_8,
696 &game.panel.element[7],
700 GAME_PANEL_ELEMENT_COUNT_1,
701 &game.panel.element_count[0],
705 GAME_PANEL_ELEMENT_COUNT_2,
706 &game.panel.element_count[1],
710 GAME_PANEL_ELEMENT_COUNT_3,
711 &game.panel.element_count[2],
715 GAME_PANEL_ELEMENT_COUNT_4,
716 &game.panel.element_count[3],
720 GAME_PANEL_ELEMENT_COUNT_5,
721 &game.panel.element_count[4],
725 GAME_PANEL_ELEMENT_COUNT_6,
726 &game.panel.element_count[5],
730 GAME_PANEL_ELEMENT_COUNT_7,
731 &game.panel.element_count[6],
735 GAME_PANEL_ELEMENT_COUNT_8,
736 &game.panel.element_count[7],
740 GAME_PANEL_CE_SCORE_1,
741 &game.panel.ce_score[0],
745 GAME_PANEL_CE_SCORE_2,
746 &game.panel.ce_score[1],
750 GAME_PANEL_CE_SCORE_3,
751 &game.panel.ce_score[2],
755 GAME_PANEL_CE_SCORE_4,
756 &game.panel.ce_score[3],
760 GAME_PANEL_CE_SCORE_5,
761 &game.panel.ce_score[4],
765 GAME_PANEL_CE_SCORE_6,
766 &game.panel.ce_score[5],
770 GAME_PANEL_CE_SCORE_7,
771 &game.panel.ce_score[6],
775 GAME_PANEL_CE_SCORE_8,
776 &game.panel.ce_score[7],
780 GAME_PANEL_CE_SCORE_1_ELEMENT,
781 &game.panel.ce_score_element[0],
785 GAME_PANEL_CE_SCORE_2_ELEMENT,
786 &game.panel.ce_score_element[1],
790 GAME_PANEL_CE_SCORE_3_ELEMENT,
791 &game.panel.ce_score_element[2],
795 GAME_PANEL_CE_SCORE_4_ELEMENT,
796 &game.panel.ce_score_element[3],
800 GAME_PANEL_CE_SCORE_5_ELEMENT,
801 &game.panel.ce_score_element[4],
805 GAME_PANEL_CE_SCORE_6_ELEMENT,
806 &game.panel.ce_score_element[5],
810 GAME_PANEL_CE_SCORE_7_ELEMENT,
811 &game.panel.ce_score_element[6],
815 GAME_PANEL_CE_SCORE_8_ELEMENT,
816 &game.panel.ce_score_element[7],
820 GAME_PANEL_PLAYER_NAME,
821 &game.panel.player_name,
825 GAME_PANEL_LEVEL_NAME,
826 &game.panel.level_name,
830 GAME_PANEL_LEVEL_AUTHOR,
831 &game.panel.level_author,
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING 3
844 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION 2
846 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF -1
850 #define INITIAL_MOVE_DELAY_ON 0
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED 32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED 4
856 #define MOVE_DELAY_MAX_SPEED 1
858 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
861 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define SOUND_CTRL_ID_MUSIC 3
1010 #define SOUND_CTRL_ID_LOOPS 4
1011 #define SOUND_CTRL_ID_SIMPLE 5
1013 #define NUM_GAME_BUTTONS 6
1016 /* forward declaration for internal use */
1018 static void CreateField(int, int, int);
1020 static void ResetGfxAnimation(int, int);
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1030 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031 static boolean DigFieldByCE(int, int, int);
1032 static boolean SnapField(struct PlayerInfo *, int, int);
1033 static boolean DropElement(struct PlayerInfo *);
1035 static void InitBeltMovement(void);
1036 static void CloseAllOpenTimegates(void);
1037 static void CheckGravityMovement(struct PlayerInfo *);
1038 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1039 static void KillPlayerUnlessEnemyProtected(int, int);
1040 static void KillPlayerUnlessExplosionProtected(int, int);
1042 static void TestIfPlayerTouchesCustomElement(int, int);
1043 static void TestIfElementTouchesCustomElement(int, int);
1044 static void TestIfElementHitsCustomElement(int, int, int);
1046 static void TestIfElementSmashesCustomElement(int, int, int);
1049 static void HandleElementChange(int, int, int);
1050 static void ExecuteCustomElementAction(int, int, int, int);
1051 static boolean ChangeElement(int, int, int, int);
1053 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1054 #define CheckTriggeredElementChange(x, y, e, ev) \
1055 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1056 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1057 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1058 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1059 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1060 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1061 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1063 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1064 #define CheckElementChange(x, y, e, te, ev) \
1065 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1066 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1068 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1069 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1071 static void PlayLevelSound(int, int, int);
1072 static void PlayLevelSoundNearest(int, int, int);
1073 static void PlayLevelSoundAction(int, int, int);
1074 static void PlayLevelSoundElementAction(int, int, int, int);
1075 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1076 static void PlayLevelSoundActionIfLoop(int, int, int);
1077 static void StopLevelSoundActionIfLoop(int, int, int);
1078 static void PlayLevelMusic();
1080 static void MapGameButtons();
1081 static void HandleGameButtons(struct GadgetInfo *);
1083 int AmoebeNachbarNr(int, int);
1084 void AmoebeUmwandeln(int, int);
1085 void ContinueMoving(int, int);
1086 void Bang(int, int);
1087 void InitMovDir(int, int);
1088 void InitAmoebaNr(int, int);
1089 int NewHiScore(void);
1091 void TestIfGoodThingHitsBadThing(int, int, int);
1092 void TestIfBadThingHitsGoodThing(int, int, int);
1093 void TestIfPlayerTouchesBadThing(int, int);
1094 void TestIfPlayerRunsIntoBadThing(int, int, int);
1095 void TestIfBadThingTouchesPlayer(int, int);
1096 void TestIfBadThingRunsIntoPlayer(int, int, int);
1097 void TestIfFriendTouchesBadThing(int, int);
1098 void TestIfBadThingTouchesFriend(int, int);
1099 void TestIfBadThingTouchesOtherBadThing(int, int);
1100 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1102 void KillPlayer(struct PlayerInfo *);
1103 void BuryPlayer(struct PlayerInfo *);
1104 void RemovePlayer(struct PlayerInfo *);
1106 static int getInvisibleActiveFromInvisibleElement(int);
1107 static int getInvisibleFromInvisibleActiveElement(int);
1109 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1111 /* for detection of endless loops, caused by custom element programming */
1112 /* (using maximal playfield width x 10 is just a rough approximation) */
1113 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1115 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1117 if (recursion_loop_detected) \
1120 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1122 recursion_loop_detected = TRUE; \
1123 recursion_loop_element = (e); \
1126 recursion_loop_depth++; \
1129 #define RECURSION_LOOP_DETECTION_END() \
1131 recursion_loop_depth--; \
1134 static int recursion_loop_depth;
1135 static boolean recursion_loop_detected;
1136 static boolean recursion_loop_element;
1138 static int map_player_action[MAX_PLAYERS];
1141 /* ------------------------------------------------------------------------- */
1142 /* definition of elements that automatically change to other elements after */
1143 /* a specified time, eventually calling a function when changing */
1144 /* ------------------------------------------------------------------------- */
1146 /* forward declaration for changer functions */
1147 static void InitBuggyBase(int, int);
1148 static void WarnBuggyBase(int, int);
1150 static void InitTrap(int, int);
1151 static void ActivateTrap(int, int);
1152 static void ChangeActiveTrap(int, int);
1154 static void InitRobotWheel(int, int);
1155 static void RunRobotWheel(int, int);
1156 static void StopRobotWheel(int, int);
1158 static void InitTimegateWheel(int, int);
1159 static void RunTimegateWheel(int, int);
1161 static void InitMagicBallDelay(int, int);
1162 static void ActivateMagicBall(int, int);
1164 struct ChangingElementInfo
1169 void (*pre_change_function)(int x, int y);
1170 void (*change_function)(int x, int y);
1171 void (*post_change_function)(int x, int y);
1174 static struct ChangingElementInfo change_delay_list[] =
1209 EL_STEEL_EXIT_OPENING,
1217 EL_STEEL_EXIT_CLOSING,
1218 EL_STEEL_EXIT_CLOSED,
1245 EL_EM_STEEL_EXIT_OPENING,
1246 EL_EM_STEEL_EXIT_OPEN,
1253 EL_EM_STEEL_EXIT_CLOSING,
1257 EL_EM_STEEL_EXIT_CLOSED,
1281 EL_SWITCHGATE_OPENING,
1289 EL_SWITCHGATE_CLOSING,
1290 EL_SWITCHGATE_CLOSED,
1297 EL_TIMEGATE_OPENING,
1305 EL_TIMEGATE_CLOSING,
1314 EL_ACID_SPLASH_LEFT,
1322 EL_ACID_SPLASH_RIGHT,
1331 EL_SP_BUGGY_BASE_ACTIVATING,
1338 EL_SP_BUGGY_BASE_ACTIVATING,
1339 EL_SP_BUGGY_BASE_ACTIVE,
1346 EL_SP_BUGGY_BASE_ACTIVE,
1370 EL_ROBOT_WHEEL_ACTIVE,
1378 EL_TIMEGATE_SWITCH_ACTIVE,
1386 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1387 EL_DC_TIMEGATE_SWITCH,
1394 EL_EMC_MAGIC_BALL_ACTIVE,
1395 EL_EMC_MAGIC_BALL_ACTIVE,
1402 EL_EMC_SPRING_BUMPER_ACTIVE,
1403 EL_EMC_SPRING_BUMPER,
1410 EL_DIAGONAL_SHRINKING,
1418 EL_DIAGONAL_GROWING,
1439 int push_delay_fixed, push_delay_random;
1443 { EL_SPRING, 0, 0 },
1444 { EL_BALLOON, 0, 0 },
1446 { EL_SOKOBAN_OBJECT, 2, 0 },
1447 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1448 { EL_SATELLITE, 2, 0 },
1449 { EL_SP_DISK_YELLOW, 2, 0 },
1451 { EL_UNDEFINED, 0, 0 },
1459 move_stepsize_list[] =
1461 { EL_AMOEBA_DROP, 2 },
1462 { EL_AMOEBA_DROPPING, 2 },
1463 { EL_QUICKSAND_FILLING, 1 },
1464 { EL_QUICKSAND_EMPTYING, 1 },
1465 { EL_QUICKSAND_FAST_FILLING, 2 },
1466 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1467 { EL_MAGIC_WALL_FILLING, 2 },
1468 { EL_MAGIC_WALL_EMPTYING, 2 },
1469 { EL_BD_MAGIC_WALL_FILLING, 2 },
1470 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_DC_MAGIC_WALL_FILLING, 2 },
1472 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_UNDEFINED, 0 },
1482 collect_count_list[] =
1485 { EL_BD_DIAMOND, 1 },
1486 { EL_EMERALD_YELLOW, 1 },
1487 { EL_EMERALD_RED, 1 },
1488 { EL_EMERALD_PURPLE, 1 },
1490 { EL_SP_INFOTRON, 1 },
1494 { EL_UNDEFINED, 0 },
1502 access_direction_list[] =
1504 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1505 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1506 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1507 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1508 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1509 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1510 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1511 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1512 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1513 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1514 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1516 { EL_SP_PORT_LEFT, MV_RIGHT },
1517 { EL_SP_PORT_RIGHT, MV_LEFT },
1518 { EL_SP_PORT_UP, MV_DOWN },
1519 { EL_SP_PORT_DOWN, MV_UP },
1520 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1521 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1522 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1524 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1525 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1526 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1527 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1528 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1529 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1530 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1531 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1532 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1533 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1534 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1536 { EL_UNDEFINED, MV_NONE }
1539 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1541 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1542 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1543 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1544 IS_JUST_CHANGING(x, y))
1546 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1548 /* static variables for playfield scan mode (scanning forward or backward) */
1549 static int playfield_scan_start_x = 0;
1550 static int playfield_scan_start_y = 0;
1551 static int playfield_scan_delta_x = 1;
1552 static int playfield_scan_delta_y = 1;
1554 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1555 (y) >= 0 && (y) <= lev_fieldy - 1; \
1556 (y) += playfield_scan_delta_y) \
1557 for ((x) = playfield_scan_start_x; \
1558 (x) >= 0 && (x) <= lev_fieldx - 1; \
1559 (x) += playfield_scan_delta_x)
1562 void DEBUG_SetMaximumDynamite()
1566 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1567 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1568 local_player->inventory_element[local_player->inventory_size++] =
1573 static void InitPlayfieldScanModeVars()
1575 if (game.use_reverse_scan_direction)
1577 playfield_scan_start_x = lev_fieldx - 1;
1578 playfield_scan_start_y = lev_fieldy - 1;
1580 playfield_scan_delta_x = -1;
1581 playfield_scan_delta_y = -1;
1585 playfield_scan_start_x = 0;
1586 playfield_scan_start_y = 0;
1588 playfield_scan_delta_x = 1;
1589 playfield_scan_delta_y = 1;
1593 static void InitPlayfieldScanMode(int mode)
1595 game.use_reverse_scan_direction =
1596 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1598 InitPlayfieldScanModeVars();
1601 static int get_move_delay_from_stepsize(int move_stepsize)
1604 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1606 /* make sure that stepsize value is always a power of 2 */
1607 move_stepsize = (1 << log_2(move_stepsize));
1609 return TILEX / move_stepsize;
1612 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615 int player_nr = player->index_nr;
1616 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1617 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1619 /* do no immediately change move delay -- the player might just be moving */
1620 player->move_delay_value_next = move_delay;
1622 /* information if player can move must be set separately */
1623 player->cannot_move = cannot_move;
1627 player->move_delay = game.initial_move_delay[player_nr];
1628 player->move_delay_value = game.initial_move_delay_value[player_nr];
1630 player->move_delay_value_next = -1;
1632 player->move_delay_reset_counter = 0;
1636 void GetPlayerConfig()
1638 GameFrameDelay = setup.game_frame_delay;
1640 if (!audio.sound_available)
1641 setup.sound_simple = FALSE;
1643 if (!audio.loops_available)
1644 setup.sound_loops = FALSE;
1646 if (!audio.music_available)
1647 setup.sound_music = FALSE;
1649 if (!video.fullscreen_available)
1650 setup.fullscreen = FALSE;
1652 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1654 SetAudioMode(setup.sound);
1658 int GetElementFromGroupElement(int element)
1660 if (IS_GROUP_ELEMENT(element))
1662 struct ElementGroupInfo *group = element_info[element].group;
1663 int last_anim_random_frame = gfx.anim_random_frame;
1666 if (group->choice_mode == ANIM_RANDOM)
1667 gfx.anim_random_frame = RND(group->num_elements_resolved);
1669 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1670 group->choice_mode, 0,
1673 if (group->choice_mode == ANIM_RANDOM)
1674 gfx.anim_random_frame = last_anim_random_frame;
1676 group->choice_pos++;
1678 element = group->element_resolved[element_pos];
1684 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 if (element == EL_SP_MURPHY)
1690 if (stored_player[0].present)
1692 Feld[x][y] = EL_SP_MURPHY_CLONE;
1698 stored_player[0].initial_element = element;
1699 stored_player[0].use_murphy = TRUE;
1701 if (!level.use_artwork_element[0])
1702 stored_player[0].artwork_element = EL_SP_MURPHY;
1705 Feld[x][y] = EL_PLAYER_1;
1711 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1712 int jx = player->jx, jy = player->jy;
1714 player->present = TRUE;
1716 player->block_last_field = (element == EL_SP_MURPHY ?
1717 level.sp_block_last_field :
1718 level.block_last_field);
1720 /* ---------- initialize player's last field block delay --------------- */
1722 /* always start with reliable default value (no adjustment needed) */
1723 player->block_delay_adjustment = 0;
1725 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1726 if (player->block_last_field && element == EL_SP_MURPHY)
1727 player->block_delay_adjustment = 1;
1729 /* special case 2: in game engines before 3.1.1, blocking was different */
1730 if (game.use_block_last_field_bug)
1731 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733 if (!options.network || player->connected)
1735 player->active = TRUE;
1737 /* remove potentially duplicate players */
1738 if (StorePlayer[jx][jy] == Feld[x][y])
1739 StorePlayer[jx][jy] = 0;
1741 StorePlayer[x][y] = Feld[x][y];
1745 printf("Player %d activated.\n", player->element_nr);
1746 printf("[Local player is %d and currently %s.]\n",
1747 local_player->element_nr,
1748 local_player->active ? "active" : "not active");
1752 Feld[x][y] = EL_EMPTY;
1754 player->jx = player->last_jx = x;
1755 player->jy = player->last_jy = y;
1758 #if USE_PLAYER_REANIMATION
1761 int player_nr = GET_PLAYER_NR(element);
1762 struct PlayerInfo *player = &stored_player[player_nr];
1764 if (player->active && player->killed)
1765 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1770 static void InitField(int x, int y, boolean init_game)
1772 int element = Feld[x][y];
1781 InitPlayerField(x, y, element, init_game);
1784 case EL_SOKOBAN_FIELD_PLAYER:
1785 element = Feld[x][y] = EL_PLAYER_1;
1786 InitField(x, y, init_game);
1788 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1789 InitField(x, y, init_game);
1792 case EL_SOKOBAN_FIELD_EMPTY:
1793 local_player->sokobanfields_still_needed++;
1797 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1798 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1799 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1800 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1801 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1802 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1803 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1804 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1805 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1806 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1815 case EL_SPACESHIP_RIGHT:
1816 case EL_SPACESHIP_UP:
1817 case EL_SPACESHIP_LEFT:
1818 case EL_SPACESHIP_DOWN:
1819 case EL_BD_BUTTERFLY:
1820 case EL_BD_BUTTERFLY_RIGHT:
1821 case EL_BD_BUTTERFLY_UP:
1822 case EL_BD_BUTTERFLY_LEFT:
1823 case EL_BD_BUTTERFLY_DOWN:
1825 case EL_BD_FIREFLY_RIGHT:
1826 case EL_BD_FIREFLY_UP:
1827 case EL_BD_FIREFLY_LEFT:
1828 case EL_BD_FIREFLY_DOWN:
1829 case EL_PACMAN_RIGHT:
1831 case EL_PACMAN_LEFT:
1832 case EL_PACMAN_DOWN:
1834 case EL_YAMYAM_LEFT:
1835 case EL_YAMYAM_RIGHT:
1837 case EL_YAMYAM_DOWN:
1838 case EL_DARK_YAMYAM:
1841 case EL_SP_SNIKSNAK:
1842 case EL_SP_ELECTRON:
1851 case EL_AMOEBA_FULL:
1856 case EL_AMOEBA_DROP:
1857 if (y == lev_fieldy - 1)
1859 Feld[x][y] = EL_AMOEBA_GROWING;
1860 Store[x][y] = EL_AMOEBA_WET;
1864 case EL_DYNAMITE_ACTIVE:
1865 case EL_SP_DISK_RED_ACTIVE:
1866 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1870 MovDelay[x][y] = 96;
1873 case EL_EM_DYNAMITE_ACTIVE:
1874 MovDelay[x][y] = 32;
1878 local_player->lights_still_needed++;
1882 local_player->friends_still_needed++;
1887 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1891 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1892 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1893 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1895 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1896 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1898 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1899 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1901 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1905 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1910 game.belt_dir[belt_nr] = belt_dir;
1911 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913 else /* more than one switch -- set it like the first switch */
1915 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1920 #if !USE_BOTH_SWITCHGATE_SWITCHES
1921 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1923 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1926 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1928 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1932 case EL_LIGHT_SWITCH_ACTIVE:
1934 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1937 case EL_INVISIBLE_STEELWALL:
1938 case EL_INVISIBLE_WALL:
1939 case EL_INVISIBLE_SAND:
1940 if (game.light_time_left > 0 ||
1941 game.lenses_time_left > 0)
1942 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1945 case EL_EMC_MAGIC_BALL:
1946 if (game.ball_state)
1947 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1950 case EL_EMC_MAGIC_BALL_SWITCH:
1951 if (game.ball_state)
1952 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1955 case EL_TRIGGER_PLAYER:
1956 case EL_TRIGGER_ELEMENT:
1957 case EL_TRIGGER_CE_VALUE:
1958 case EL_TRIGGER_CE_SCORE:
1960 case EL_ANY_ELEMENT:
1961 case EL_CURRENT_CE_VALUE:
1962 case EL_CURRENT_CE_SCORE:
1979 /* reference elements should not be used on the playfield */
1980 Feld[x][y] = EL_EMPTY;
1984 if (IS_CUSTOM_ELEMENT(element))
1986 if (CAN_MOVE(element))
1989 #if USE_NEW_CUSTOM_VALUE
1990 if (!element_info[element].use_last_ce_value || init_game)
1991 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1994 else if (IS_GROUP_ELEMENT(element))
1996 Feld[x][y] = GetElementFromGroupElement(element);
1998 InitField(x, y, init_game);
2005 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2008 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2010 InitField(x, y, init_game);
2012 /* not needed to call InitMovDir() -- already done by InitField()! */
2013 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014 CAN_MOVE(Feld[x][y]))
2018 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2020 int old_element = Feld[x][y];
2022 InitField(x, y, init_game);
2024 /* not needed to call InitMovDir() -- already done by InitField()! */
2025 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2026 CAN_MOVE(old_element) &&
2027 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2030 /* this case is in fact a combination of not less than three bugs:
2031 first, it calls InitMovDir() for elements that can move, although this is
2032 already done by InitField(); then, it checks the element that was at this
2033 field _before_ the call to InitField() (which can change it); lastly, it
2034 was not called for "mole with direction" elements, which were treated as
2035 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2039 static int get_key_element_from_nr(int key_nr)
2041 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2042 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2043 EL_EM_KEY_1 : EL_KEY_1);
2045 return key_base_element + key_nr;
2048 static int get_next_dropped_element(struct PlayerInfo *player)
2050 return (player->inventory_size > 0 ?
2051 player->inventory_element[player->inventory_size - 1] :
2052 player->inventory_infinite_element != EL_UNDEFINED ?
2053 player->inventory_infinite_element :
2054 player->dynabombs_left > 0 ?
2055 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2059 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2061 /* pos >= 0: get element from bottom of the stack;
2062 pos < 0: get element from top of the stack */
2066 int min_inventory_size = -pos;
2067 int inventory_pos = player->inventory_size - min_inventory_size;
2068 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2070 return (player->inventory_size >= min_inventory_size ?
2071 player->inventory_element[inventory_pos] :
2072 player->inventory_infinite_element != EL_UNDEFINED ?
2073 player->inventory_infinite_element :
2074 player->dynabombs_left >= min_dynabombs_left ?
2075 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2080 int min_dynabombs_left = pos + 1;
2081 int min_inventory_size = pos + 1 - player->dynabombs_left;
2082 int inventory_pos = pos - player->dynabombs_left;
2084 return (player->inventory_infinite_element != EL_UNDEFINED ?
2085 player->inventory_infinite_element :
2086 player->dynabombs_left >= min_dynabombs_left ?
2087 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 player->inventory_size >= min_inventory_size ?
2089 player->inventory_element[inventory_pos] :
2094 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2096 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2097 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2100 if (gpo1->sort_priority != gpo2->sort_priority)
2101 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2103 compare_result = gpo1->nr - gpo2->nr;
2105 return compare_result;
2108 void InitGameControlValues()
2112 for (i = 0; game_panel_controls[i].nr != -1; i++)
2114 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2115 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2116 struct TextPosInfo *pos = gpc->pos;
2118 int type = gpc->type;
2122 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2123 Error(ERR_EXIT, "this should not happen -- please debug");
2126 /* force update of game controls after initialization */
2127 gpc->value = gpc->last_value = -1;
2128 gpc->frame = gpc->last_frame = -1;
2129 gpc->gfx_frame = -1;
2131 /* determine panel value width for later calculation of alignment */
2132 if (type == TYPE_INTEGER || type == TYPE_STRING)
2134 pos->width = pos->size * getFontWidth(pos->font);
2135 pos->height = getFontHeight(pos->font);
2137 else if (type == TYPE_ELEMENT)
2139 pos->width = pos->size;
2140 pos->height = pos->size;
2143 /* fill structure for game panel draw order */
2145 gpo->sort_priority = pos->sort_priority;
2148 /* sort game panel controls according to sort_priority and control number */
2149 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2150 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2153 void UpdatePlayfieldElementCount()
2155 boolean use_element_count = FALSE;
2158 /* first check if it is needed at all to calculate playfield element count */
2159 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2160 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2161 use_element_count = TRUE;
2163 if (!use_element_count)
2166 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2167 element_info[i].element_count = 0;
2169 SCAN_PLAYFIELD(x, y)
2171 element_info[Feld[x][y]].element_count++;
2174 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2175 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2176 if (IS_IN_GROUP(j, i))
2177 element_info[EL_GROUP_START + i].element_count +=
2178 element_info[j].element_count;
2181 void UpdateGameControlValues()
2184 int time = (local_player->LevelSolved ?
2185 local_player->LevelSolved_CountingTime :
2186 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187 level.native_em_level->lev->time :
2188 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189 level.native_sp_level->game_sp->time_played :
2190 game.no_time_limit ? TimePlayed : TimeLeft);
2191 int score = (local_player->LevelSolved ?
2192 local_player->LevelSolved_CountingScore :
2193 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->score :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->score :
2197 local_player->score);
2198 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199 level.native_em_level->lev->required :
2200 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201 level.native_sp_level->game_sp->infotrons_still_needed :
2202 local_player->gems_still_needed);
2203 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204 level.native_em_level->lev->required > 0 :
2205 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2206 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2207 local_player->gems_still_needed > 0 ||
2208 local_player->sokobanfields_still_needed > 0 ||
2209 local_player->lights_still_needed > 0);
2211 UpdatePlayfieldElementCount();
2213 /* update game panel control values */
2215 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2218 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219 for (i = 0; i < MAX_NUM_KEYS; i++)
2220 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2224 if (game.centered_player_nr == -1)
2226 for (i = 0; i < MAX_PLAYERS; i++)
2228 /* only one player in Supaplex game engine */
2229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2232 for (k = 0; k < MAX_NUM_KEYS; k++)
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2236 if (level.native_em_level->ply[i]->keys & (1 << k))
2237 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 get_key_element_from_nr(k);
2240 else if (stored_player[i].key[k])
2241 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 get_key_element_from_nr(k);
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2247 level.native_em_level->ply[i]->dynamite;
2248 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2249 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250 level.native_sp_level->game_sp->red_disk_count;
2252 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253 stored_player[i].inventory_size;
2255 if (stored_player[i].num_white_keys > 0)
2256 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2259 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260 stored_player[i].num_white_keys;
2265 int player_nr = game.centered_player_nr;
2267 for (k = 0; k < MAX_NUM_KEYS; k++)
2269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2271 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273 get_key_element_from_nr(k);
2275 else if (stored_player[player_nr].key[k])
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2280 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282 level.native_em_level->ply[player_nr]->dynamite;
2283 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 level.native_sp_level->game_sp->red_disk_count;
2287 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288 stored_player[player_nr].inventory_size;
2290 if (stored_player[player_nr].num_white_keys > 0)
2291 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2293 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294 stored_player[player_nr].num_white_keys;
2297 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2299 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300 get_inventory_element_from_pos(local_player, i);
2301 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302 get_inventory_element_from_pos(local_player, -i - 1);
2305 game_panel_controls[GAME_PANEL_SCORE].value = score;
2306 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2308 game_panel_controls[GAME_PANEL_TIME].value = time;
2310 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2314 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2316 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2317 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2319 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2320 local_player->shield_normal_time_left;
2321 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2322 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2325 local_player->shield_deadly_time_left;
2327 game_panel_controls[GAME_PANEL_EXIT].value =
2328 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2330 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2331 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2332 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2333 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2334 EL_EMC_MAGIC_BALL_SWITCH);
2336 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2337 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2338 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2339 game.light_time_left;
2341 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2342 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2343 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2344 game.timegate_time_left;
2346 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2347 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2349 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2350 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2351 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2352 game.lenses_time_left;
2354 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2355 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2357 game.magnify_time_left;
2359 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2360 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2361 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2362 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2363 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2364 EL_BALLOON_SWITCH_NONE);
2366 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2367 local_player->dynabomb_count;
2368 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2369 local_player->dynabomb_size;
2370 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2371 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2373 game_panel_controls[GAME_PANEL_PENGUINS].value =
2374 local_player->friends_still_needed;
2376 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2377 local_player->sokobanfields_still_needed;
2378 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2379 local_player->sokobanfields_still_needed;
2381 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2382 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2384 for (i = 0; i < NUM_BELTS; i++)
2386 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2387 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2388 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2389 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2390 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2393 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2394 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2395 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2396 game.magic_wall_time_left;
2398 #if USE_PLAYER_GRAVITY
2399 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2400 local_player->gravity;
2402 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2405 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2406 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2410 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2411 game.panel.element[i].id : EL_UNDEFINED);
2413 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2414 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2415 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2416 element_info[game.panel.element_count[i].id].element_count : 0);
2418 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2420 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2421 element_info[game.panel.ce_score[i].id].collect_score : 0);
2423 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2424 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2425 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2426 element_info[game.panel.ce_score_element[i].id].collect_score :
2429 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2430 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433 /* update game panel control frames */
2435 for (i = 0; game_panel_controls[i].nr != -1; i++)
2437 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439 if (gpc->type == TYPE_ELEMENT)
2441 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443 int last_anim_random_frame = gfx.anim_random_frame;
2444 int element = gpc->value;
2445 int graphic = el2panelimg(element);
2447 if (gpc->value != gpc->last_value)
2450 gpc->gfx_random = INIT_GFX_RANDOM();
2456 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2457 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2458 gpc->gfx_random = INIT_GFX_RANDOM();
2461 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2462 gfx.anim_random_frame = gpc->gfx_random;
2464 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2465 gpc->gfx_frame = element_info[element].collect_score;
2467 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2470 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471 gfx.anim_random_frame = last_anim_random_frame;
2477 void DisplayGameControlValues()
2479 boolean redraw_panel = FALSE;
2482 for (i = 0; game_panel_controls[i].nr != -1; i++)
2484 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2486 if (PANEL_DEACTIVATED(gpc->pos))
2489 if (gpc->value == gpc->last_value &&
2490 gpc->frame == gpc->last_frame)
2493 redraw_panel = TRUE;
2499 /* copy default game door content to main double buffer */
2501 /* !!! CHECK AGAIN !!! */
2502 SetPanelBackground();
2503 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2504 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2506 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2507 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2510 /* redraw game control buttons */
2512 RedrawGameButtons();
2518 game_status = GAME_MODE_PSEUDO_PANEL;
2521 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2523 for (i = 0; game_panel_controls[i].nr != -1; i++)
2527 int nr = game_panel_order[i].nr;
2528 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2530 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533 struct TextPosInfo *pos = gpc->pos;
2534 int type = gpc->type;
2535 int value = gpc->value;
2536 int frame = gpc->frame;
2538 int last_value = gpc->last_value;
2539 int last_frame = gpc->last_frame;
2541 int size = pos->size;
2542 int font = pos->font;
2543 boolean draw_masked = pos->draw_masked;
2544 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2546 if (PANEL_DEACTIVATED(pos))
2550 if (value == last_value && frame == last_frame)
2554 gpc->last_value = value;
2555 gpc->last_frame = frame;
2558 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2580 /* clear background if value just changed its size (dynamic digits) */
2581 if ((last_value < value_change) != (value < value_change))
2583 int width1 = size1 * getFontWidth(font1);
2584 int width2 = size2 * getFontWidth(font2);
2585 int max_width = MAX(width1, width2);
2586 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2588 pos->width = max_width;
2590 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591 max_width, max_height);
2598 /* correct text size if "digits" is zero or less */
2600 size = strlen(int2str(value, size));
2602 /* dynamically correct text alignment */
2603 pos->width = size * getFontWidth(font);
2606 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2607 int2str(value, size), font, mask_mode);
2609 else if (type == TYPE_ELEMENT)
2611 int element, graphic;
2615 int dst_x = PANEL_XPOS(pos);
2616 int dst_y = PANEL_YPOS(pos);
2619 if (value != EL_UNDEFINED && value != EL_EMPTY)
2622 graphic = el2panelimg(value);
2624 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2627 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2631 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2634 width = graphic_info[graphic].width * size / TILESIZE;
2635 height = graphic_info[graphic].height * size / TILESIZE;
2639 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2640 dst_x - src_x, dst_y - src_y);
2641 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2646 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2651 if (value == EL_UNDEFINED || value == EL_EMPTY)
2653 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2654 graphic = el2panelimg(element);
2656 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2657 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2658 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2663 graphic = el2panelimg(value);
2665 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2668 width = graphic_info[graphic].width * size / TILESIZE;
2669 height = graphic_info[graphic].height * size / TILESIZE;
2671 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2674 else if (type == TYPE_STRING)
2676 boolean active = (value != 0);
2677 char *state_normal = "off";
2678 char *state_active = "on";
2679 char *state = (active ? state_active : state_normal);
2680 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2681 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2682 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2683 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2685 if (nr == GAME_PANEL_GRAVITY_STATE)
2687 int font1 = pos->font; /* (used for normal state) */
2688 int font2 = pos->font_alt; /* (used for active state) */
2690 int size1 = strlen(state_normal);
2691 int size2 = strlen(state_active);
2692 int width1 = size1 * getFontWidth(font1);
2693 int width2 = size2 * getFontWidth(font2);
2694 int max_width = MAX(width1, width2);
2695 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2697 pos->width = max_width;
2699 /* clear background for values that may have changed its size */
2700 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701 max_width, max_height);
2704 font = (active ? font2 : font1);
2714 /* don't truncate output if "chars" is zero or less */
2717 /* dynamically correct text alignment */
2718 pos->width = size * getFontWidth(font);
2722 s_cut = getStringCopyN(s, size);
2724 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2725 s_cut, font, mask_mode);
2731 redraw_mask |= REDRAW_DOOR_1;
2734 game_status = GAME_MODE_PLAYING;
2737 void UpdateAndDisplayGameControlValues()
2739 if (tape.warp_forward)
2742 UpdateGameControlValues();
2743 DisplayGameControlValues();
2746 void DrawGameValue_Emeralds(int value)
2748 struct TextPosInfo *pos = &game.panel.gems;
2749 int font_nr = pos->font;
2750 int font_width = getFontWidth(font_nr);
2751 int chars = pos->size;
2754 return; /* !!! USE NEW STUFF !!! */
2757 if (PANEL_DEACTIVATED(pos))
2760 pos->width = chars * font_width;
2762 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2765 void DrawGameValue_Dynamite(int value)
2767 struct TextPosInfo *pos = &game.panel.inventory_count;
2768 int font_nr = pos->font;
2769 int font_width = getFontWidth(font_nr);
2770 int chars = pos->size;
2773 return; /* !!! USE NEW STUFF !!! */
2776 if (PANEL_DEACTIVATED(pos))
2779 pos->width = chars * font_width;
2781 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2784 void DrawGameValue_Score(int value)
2786 struct TextPosInfo *pos = &game.panel.score;
2787 int font_nr = pos->font;
2788 int font_width = getFontWidth(font_nr);
2789 int chars = pos->size;
2792 return; /* !!! USE NEW STUFF !!! */
2795 if (PANEL_DEACTIVATED(pos))
2798 pos->width = chars * font_width;
2800 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2803 void DrawGameValue_Time(int value)
2805 struct TextPosInfo *pos = &game.panel.time;
2806 static int last_value = -1;
2809 int chars = pos->size;
2810 int font1_nr = pos->font;
2811 int font2_nr = pos->font_alt;
2812 int font_nr = font1_nr;
2813 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2816 return; /* !!! USE NEW STUFF !!! */
2819 if (PANEL_DEACTIVATED(pos))
2822 if (use_dynamic_chars) /* use dynamic number of chars */
2824 chars = (value < 1000 ? chars1 : chars2);
2825 font_nr = (value < 1000 ? font1_nr : font2_nr);
2828 /* clear background if value just changed its size (dynamic chars only) */
2829 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2831 int width1 = chars1 * getFontWidth(font1_nr);
2832 int width2 = chars2 * getFontWidth(font2_nr);
2833 int max_width = MAX(width1, width2);
2834 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2836 pos->width = max_width;
2838 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2839 max_width, max_height);
2842 pos->width = chars * getFontWidth(font_nr);
2844 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2849 void DrawGameValue_Level(int value)
2851 struct TextPosInfo *pos = &game.panel.level_number;
2854 int chars = pos->size;
2855 int font1_nr = pos->font;
2856 int font2_nr = pos->font_alt;
2857 int font_nr = font1_nr;
2858 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2861 return; /* !!! USE NEW STUFF !!! */
2864 if (PANEL_DEACTIVATED(pos))
2867 if (use_dynamic_chars) /* use dynamic number of chars */
2869 chars = (level_nr < 100 ? chars1 : chars2);
2870 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2873 pos->width = chars * getFontWidth(font_nr);
2875 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2878 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2883 return; /* !!! USE NEW STUFF !!! */
2886 for (i = 0; i < MAX_NUM_KEYS; i++)
2888 struct TextPosInfo *pos = &game.panel.key[i];
2889 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2890 int src_y = DOOR_GFX_PAGEY1 + 123;
2891 int dst_x = PANEL_XPOS(pos);
2892 int dst_y = PANEL_YPOS(pos);
2894 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2895 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2897 int graphic = el2edimg(element);
2899 if (PANEL_DEACTIVATED(pos))
2903 /* masked blit with tiles from half-size scaled bitmap does not work yet
2904 (no mask bitmap created for these sizes after loading and scaling) --
2905 solution: load without creating mask, scale, then create final mask */
2907 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2915 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2917 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2918 dst_x - src_x, dst_y - src_y);
2919 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2924 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2926 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2927 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2932 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2935 int key[MAX_NUM_KEYS];
2938 /* prevent EM engine from updating time/score values parallel to GameWon() */
2939 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2940 local_player->LevelSolved)
2943 for (i = 0; i < MAX_NUM_KEYS; i++)
2944 key[i] = key_bits & (1 << i);
2946 DrawGameValue_Level(level_nr);
2948 DrawGameValue_Emeralds(emeralds);
2949 DrawGameValue_Dynamite(dynamite);
2950 DrawGameValue_Score(score);
2951 DrawGameValue_Time(time);
2953 DrawGameValue_Keys(key);
2956 void UpdateGameDoorValues()
2958 UpdateGameControlValues();
2961 void DrawGameDoorValues()
2963 DisplayGameControlValues();
2966 void DrawGameDoorValues_OLD()
2968 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2969 int dynamite_value = 0;
2970 int score_value = (local_player->LevelSolved ? local_player->score_final :
2971 local_player->score);
2972 int gems_value = local_player->gems_still_needed;
2976 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2978 DrawGameDoorValues_EM();
2983 if (game.centered_player_nr == -1)
2985 for (i = 0; i < MAX_PLAYERS; i++)
2987 for (j = 0; j < MAX_NUM_KEYS; j++)
2988 if (stored_player[i].key[j])
2989 key_bits |= (1 << j);
2991 dynamite_value += stored_player[i].inventory_size;
2996 int player_nr = game.centered_player_nr;
2998 for (i = 0; i < MAX_NUM_KEYS; i++)
2999 if (stored_player[player_nr].key[i])
3000 key_bits |= (1 << i);
3002 dynamite_value = stored_player[player_nr].inventory_size;
3005 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3011 =============================================================================
3013 -----------------------------------------------------------------------------
3014 initialize game engine due to level / tape version number
3015 =============================================================================
3018 static void InitGameEngine()
3020 int i, j, k, l, x, y;
3022 /* set game engine from tape file when re-playing, else from level file */
3023 game.engine_version = (tape.playing ? tape.engine_version :
3024 level.game_version);
3026 /* ---------------------------------------------------------------------- */
3027 /* set flags for bugs and changes according to active game engine version */
3028 /* ---------------------------------------------------------------------- */
3031 Summary of bugfix/change:
3032 Fixed handling for custom elements that change when pushed by the player.
3034 Fixed/changed in version:
3038 Before 3.1.0, custom elements that "change when pushing" changed directly
3039 after the player started pushing them (until then handled in "DigField()").
3040 Since 3.1.0, these custom elements are not changed until the "pushing"
3041 move of the element is finished (now handled in "ContinueMoving()").
3043 Affected levels/tapes:
3044 The first condition is generally needed for all levels/tapes before version
3045 3.1.0, which might use the old behaviour before it was changed; known tapes
3046 that are affected are some tapes from the level set "Walpurgis Gardens" by
3048 The second condition is an exception from the above case and is needed for
3049 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3050 above (including some development versions of 3.1.0), but before it was
3051 known that this change would break tapes like the above and was fixed in
3052 3.1.1, so that the changed behaviour was active although the engine version
3053 while recording maybe was before 3.1.0. There is at least one tape that is
3054 affected by this exception, which is the tape for the one-level set "Bug
3055 Machine" by Juergen Bonhagen.
3058 game.use_change_when_pushing_bug =
3059 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3062 tape.game_version < VERSION_IDENT(3,1,1,0)));
3065 Summary of bugfix/change:
3066 Fixed handling for blocking the field the player leaves when moving.
3068 Fixed/changed in version:
3072 Before 3.1.1, when "block last field when moving" was enabled, the field
3073 the player is leaving when moving was blocked for the time of the move,
3074 and was directly unblocked afterwards. This resulted in the last field
3075 being blocked for exactly one less than the number of frames of one player
3076 move. Additionally, even when blocking was disabled, the last field was
3077 blocked for exactly one frame.
3078 Since 3.1.1, due to changes in player movement handling, the last field
3079 is not blocked at all when blocking is disabled. When blocking is enabled,
3080 the last field is blocked for exactly the number of frames of one player
3081 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3082 last field is blocked for exactly one more than the number of frames of
3085 Affected levels/tapes:
3086 (!!! yet to be determined -- probably many !!!)
3089 game.use_block_last_field_bug =
3090 (game.engine_version < VERSION_IDENT(3,1,1,0));
3093 Summary of bugfix/change:
3094 Changed behaviour of CE changes with multiple changes per single frame.
3096 Fixed/changed in version:
3100 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3101 This resulted in race conditions where CEs seem to behave strange in some
3102 situations (where triggered CE changes were just skipped because there was
3103 already a CE change on that tile in the playfield in that engine frame).
3104 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3105 (The number of changes per frame must be limited in any case, because else
3106 it is easily possible to define CE changes that would result in an infinite
3107 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3108 should be set large enough so that it would only be reached in cases where
3109 the corresponding CE change conditions run into a loop. Therefore, it seems
3110 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3111 maximal number of change pages for custom elements.)
3113 Affected levels/tapes:
3117 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3118 game.max_num_changes_per_frame = 1;
3120 game.max_num_changes_per_frame =
3121 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3124 /* ---------------------------------------------------------------------- */
3126 /* default scan direction: scan playfield from top/left to bottom/right */
3127 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3129 /* dynamically adjust element properties according to game engine version */
3130 InitElementPropertiesEngine(game.engine_version);
3133 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3134 printf(" tape version == %06d [%s] [file: %06d]\n",
3135 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3137 printf(" => game.engine_version == %06d\n", game.engine_version);
3140 /* ---------- initialize player's initial move delay --------------------- */
3142 /* dynamically adjust player properties according to level information */
3143 for (i = 0; i < MAX_PLAYERS; i++)
3144 game.initial_move_delay_value[i] =
3145 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3147 /* dynamically adjust player properties according to game engine version */
3148 for (i = 0; i < MAX_PLAYERS; i++)
3149 game.initial_move_delay[i] =
3150 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3151 game.initial_move_delay_value[i] : 0);
3153 /* ---------- initialize player's initial push delay --------------------- */
3155 /* dynamically adjust player properties according to game engine version */
3156 game.initial_push_delay_value =
3157 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3159 /* ---------- initialize changing elements ------------------------------- */
3161 /* initialize changing elements information */
3162 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3164 struct ElementInfo *ei = &element_info[i];
3166 /* this pointer might have been changed in the level editor */
3167 ei->change = &ei->change_page[0];
3169 if (!IS_CUSTOM_ELEMENT(i))
3171 ei->change->target_element = EL_EMPTY_SPACE;
3172 ei->change->delay_fixed = 0;
3173 ei->change->delay_random = 0;
3174 ei->change->delay_frames = 1;
3177 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3179 ei->has_change_event[j] = FALSE;
3181 ei->event_page_nr[j] = 0;
3182 ei->event_page[j] = &ei->change_page[0];
3186 /* add changing elements from pre-defined list */
3187 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3189 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3190 struct ElementInfo *ei = &element_info[ch_delay->element];
3192 ei->change->target_element = ch_delay->target_element;
3193 ei->change->delay_fixed = ch_delay->change_delay;
3195 ei->change->pre_change_function = ch_delay->pre_change_function;
3196 ei->change->change_function = ch_delay->change_function;
3197 ei->change->post_change_function = ch_delay->post_change_function;
3199 ei->change->can_change = TRUE;
3200 ei->change->can_change_or_has_action = TRUE;
3202 ei->has_change_event[CE_DELAY] = TRUE;
3204 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3205 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3208 /* ---------- initialize internal run-time variables --------------------- */
3210 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214 for (j = 0; j < ei->num_change_pages; j++)
3216 ei->change_page[j].can_change_or_has_action =
3217 (ei->change_page[j].can_change |
3218 ei->change_page[j].has_action);
3222 /* add change events from custom element configuration */
3223 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227 for (j = 0; j < ei->num_change_pages; j++)
3229 if (!ei->change_page[j].can_change_or_has_action)
3232 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234 /* only add event page for the first page found with this event */
3235 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237 ei->has_change_event[k] = TRUE;
3239 ei->event_page_nr[k] = j;
3240 ei->event_page[k] = &ei->change_page[j];
3247 /* ---------- initialize reference elements in change conditions --------- */
3249 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3251 int element = EL_CUSTOM_START + i;
3252 struct ElementInfo *ei = &element_info[element];
3254 for (j = 0; j < ei->num_change_pages; j++)
3256 int trigger_element = ei->change_page[j].initial_trigger_element;
3258 if (trigger_element >= EL_PREV_CE_8 &&
3259 trigger_element <= EL_NEXT_CE_8)
3260 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3262 ei->change_page[j].trigger_element = trigger_element;
3267 /* ---------- initialize run-time trigger player and element ------------- */
3269 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3271 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3273 for (j = 0; j < ei->num_change_pages; j++)
3275 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3276 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3277 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3278 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3279 ei->change_page[j].actual_trigger_ce_value = 0;
3280 ei->change_page[j].actual_trigger_ce_score = 0;
3284 /* ---------- initialize trigger events ---------------------------------- */
3286 /* initialize trigger events information */
3287 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3288 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3289 trigger_events[i][j] = FALSE;
3291 /* add trigger events from element change event properties */
3292 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294 struct ElementInfo *ei = &element_info[i];
3296 for (j = 0; j < ei->num_change_pages; j++)
3298 if (!ei->change_page[j].can_change_or_has_action)
3301 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3303 int trigger_element = ei->change_page[j].trigger_element;
3305 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3307 if (ei->change_page[j].has_event[k])
3309 if (IS_GROUP_ELEMENT(trigger_element))
3311 struct ElementGroupInfo *group =
3312 element_info[trigger_element].group;
3314 for (l = 0; l < group->num_elements_resolved; l++)
3315 trigger_events[group->element_resolved[l]][k] = TRUE;
3317 else if (trigger_element == EL_ANY_ELEMENT)
3318 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3319 trigger_events[l][k] = TRUE;
3321 trigger_events[trigger_element][k] = TRUE;
3328 /* ---------- initialize push delay -------------------------------------- */
3330 /* initialize push delay values to default */
3331 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333 if (!IS_CUSTOM_ELEMENT(i))
3335 /* set default push delay values (corrected since version 3.0.7-1) */
3336 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3338 element_info[i].push_delay_fixed = 2;
3339 element_info[i].push_delay_random = 8;
3343 element_info[i].push_delay_fixed = 8;
3344 element_info[i].push_delay_random = 8;
3349 /* set push delay value for certain elements from pre-defined list */
3350 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3352 int e = push_delay_list[i].element;
3354 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3355 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3358 /* set push delay value for Supaplex elements for newer engine versions */
3359 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3361 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3363 if (IS_SP_ELEMENT(i))
3365 /* set SP push delay to just enough to push under a falling zonk */
3366 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3368 element_info[i].push_delay_fixed = delay;
3369 element_info[i].push_delay_random = 0;
3374 /* ---------- initialize move stepsize ----------------------------------- */
3376 /* initialize move stepsize values to default */
3377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378 if (!IS_CUSTOM_ELEMENT(i))
3379 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3381 /* set move stepsize value for certain elements from pre-defined list */
3382 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3384 int e = move_stepsize_list[i].element;
3386 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3389 /* ---------- initialize collect score ----------------------------------- */
3391 /* initialize collect score values for custom elements from initial value */
3392 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3393 if (IS_CUSTOM_ELEMENT(i))
3394 element_info[i].collect_score = element_info[i].collect_score_initial;
3396 /* ---------- initialize collect count ----------------------------------- */
3398 /* initialize collect count values for non-custom elements */
3399 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400 if (!IS_CUSTOM_ELEMENT(i))
3401 element_info[i].collect_count_initial = 0;
3403 /* add collect count values for all elements from pre-defined list */
3404 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3405 element_info[collect_count_list[i].element].collect_count_initial =
3406 collect_count_list[i].count;
3408 /* ---------- initialize access direction -------------------------------- */
3410 /* initialize access direction values to default (access from every side) */
3411 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412 if (!IS_CUSTOM_ELEMENT(i))
3413 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3415 /* set access direction value for certain elements from pre-defined list */
3416 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3417 element_info[access_direction_list[i].element].access_direction =
3418 access_direction_list[i].direction;
3420 /* ---------- initialize explosion content ------------------------------- */
3421 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423 if (IS_CUSTOM_ELEMENT(i))
3426 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3428 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3430 element_info[i].content.e[x][y] =
3431 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3432 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3433 i == EL_PLAYER_3 ? EL_EMERALD :
3434 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3435 i == EL_MOLE ? EL_EMERALD_RED :
3436 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3437 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3438 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3439 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3440 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3441 i == EL_WALL_EMERALD ? EL_EMERALD :
3442 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3443 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3444 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3445 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3446 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3447 i == EL_WALL_PEARL ? EL_PEARL :
3448 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3453 /* ---------- initialize recursion detection ------------------------------ */
3454 recursion_loop_depth = 0;
3455 recursion_loop_detected = FALSE;
3456 recursion_loop_element = EL_UNDEFINED;
3458 /* ---------- initialize graphics engine ---------------------------------- */
3459 game.scroll_delay_value =
3460 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3461 setup.scroll_delay ? setup.scroll_delay_value : 0);
3462 game.scroll_delay_value =
3463 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3466 int get_num_special_action(int element, int action_first, int action_last)
3468 int num_special_action = 0;
3471 for (i = action_first; i <= action_last; i++)
3473 boolean found = FALSE;
3475 for (j = 0; j < NUM_DIRECTIONS; j++)
3476 if (el_act_dir2img(element, i, j) !=
3477 el_act_dir2img(element, ACTION_DEFAULT, j))
3481 num_special_action++;
3486 return num_special_action;
3491 =============================================================================
3493 -----------------------------------------------------------------------------
3494 initialize and start new game
3495 =============================================================================
3500 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3501 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3502 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3504 boolean do_fading = (game_status == GAME_MODE_MAIN);
3507 int initial_move_dir = MV_DOWN;
3509 int initial_move_dir = MV_NONE;
3513 game_status = GAME_MODE_PLAYING;
3516 /* needed if different viewport properties defined for playing */
3517 ChangeViewportPropertiesIfNeeded();
3521 DrawCompleteVideoDisplay();
3525 InitGameControlValues();
3527 /* don't play tapes over network */
3528 network_playing = (options.network && !tape.playing);
3530 for (i = 0; i < MAX_PLAYERS; i++)
3532 struct PlayerInfo *player = &stored_player[i];
3534 player->index_nr = i;
3535 player->index_bit = (1 << i);
3536 player->element_nr = EL_PLAYER_1 + i;
3538 player->present = FALSE;
3539 player->active = FALSE;
3540 player->mapped = FALSE;
3542 player->killed = FALSE;
3543 player->reanimated = FALSE;
3546 player->effective_action = 0;
3547 player->programmed_action = 0;
3550 player->score_final = 0;
3552 player->gems_still_needed = level.gems_needed;
3553 player->sokobanfields_still_needed = 0;
3554 player->lights_still_needed = 0;
3555 player->friends_still_needed = 0;
3557 for (j = 0; j < MAX_NUM_KEYS; j++)
3558 player->key[j] = FALSE;
3560 player->num_white_keys = 0;
3562 player->dynabomb_count = 0;
3563 player->dynabomb_size = 1;
3564 player->dynabombs_left = 0;
3565 player->dynabomb_xl = FALSE;
3567 player->MovDir = initial_move_dir;
3570 player->GfxDir = initial_move_dir;
3571 player->GfxAction = ACTION_DEFAULT;
3573 player->StepFrame = 0;
3575 player->initial_element = player->element_nr;
3576 player->artwork_element =
3577 (level.use_artwork_element[i] ? level.artwork_element[i] :
3578 player->element_nr);
3579 player->use_murphy = FALSE;
3581 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3582 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3584 player->gravity = level.initial_player_gravity[i];
3586 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3588 player->actual_frame_counter = 0;
3590 player->step_counter = 0;
3592 player->last_move_dir = initial_move_dir;
3594 player->is_active = FALSE;
3596 player->is_waiting = FALSE;
3597 player->is_moving = FALSE;
3598 player->is_auto_moving = FALSE;
3599 player->is_digging = FALSE;
3600 player->is_snapping = FALSE;
3601 player->is_collecting = FALSE;
3602 player->is_pushing = FALSE;
3603 player->is_switching = FALSE;
3604 player->is_dropping = FALSE;
3605 player->is_dropping_pressed = FALSE;
3607 player->is_bored = FALSE;
3608 player->is_sleeping = FALSE;
3610 player->frame_counter_bored = -1;
3611 player->frame_counter_sleeping = -1;
3613 player->anim_delay_counter = 0;
3614 player->post_delay_counter = 0;
3616 player->dir_waiting = initial_move_dir;
3617 player->action_waiting = ACTION_DEFAULT;
3618 player->last_action_waiting = ACTION_DEFAULT;
3619 player->special_action_bored = ACTION_DEFAULT;
3620 player->special_action_sleeping = ACTION_DEFAULT;
3622 player->switch_x = -1;
3623 player->switch_y = -1;
3625 player->drop_x = -1;
3626 player->drop_y = -1;
3628 player->show_envelope = 0;
3630 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3632 player->push_delay = -1; /* initialized when pushing starts */
3633 player->push_delay_value = game.initial_push_delay_value;
3635 player->drop_delay = 0;
3636 player->drop_pressed_delay = 0;
3638 player->last_jx = -1;
3639 player->last_jy = -1;
3643 player->shield_normal_time_left = 0;
3644 player->shield_deadly_time_left = 0;
3646 player->inventory_infinite_element = EL_UNDEFINED;
3647 player->inventory_size = 0;
3649 if (level.use_initial_inventory[i])
3651 for (j = 0; j < level.initial_inventory_size[i]; j++)
3653 int element = level.initial_inventory_content[i][j];
3654 int collect_count = element_info[element].collect_count_initial;
3657 if (!IS_CUSTOM_ELEMENT(element))
3660 if (collect_count == 0)
3661 player->inventory_infinite_element = element;
3663 for (k = 0; k < collect_count; k++)
3664 if (player->inventory_size < MAX_INVENTORY_SIZE)
3665 player->inventory_element[player->inventory_size++] = element;
3669 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3670 SnapField(player, 0, 0);
3672 player->LevelSolved = FALSE;
3673 player->GameOver = FALSE;
3675 player->LevelSolved_GameWon = FALSE;
3676 player->LevelSolved_GameEnd = FALSE;
3677 player->LevelSolved_PanelOff = FALSE;
3678 player->LevelSolved_SaveTape = FALSE;
3679 player->LevelSolved_SaveScore = FALSE;
3680 player->LevelSolved_CountingTime = 0;
3681 player->LevelSolved_CountingScore = 0;
3683 map_player_action[i] = i;
3686 network_player_action_received = FALSE;
3688 #if defined(NETWORK_AVALIABLE)
3689 /* initial null action */
3690 if (network_playing)
3691 SendToServer_MovePlayer(MV_NONE);
3700 TimeLeft = level.time;
3703 ScreenMovDir = MV_NONE;
3707 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3709 AllPlayersGone = FALSE;
3711 game.no_time_limit = (level.time == 0);
3713 game.yamyam_content_nr = 0;
3714 game.robot_wheel_active = FALSE;
3715 game.magic_wall_active = FALSE;
3716 game.magic_wall_time_left = 0;
3717 game.light_time_left = 0;
3718 game.timegate_time_left = 0;
3719 game.switchgate_pos = 0;
3720 game.wind_direction = level.wind_direction_initial;
3722 #if !USE_PLAYER_GRAVITY
3723 game.gravity = FALSE;
3724 game.explosions_delayed = TRUE;
3727 game.lenses_time_left = 0;
3728 game.magnify_time_left = 0;
3730 game.ball_state = level.ball_state_initial;
3731 game.ball_content_nr = 0;
3733 game.envelope_active = FALSE;
3735 /* set focus to local player for network games, else to all players */
3736 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3737 game.centered_player_nr_next = game.centered_player_nr;
3738 game.set_centered_player = FALSE;
3740 if (network_playing && tape.recording)
3742 /* store client dependent player focus when recording network games */
3743 tape.centered_player_nr_next = game.centered_player_nr_next;
3744 tape.set_centered_player = TRUE;
3747 for (i = 0; i < NUM_BELTS; i++)
3749 game.belt_dir[i] = MV_NONE;
3750 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3753 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3754 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3756 SCAN_PLAYFIELD(x, y)
3758 Feld[x][y] = level.field[x][y];
3759 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3760 ChangeDelay[x][y] = 0;
3761 ChangePage[x][y] = -1;
3762 #if USE_NEW_CUSTOM_VALUE
3763 CustomValue[x][y] = 0; /* initialized in InitField() */
3765 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3767 WasJustMoving[x][y] = 0;
3768 WasJustFalling[x][y] = 0;
3769 CheckCollision[x][y] = 0;
3770 CheckImpact[x][y] = 0;
3772 Pushed[x][y] = FALSE;
3774 ChangeCount[x][y] = 0;
3775 ChangeEvent[x][y] = -1;
3777 ExplodePhase[x][y] = 0;
3778 ExplodeDelay[x][y] = 0;
3779 ExplodeField[x][y] = EX_TYPE_NONE;
3781 RunnerVisit[x][y] = 0;
3782 PlayerVisit[x][y] = 0;
3785 GfxRandom[x][y] = INIT_GFX_RANDOM();
3786 GfxElement[x][y] = EL_UNDEFINED;
3787 GfxAction[x][y] = ACTION_DEFAULT;
3788 GfxDir[x][y] = MV_NONE;
3789 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3792 SCAN_PLAYFIELD(x, y)
3794 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3796 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3798 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3801 InitField(x, y, TRUE);
3803 ResetGfxAnimation(x, y);
3808 for (i = 0; i < MAX_PLAYERS; i++)
3810 struct PlayerInfo *player = &stored_player[i];
3812 /* set number of special actions for bored and sleeping animation */
3813 player->num_special_action_bored =
3814 get_num_special_action(player->artwork_element,
3815 ACTION_BORING_1, ACTION_BORING_LAST);
3816 player->num_special_action_sleeping =
3817 get_num_special_action(player->artwork_element,
3818 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3821 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3822 emulate_sb ? EMU_SOKOBAN :
3823 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3825 #if USE_NEW_ALL_SLIPPERY
3826 /* initialize type of slippery elements */
3827 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3829 if (!IS_CUSTOM_ELEMENT(i))
3831 /* default: elements slip down either to the left or right randomly */
3832 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3834 /* SP style elements prefer to slip down on the left side */
3835 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3838 /* BD style elements prefer to slip down on the left side */
3839 if (game.emulation == EMU_BOULDERDASH)
3840 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3845 /* initialize explosion and ignition delay */
3846 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3848 if (!IS_CUSTOM_ELEMENT(i))
3851 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3852 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3853 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3854 int last_phase = (num_phase + 1) * delay;
3855 int half_phase = (num_phase / 2) * delay;
3857 element_info[i].explosion_delay = last_phase - 1;
3858 element_info[i].ignition_delay = half_phase;
3860 if (i == EL_BLACK_ORB)
3861 element_info[i].ignition_delay = 1;
3865 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3866 element_info[i].explosion_delay = 1;
3868 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3869 element_info[i].ignition_delay = 1;
3873 /* correct non-moving belts to start moving left */
3874 for (i = 0; i < NUM_BELTS; i++)
3875 if (game.belt_dir[i] == MV_NONE)
3876 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3878 #if USE_NEW_PLAYER_ASSIGNMENTS
3879 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3880 /* choose default local player */
3881 local_player = &stored_player[0];
3883 for (i = 0; i < MAX_PLAYERS; i++)
3884 stored_player[i].connected = FALSE;
3886 local_player->connected = TRUE;
3887 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3891 /* try to guess locally connected team mode players (needed for correct
3892 assignment of player figures from level to locally playing players) */
3894 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (tape.player_participates[i])
3896 stored_player[i].connected = TRUE;
3898 else if (setup.team_mode && !options.network)
3900 /* try to guess locally connected team mode players (needed for correct
3901 assignment of player figures from level to locally playing players) */
3903 for (i = 0; i < MAX_PLAYERS; i++)
3904 if (setup.input[i].use_joystick ||
3905 setup.input[i].key.left != KSYM_UNDEFINED)
3906 stored_player[i].connected = TRUE;
3910 for (i = 0; i < MAX_PLAYERS; i++)
3911 printf("::: player %d: %s\n", i,
3912 (stored_player[i].connected ? "connected" : "not connected"));
3914 for (i = 0; i < MAX_PLAYERS; i++)
3915 printf("::: player %d: %s\n", i,
3916 (stored_player[i].present ? "present" : "not present"));
3919 /* check if any connected player was not found in playfield */
3920 for (i = 0; i < MAX_PLAYERS; i++)
3922 struct PlayerInfo *player = &stored_player[i];
3924 if (player->connected && !player->present)
3926 struct PlayerInfo *field_player = NULL;
3929 printf("::: looking for field player for player %d ...\n", i);
3932 /* assign first free player found that is present in the playfield */
3934 /* first try: look for unmapped playfield player that is not connected */
3935 if (field_player == NULL)
3936 for (j = 0; j < MAX_PLAYERS; j++)
3937 if (stored_player[j].present &&
3938 !stored_player[j].mapped &&
3939 !stored_player[j].connected)
3940 field_player = &stored_player[j];
3942 /* second try: look for *any* unmapped playfield player */
3943 if (field_player == NULL)
3944 for (j = 0; j < MAX_PLAYERS; j++)
3945 if (stored_player[j].present &&
3946 !stored_player[j].mapped)
3947 field_player = &stored_player[j];
3949 if (field_player != NULL)
3951 int jx = field_player->jx, jy = field_player->jy;
3954 printf("::: found player figure %d\n", field_player->index_nr);
3957 player->present = FALSE;
3958 player->active = FALSE;
3960 field_player->present = TRUE;
3961 field_player->active = TRUE;
3964 player->initial_element = field_player->initial_element;
3965 player->artwork_element = field_player->artwork_element;
3967 player->block_last_field = field_player->block_last_field;
3968 player->block_delay_adjustment = field_player->block_delay_adjustment;
3971 StorePlayer[jx][jy] = field_player->element_nr;
3973 field_player->jx = field_player->last_jx = jx;
3974 field_player->jy = field_player->last_jy = jy;
3976 if (local_player == player)
3977 local_player = field_player;
3979 map_player_action[field_player->index_nr] = i;
3981 field_player->mapped = TRUE;
3984 printf("::: map_player_action[%d] == %d\n",
3985 field_player->index_nr, i);
3990 if (player->connected && player->present)
3991 player->mapped = TRUE;
3996 /* check if any connected player was not found in playfield */
3997 for (i = 0; i < MAX_PLAYERS; i++)
3999 struct PlayerInfo *player = &stored_player[i];
4001 if (player->connected && !player->present)
4003 for (j = 0; j < MAX_PLAYERS; j++)
4005 struct PlayerInfo *field_player = &stored_player[j];
4006 int jx = field_player->jx, jy = field_player->jy;
4008 /* assign first free player found that is present in the playfield */
4009 if (field_player->present && !field_player->connected)
4011 player->present = TRUE;
4012 player->active = TRUE;
4014 field_player->present = FALSE;
4015 field_player->active = FALSE;
4017 player->initial_element = field_player->initial_element;
4018 player->artwork_element = field_player->artwork_element;
4020 player->block_last_field = field_player->block_last_field;
4021 player->block_delay_adjustment = field_player->block_delay_adjustment;
4023 StorePlayer[jx][jy] = player->element_nr;
4025 player->jx = player->last_jx = jx;
4026 player->jy = player->last_jy = jy;
4036 printf("::: local_player->present == %d\n", local_player->present);
4041 /* when playing a tape, eliminate all players who do not participate */
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044 for (i = 0; i < MAX_PLAYERS; i++)
4046 if (stored_player[i].active &&
4047 !tape.player_participates[map_player_action[i]])
4049 struct PlayerInfo *player = &stored_player[i];
4050 int jx = player->jx, jy = player->jy;
4052 player->active = FALSE;
4053 StorePlayer[jx][jy] = 0;
4054 Feld[jx][jy] = EL_EMPTY;
4058 for (i = 0; i < MAX_PLAYERS; i++)
4060 if (stored_player[i].active &&
4061 !tape.player_participates[i])
4063 struct PlayerInfo *player = &stored_player[i];
4064 int jx = player->jx, jy = player->jy;
4066 player->active = FALSE;
4067 StorePlayer[jx][jy] = 0;
4068 Feld[jx][jy] = EL_EMPTY;
4073 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4075 /* when in single player mode, eliminate all but the first active player */
4077 for (i = 0; i < MAX_PLAYERS; i++)
4079 if (stored_player[i].active)
4081 for (j = i + 1; j < MAX_PLAYERS; j++)
4083 if (stored_player[j].active)
4085 struct PlayerInfo *player = &stored_player[j];
4086 int jx = player->jx, jy = player->jy;
4088 player->active = FALSE;
4089 player->present = FALSE;
4091 StorePlayer[jx][jy] = 0;
4092 Feld[jx][jy] = EL_EMPTY;
4099 /* when recording the game, store which players take part in the game */
4102 #if USE_NEW_PLAYER_ASSIGNMENTS
4103 for (i = 0; i < MAX_PLAYERS; i++)
4104 if (stored_player[i].connected)
4105 tape.player_participates[i] = TRUE;
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 if (stored_player[i].active)
4109 tape.player_participates[i] = TRUE;
4115 for (i = 0; i < MAX_PLAYERS; i++)
4117 struct PlayerInfo *player = &stored_player[i];
4119 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4124 if (local_player == player)
4125 printf("Player %d is local player.\n", i+1);
4129 if (BorderElement == EL_EMPTY)
4132 SBX_Right = lev_fieldx - SCR_FIELDX;
4134 SBY_Lower = lev_fieldy - SCR_FIELDY;
4139 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4141 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4146 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4147 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4149 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4150 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4152 if (EVEN(SCR_FIELDX))
4154 if (EVEN(SCR_FIELDY))
4159 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4162 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4166 /* if local player not found, look for custom element that might create
4167 the player (make some assumptions about the right custom element) */
4168 if (!local_player->present)
4170 int start_x = 0, start_y = 0;
4171 int found_rating = 0;
4172 int found_element = EL_UNDEFINED;
4173 int player_nr = local_player->index_nr;
4175 SCAN_PLAYFIELD(x, y)
4177 int element = Feld[x][y];
4182 if (level.use_start_element[player_nr] &&
4183 level.start_element[player_nr] == element &&
4190 found_element = element;
4193 if (!IS_CUSTOM_ELEMENT(element))
4196 if (CAN_CHANGE(element))
4198 for (i = 0; i < element_info[element].num_change_pages; i++)
4200 /* check for player created from custom element as single target */
4201 content = element_info[element].change_page[i].target_element;
4202 is_player = ELEM_IS_PLAYER(content);
4204 if (is_player && (found_rating < 3 ||
4205 (found_rating == 3 && element < found_element)))
4211 found_element = element;
4216 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4218 /* check for player created from custom element as explosion content */
4219 content = element_info[element].content.e[xx][yy];
4220 is_player = ELEM_IS_PLAYER(content);
4222 if (is_player && (found_rating < 2 ||
4223 (found_rating == 2 && element < found_element)))
4225 start_x = x + xx - 1;
4226 start_y = y + yy - 1;
4229 found_element = element;
4232 if (!CAN_CHANGE(element))
4235 for (i = 0; i < element_info[element].num_change_pages; i++)
4237 /* check for player created from custom element as extended target */
4239 element_info[element].change_page[i].target_content.e[xx][yy];
4241 is_player = ELEM_IS_PLAYER(content);
4243 if (is_player && (found_rating < 1 ||
4244 (found_rating == 1 && element < found_element)))
4246 start_x = x + xx - 1;
4247 start_y = y + yy - 1;
4250 found_element = element;
4256 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4257 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4260 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4266 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4267 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268 local_player->jx - MIDPOSX);
4270 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272 local_player->jy - MIDPOSY);
4276 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4280 /* do not use PLAYING mask for fading out from main screen */
4281 game_status = GAME_MODE_MAIN;
4286 if (!game.restart_level)
4287 CloseDoor(DOOR_CLOSE_1);
4290 if (level_editor_test_game)
4291 FadeSkipNextFadeIn();
4293 FadeSetEnterScreen();
4295 if (level_editor_test_game)
4296 fading = fading_none;
4298 fading = menu.destination;
4302 FadeOut(REDRAW_FIELD);
4305 FadeOut(REDRAW_FIELD);
4309 game_status = GAME_MODE_PLAYING;
4312 /* !!! FIX THIS (START) !!! */
4313 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4315 InitGameEngine_EM();
4317 /* blit playfield from scroll buffer to normal back buffer for fading in */
4318 BlitScreenToBitmap_EM(backbuffer);
4320 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4322 InitGameEngine_SP();
4324 /* blit playfield from scroll buffer to normal back buffer for fading in */
4325 BlitScreenToBitmap_SP(backbuffer);
4332 /* after drawing the level, correct some elements */
4333 if (game.timegate_time_left == 0)
4334 CloseAllOpenTimegates();
4337 BlitScreenToBitmap(backbuffer);
4339 /* blit playfield from scroll buffer to normal back buffer for fading in */
4340 if (setup.soft_scrolling)
4341 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4344 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4346 /* !!! FIX THIS (END) !!! */
4349 FadeIn(REDRAW_FIELD);
4352 FadeIn(REDRAW_FIELD);
4357 if (!game.restart_level)
4359 /* copy default game door content to main double buffer */
4362 /* !!! CHECK AGAIN !!! */
4363 SetPanelBackground();
4364 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4365 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4367 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4369 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4370 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4371 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4372 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4375 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4376 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4380 SetPanelBackground();
4381 SetDrawBackgroundMask(REDRAW_DOOR_1);
4384 UpdateAndDisplayGameControlValues();
4386 UpdateGameDoorValues();
4387 DrawGameDoorValues();
4390 if (!game.restart_level)
4394 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4395 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4396 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4400 /* copy actual game door content to door double buffer for OpenDoor() */
4401 BlitBitmap(drawto, bitmap_db_door,
4402 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4404 OpenDoor(DOOR_OPEN_ALL);
4406 PlaySound(SND_GAME_STARTING);
4408 if (setup.sound_music)
4411 KeyboardAutoRepeatOffUnlessAutoplay();
4415 for (i = 0; i < MAX_PLAYERS; i++)
4416 printf("Player %d %sactive.\n",
4417 i + 1, (stored_player[i].active ? "" : "not "));
4428 if (!game.restart_level && !tape.playing)
4430 LevelStats_incPlayed(level_nr);
4432 SaveLevelSetup_SeriesInfo();
4435 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4439 game.restart_level = FALSE;
4442 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4444 /* this is used for non-R'n'D game engines to update certain engine values */
4446 /* needed to determine if sounds are played within the visible screen area */
4447 scroll_x = actual_scroll_x;
4448 scroll_y = actual_scroll_y;
4451 void InitMovDir(int x, int y)
4453 int i, element = Feld[x][y];
4454 static int xy[4][2] =
4461 static int direction[3][4] =
4463 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4464 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4465 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4474 Feld[x][y] = EL_BUG;
4475 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4478 case EL_SPACESHIP_RIGHT:
4479 case EL_SPACESHIP_UP:
4480 case EL_SPACESHIP_LEFT:
4481 case EL_SPACESHIP_DOWN:
4482 Feld[x][y] = EL_SPACESHIP;
4483 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4486 case EL_BD_BUTTERFLY_RIGHT:
4487 case EL_BD_BUTTERFLY_UP:
4488 case EL_BD_BUTTERFLY_LEFT:
4489 case EL_BD_BUTTERFLY_DOWN:
4490 Feld[x][y] = EL_BD_BUTTERFLY;
4491 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4494 case EL_BD_FIREFLY_RIGHT:
4495 case EL_BD_FIREFLY_UP:
4496 case EL_BD_FIREFLY_LEFT:
4497 case EL_BD_FIREFLY_DOWN:
4498 Feld[x][y] = EL_BD_FIREFLY;
4499 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4502 case EL_PACMAN_RIGHT:
4504 case EL_PACMAN_LEFT:
4505 case EL_PACMAN_DOWN:
4506 Feld[x][y] = EL_PACMAN;
4507 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4510 case EL_YAMYAM_LEFT:
4511 case EL_YAMYAM_RIGHT:
4513 case EL_YAMYAM_DOWN:
4514 Feld[x][y] = EL_YAMYAM;
4515 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4518 case EL_SP_SNIKSNAK:
4519 MovDir[x][y] = MV_UP;
4522 case EL_SP_ELECTRON:
4523 MovDir[x][y] = MV_LEFT;
4530 Feld[x][y] = EL_MOLE;
4531 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4535 if (IS_CUSTOM_ELEMENT(element))
4537 struct ElementInfo *ei = &element_info[element];
4538 int move_direction_initial = ei->move_direction_initial;
4539 int move_pattern = ei->move_pattern;
4541 if (move_direction_initial == MV_START_PREVIOUS)
4543 if (MovDir[x][y] != MV_NONE)
4546 move_direction_initial = MV_START_AUTOMATIC;
4549 if (move_direction_initial == MV_START_RANDOM)
4550 MovDir[x][y] = 1 << RND(4);
4551 else if (move_direction_initial & MV_ANY_DIRECTION)
4552 MovDir[x][y] = move_direction_initial;
4553 else if (move_pattern == MV_ALL_DIRECTIONS ||
4554 move_pattern == MV_TURNING_LEFT ||
4555 move_pattern == MV_TURNING_RIGHT ||
4556 move_pattern == MV_TURNING_LEFT_RIGHT ||
4557 move_pattern == MV_TURNING_RIGHT_LEFT ||
4558 move_pattern == MV_TURNING_RANDOM)
4559 MovDir[x][y] = 1 << RND(4);
4560 else if (move_pattern == MV_HORIZONTAL)
4561 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4562 else if (move_pattern == MV_VERTICAL)
4563 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4564 else if (move_pattern & MV_ANY_DIRECTION)
4565 MovDir[x][y] = element_info[element].move_pattern;
4566 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4567 move_pattern == MV_ALONG_RIGHT_SIDE)
4569 /* use random direction as default start direction */
4570 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4571 MovDir[x][y] = 1 << RND(4);
4573 for (i = 0; i < NUM_DIRECTIONS; i++)
4575 int x1 = x + xy[i][0];
4576 int y1 = y + xy[i][1];
4578 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4580 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4581 MovDir[x][y] = direction[0][i];
4583 MovDir[x][y] = direction[1][i];
4592 MovDir[x][y] = 1 << RND(4);
4594 if (element != EL_BUG &&
4595 element != EL_SPACESHIP &&
4596 element != EL_BD_BUTTERFLY &&
4597 element != EL_BD_FIREFLY)
4600 for (i = 0; i < NUM_DIRECTIONS; i++)
4602 int x1 = x + xy[i][0];
4603 int y1 = y + xy[i][1];
4605 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4607 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4609 MovDir[x][y] = direction[0][i];
4612 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4613 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4615 MovDir[x][y] = direction[1][i];
4624 GfxDir[x][y] = MovDir[x][y];
4627 void InitAmoebaNr(int x, int y)
4630 int group_nr = AmoebeNachbarNr(x, y);
4634 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4636 if (AmoebaCnt[i] == 0)
4644 AmoebaNr[x][y] = group_nr;
4645 AmoebaCnt[group_nr]++;
4646 AmoebaCnt2[group_nr]++;
4649 static void PlayerWins(struct PlayerInfo *player)
4651 player->LevelSolved = TRUE;
4652 player->GameOver = TRUE;
4654 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4655 level.native_em_level->lev->score : player->score);
4657 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4659 player->LevelSolved_CountingScore = player->score_final;
4664 static int time, time_final;
4665 static int score, score_final;
4666 static int game_over_delay_1 = 0;
4667 static int game_over_delay_2 = 0;
4668 int game_over_delay_value_1 = 50;
4669 int game_over_delay_value_2 = 50;
4671 if (!local_player->LevelSolved_GameWon)
4675 /* do not start end game actions before the player stops moving (to exit) */
4676 if (local_player->MovPos)
4679 local_player->LevelSolved_GameWon = TRUE;
4680 local_player->LevelSolved_SaveTape = tape.recording;
4681 local_player->LevelSolved_SaveScore = !tape.playing;
4685 LevelStats_incSolved(level_nr);
4687 SaveLevelSetup_SeriesInfo();
4690 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4694 if (tape.auto_play) /* tape might already be stopped here */
4695 tape.auto_play_level_solved = TRUE;
4701 game_over_delay_1 = game_over_delay_value_1;
4702 game_over_delay_2 = game_over_delay_value_2;
4704 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4705 score = score_final = local_player->score_final;
4710 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4712 else if (game.no_time_limit && TimePlayed < 999)
4715 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4718 local_player->score_final = score_final;
4720 if (level_editor_test_game)
4723 score = score_final;
4726 local_player->LevelSolved_CountingTime = time;
4727 local_player->LevelSolved_CountingScore = score;
4729 game_panel_controls[GAME_PANEL_TIME].value = time;
4730 game_panel_controls[GAME_PANEL_SCORE].value = score;
4732 DisplayGameControlValues();
4734 DrawGameValue_Time(time);
4735 DrawGameValue_Score(score);
4739 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4741 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4743 /* close exit door after last player */
4744 if ((AllPlayersGone &&
4745 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4746 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4747 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4748 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4749 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4751 int element = Feld[ExitX][ExitY];
4754 if (element == EL_EM_EXIT_OPEN ||
4755 element == EL_EM_STEEL_EXIT_OPEN)
4762 Feld[ExitX][ExitY] =
4763 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4764 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4765 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4766 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4767 EL_EM_STEEL_EXIT_CLOSING);
4769 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4773 /* player disappears */
4774 DrawLevelField(ExitX, ExitY);
4777 for (i = 0; i < MAX_PLAYERS; i++)
4779 struct PlayerInfo *player = &stored_player[i];
4781 if (player->present)
4783 RemovePlayer(player);
4785 /* player disappears */
4786 DrawLevelField(player->jx, player->jy);
4791 PlaySound(SND_GAME_WINNING);
4794 if (game_over_delay_1 > 0)
4796 game_over_delay_1--;
4801 if (time != time_final)
4803 int time_to_go = ABS(time_final - time);
4804 int time_count_dir = (time < time_final ? +1 : -1);
4805 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4807 time += time_count_steps * time_count_dir;
4808 score += time_count_steps * level.score[SC_TIME_BONUS];
4811 local_player->LevelSolved_CountingTime = time;
4812 local_player->LevelSolved_CountingScore = score;
4814 game_panel_controls[GAME_PANEL_TIME].value = time;
4815 game_panel_controls[GAME_PANEL_SCORE].value = score;
4817 DisplayGameControlValues();
4819 DrawGameValue_Time(time);
4820 DrawGameValue_Score(score);
4823 if (time == time_final)
4824 StopSound(SND_GAME_LEVELTIME_BONUS);
4825 else if (setup.sound_loops)
4826 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4828 PlaySound(SND_GAME_LEVELTIME_BONUS);
4833 local_player->LevelSolved_PanelOff = TRUE;
4835 if (game_over_delay_2 > 0)
4837 game_over_delay_2--;
4850 boolean raise_level = FALSE;
4852 local_player->LevelSolved_GameEnd = TRUE;
4854 CloseDoor(DOOR_CLOSE_1);
4856 if (local_player->LevelSolved_SaveTape)
4863 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4865 SaveTape(tape.level_nr); /* ask to save tape */
4869 if (level_editor_test_game)
4871 game_status = GAME_MODE_MAIN;
4874 DrawAndFadeInMainMenu(REDRAW_FIELD);
4882 if (!local_player->LevelSolved_SaveScore)
4885 FadeOut(REDRAW_FIELD);
4888 game_status = GAME_MODE_MAIN;
4890 DrawAndFadeInMainMenu(REDRAW_FIELD);
4895 if (level_nr == leveldir_current->handicap_level)
4897 leveldir_current->handicap_level++;
4899 SaveLevelSetup_SeriesInfo();
4902 if (level_nr < leveldir_current->last_level)
4903 raise_level = TRUE; /* advance to next level */
4905 if ((hi_pos = NewHiScore()) >= 0)
4907 game_status = GAME_MODE_SCORES;
4909 DrawHallOfFame(hi_pos);
4920 FadeOut(REDRAW_FIELD);
4923 game_status = GAME_MODE_MAIN;
4931 DrawAndFadeInMainMenu(REDRAW_FIELD);
4940 LoadScore(level_nr);
4942 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4943 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4946 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4948 if (local_player->score_final > highscore[k].Score)
4950 /* player has made it to the hall of fame */
4952 if (k < MAX_SCORE_ENTRIES - 1)
4954 int m = MAX_SCORE_ENTRIES - 1;
4957 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4958 if (strEqual(setup.player_name, highscore[l].Name))
4960 if (m == k) /* player's new highscore overwrites his old one */
4964 for (l = m; l > k; l--)
4966 strcpy(highscore[l].Name, highscore[l - 1].Name);
4967 highscore[l].Score = highscore[l - 1].Score;
4974 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4975 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4976 highscore[k].Score = local_player->score_final;
4982 else if (!strncmp(setup.player_name, highscore[k].Name,
4983 MAX_PLAYER_NAME_LEN))
4984 break; /* player already there with a higher score */
4990 SaveScore(level_nr);
4995 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4997 int element = Feld[x][y];
4998 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4999 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5000 int horiz_move = (dx != 0);
5001 int sign = (horiz_move ? dx : dy);
5002 int step = sign * element_info[element].move_stepsize;
5004 /* special values for move stepsize for spring and things on conveyor belt */
5007 if (CAN_FALL(element) &&
5008 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5009 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5010 else if (element == EL_SPRING)
5011 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5017 inline static int getElementMoveStepsize(int x, int y)
5019 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5022 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5024 if (player->GfxAction != action || player->GfxDir != dir)
5027 printf("Player frame reset! (%d => %d, %d => %d)\n",
5028 player->GfxAction, action, player->GfxDir, dir);
5031 player->GfxAction = action;
5032 player->GfxDir = dir;
5034 player->StepFrame = 0;
5038 #if USE_GFX_RESET_GFX_ANIMATION
5039 static void ResetGfxFrame(int x, int y, boolean redraw)
5041 int element = Feld[x][y];
5042 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5043 int last_gfx_frame = GfxFrame[x][y];
5045 if (graphic_info[graphic].anim_global_sync)
5046 GfxFrame[x][y] = FrameCounter;
5047 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048 GfxFrame[x][y] = CustomValue[x][y];
5049 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050 GfxFrame[x][y] = element_info[element].collect_score;
5051 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052 GfxFrame[x][y] = ChangeDelay[x][y];
5054 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5055 DrawLevelGraphicAnimation(x, y, graphic);
5059 static void ResetGfxAnimation(int x, int y)
5061 GfxAction[x][y] = ACTION_DEFAULT;
5062 GfxDir[x][y] = MovDir[x][y];
5065 #if USE_GFX_RESET_GFX_ANIMATION
5066 ResetGfxFrame(x, y, FALSE);
5070 static void ResetRandomAnimationValue(int x, int y)
5072 GfxRandom[x][y] = INIT_GFX_RANDOM();
5075 void InitMovingField(int x, int y, int direction)
5077 int element = Feld[x][y];
5078 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5082 boolean is_moving_before, is_moving_after;
5084 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5087 /* check if element was/is moving or being moved before/after mode change */
5090 is_moving_before = (WasJustMoving[x][y] != 0);
5092 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5093 is_moving_before = WasJustMoving[x][y];
5096 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5098 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5100 /* reset animation only for moving elements which change direction of moving
5101 or which just started or stopped moving
5102 (else CEs with property "can move" / "not moving" are reset each frame) */
5103 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5105 if (is_moving_before != is_moving_after ||
5106 direction != MovDir[x][y])
5107 ResetGfxAnimation(x, y);
5109 if ((is_moving_before || is_moving_after) && !continues_moving)
5110 ResetGfxAnimation(x, y);
5113 if (!continues_moving)
5114 ResetGfxAnimation(x, y);
5117 MovDir[x][y] = direction;
5118 GfxDir[x][y] = direction;
5120 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5121 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5122 direction == MV_DOWN && CAN_FALL(element) ?
5123 ACTION_FALLING : ACTION_MOVING);
5125 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5126 ACTION_FALLING : ACTION_MOVING);
5129 /* this is needed for CEs with property "can move" / "not moving" */
5131 if (is_moving_after)
5133 if (Feld[newx][newy] == EL_EMPTY)
5134 Feld[newx][newy] = EL_BLOCKED;
5136 MovDir[newx][newy] = MovDir[x][y];
5138 #if USE_NEW_CUSTOM_VALUE
5139 CustomValue[newx][newy] = CustomValue[x][y];
5142 GfxFrame[newx][newy] = GfxFrame[x][y];
5143 GfxRandom[newx][newy] = GfxRandom[x][y];
5144 GfxAction[newx][newy] = GfxAction[x][y];
5145 GfxDir[newx][newy] = GfxDir[x][y];
5149 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5151 int direction = MovDir[x][y];
5152 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5153 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5159 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5161 int oldx = x, oldy = y;
5162 int direction = MovDir[x][y];
5164 if (direction == MV_LEFT)
5166 else if (direction == MV_RIGHT)
5168 else if (direction == MV_UP)
5170 else if (direction == MV_DOWN)
5173 *comes_from_x = oldx;
5174 *comes_from_y = oldy;
5177 int MovingOrBlocked2Element(int x, int y)
5179 int element = Feld[x][y];
5181 if (element == EL_BLOCKED)
5185 Blocked2Moving(x, y, &oldx, &oldy);
5186 return Feld[oldx][oldy];
5192 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5194 /* like MovingOrBlocked2Element(), but if element is moving
5195 and (x,y) is the field the moving element is just leaving,
5196 return EL_BLOCKED instead of the element value */
5197 int element = Feld[x][y];
5199 if (IS_MOVING(x, y))
5201 if (element == EL_BLOCKED)
5205 Blocked2Moving(x, y, &oldx, &oldy);
5206 return Feld[oldx][oldy];
5215 static void RemoveField(int x, int y)
5217 Feld[x][y] = EL_EMPTY;
5223 #if USE_NEW_CUSTOM_VALUE
5224 CustomValue[x][y] = 0;
5228 ChangeDelay[x][y] = 0;
5229 ChangePage[x][y] = -1;
5230 Pushed[x][y] = FALSE;
5233 ExplodeField[x][y] = EX_TYPE_NONE;
5236 GfxElement[x][y] = EL_UNDEFINED;
5237 GfxAction[x][y] = ACTION_DEFAULT;
5238 GfxDir[x][y] = MV_NONE;
5240 /* !!! this would prevent the removed tile from being redrawn !!! */
5241 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5245 void RemoveMovingField(int x, int y)
5247 int oldx = x, oldy = y, newx = x, newy = y;
5248 int element = Feld[x][y];
5249 int next_element = EL_UNDEFINED;
5251 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5254 if (IS_MOVING(x, y))
5256 Moving2Blocked(x, y, &newx, &newy);
5258 if (Feld[newx][newy] != EL_BLOCKED)
5260 /* element is moving, but target field is not free (blocked), but
5261 already occupied by something different (example: acid pool);
5262 in this case, only remove the moving field, but not the target */
5264 RemoveField(oldx, oldy);
5266 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5268 TEST_DrawLevelField(oldx, oldy);
5273 else if (element == EL_BLOCKED)
5275 Blocked2Moving(x, y, &oldx, &oldy);
5276 if (!IS_MOVING(oldx, oldy))
5280 if (element == EL_BLOCKED &&
5281 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5282 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5283 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5284 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5285 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5286 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5287 next_element = get_next_element(Feld[oldx][oldy]);
5289 RemoveField(oldx, oldy);
5290 RemoveField(newx, newy);
5292 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5294 if (next_element != EL_UNDEFINED)
5295 Feld[oldx][oldy] = next_element;
5297 TEST_DrawLevelField(oldx, oldy);
5298 TEST_DrawLevelField(newx, newy);
5301 void DrawDynamite(int x, int y)
5303 int sx = SCREENX(x), sy = SCREENY(y);
5304 int graphic = el2img(Feld[x][y]);
5307 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5310 if (IS_WALKABLE_INSIDE(Back[x][y]))
5314 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5315 else if (Store[x][y])
5316 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5318 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5320 if (Back[x][y] || Store[x][y])
5321 DrawGraphicThruMask(sx, sy, graphic, frame);
5323 DrawGraphic(sx, sy, graphic, frame);
5326 void CheckDynamite(int x, int y)
5328 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5332 if (MovDelay[x][y] != 0)
5335 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5341 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5348 boolean num_checked_players = 0;
5351 for (i = 0; i < MAX_PLAYERS; i++)
5353 if (stored_player[i].active)
5355 int sx = stored_player[i].jx;
5356 int sy = stored_player[i].jy;
5358 if (num_checked_players == 0)
5365 *sx1 = MIN(*sx1, sx);
5366 *sy1 = MIN(*sy1, sy);
5367 *sx2 = MAX(*sx2, sx);
5368 *sy2 = MAX(*sy2, sy);
5371 num_checked_players++;
5376 static boolean checkIfAllPlayersFitToScreen_RND()
5378 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5380 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5382 return (sx2 - sx1 < SCR_FIELDX &&
5383 sy2 - sy1 < SCR_FIELDY);
5386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5388 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5390 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5392 *sx = (sx1 + sx2) / 2;
5393 *sy = (sy1 + sy2) / 2;
5396 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5397 boolean center_screen, boolean quick_relocation)
5399 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5400 boolean no_delay = (tape.warp_forward);
5401 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5402 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5404 if (quick_relocation)
5406 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5408 if (!level.shifted_relocation || center_screen)
5410 /* quick relocation (without scrolling), with centering of screen */
5412 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5413 x > SBX_Right + MIDPOSX ? SBX_Right :
5416 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5417 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5422 /* quick relocation (without scrolling), but do not center screen */
5424 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5425 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5428 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5429 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5432 int offset_x = x + (scroll_x - center_scroll_x);
5433 int offset_y = y + (scroll_y - center_scroll_y);
5435 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5436 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5437 offset_x - MIDPOSX);
5439 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441 offset_y - MIDPOSY);
5447 if (!level.shifted_relocation || center_screen)
5449 /* quick relocation (without scrolling), with centering of screen */
5451 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5452 x > SBX_Right + MIDPOSX ? SBX_Right :
5455 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5456 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5461 /* quick relocation (without scrolling), but do not center screen */
5463 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5464 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5467 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5468 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5471 int offset_x = x + (scroll_x - center_scroll_x);
5472 int offset_y = y + (scroll_y - center_scroll_y);
5474 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5475 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5476 offset_x - MIDPOSX);
5478 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5479 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5480 offset_y - MIDPOSY);
5483 /* quick relocation (without scrolling), inside visible screen area */
5485 int offset = game.scroll_delay_value;
5487 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5488 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5489 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5491 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5492 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5493 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5495 /* don't scroll over playfield boundaries */
5496 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5497 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5499 /* don't scroll over playfield boundaries */
5500 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5501 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5505 RedrawPlayfield(TRUE, 0,0,0,0);
5510 int scroll_xx, scroll_yy;
5512 if (!level.shifted_relocation || center_screen)
5514 /* visible relocation (with scrolling), with centering of screen */
5516 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5517 x > SBX_Right + MIDPOSX ? SBX_Right :
5520 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5521 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5526 /* visible relocation (with scrolling), but do not center screen */
5528 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5529 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5532 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5533 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5536 int offset_x = x + (scroll_x - center_scroll_x);
5537 int offset_y = y + (scroll_y - center_scroll_y);
5539 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5540 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5541 offset_x - MIDPOSX);
5543 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5544 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5545 offset_y - MIDPOSY);
5550 /* visible relocation (with scrolling), with centering of screen */
5552 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5553 x > SBX_Right + MIDPOSX ? SBX_Right :
5556 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5557 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5561 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5563 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5566 int fx = FX, fy = FY;
5568 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5569 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5571 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5577 fx += dx * TILEX / 2;
5578 fy += dy * TILEY / 2;
5580 ScrollLevel(dx, dy);
5583 /* scroll in two steps of half tile size to make things smoother */
5584 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5586 Delay(wait_delay_value);
5588 /* scroll second step to align at full tile size */
5590 Delay(wait_delay_value);
5595 Delay(wait_delay_value);
5599 void RelocatePlayer(int jx, int jy, int el_player_raw)
5601 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5602 int player_nr = GET_PLAYER_NR(el_player);
5603 struct PlayerInfo *player = &stored_player[player_nr];
5604 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5605 boolean no_delay = (tape.warp_forward);
5606 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5607 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5608 int old_jx = player->jx;
5609 int old_jy = player->jy;
5610 int old_element = Feld[old_jx][old_jy];
5611 int element = Feld[jx][jy];
5612 boolean player_relocated = (old_jx != jx || old_jy != jy);
5614 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5615 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5616 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5617 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5618 int leave_side_horiz = move_dir_horiz;
5619 int leave_side_vert = move_dir_vert;
5620 int enter_side = enter_side_horiz | enter_side_vert;
5621 int leave_side = leave_side_horiz | leave_side_vert;
5623 if (player->GameOver) /* do not reanimate dead player */
5626 if (!player_relocated) /* no need to relocate the player */
5629 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5631 RemoveField(jx, jy); /* temporarily remove newly placed player */
5632 DrawLevelField(jx, jy);
5635 if (player->present)
5637 while (player->MovPos)
5639 ScrollPlayer(player, SCROLL_GO_ON);
5640 ScrollScreen(NULL, SCROLL_GO_ON);
5642 AdvanceFrameAndPlayerCounters(player->index_nr);
5647 Delay(wait_delay_value);
5650 DrawPlayer(player); /* needed here only to cleanup last field */
5651 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5653 player->is_moving = FALSE;
5656 if (IS_CUSTOM_ELEMENT(old_element))
5657 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5659 player->index_bit, leave_side);
5661 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5663 player->index_bit, leave_side);
5665 Feld[jx][jy] = el_player;
5666 InitPlayerField(jx, jy, el_player, TRUE);
5668 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5669 possible that the relocation target field did not contain a player element,
5670 but a walkable element, to which the new player was relocated -- in this
5671 case, restore that (already initialized!) element on the player field */
5672 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5674 Feld[jx][jy] = element; /* restore previously existing element */
5676 /* !!! do not initialize already initialized element a second time !!! */
5677 /* (this causes at least problems with "element creation" CE trigger for
5678 already existing elements, and existing Sokoban fields counted twice) */
5679 InitField(jx, jy, FALSE);
5683 /* only visually relocate centered player */
5684 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5685 FALSE, level.instant_relocation);
5687 TestIfPlayerTouchesBadThing(jx, jy);
5688 TestIfPlayerTouchesCustomElement(jx, jy);
5690 if (IS_CUSTOM_ELEMENT(element))
5691 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5692 player->index_bit, enter_side);
5694 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5695 player->index_bit, enter_side);
5698 if (player->is_switching)
5700 /* ensure that relocation while still switching an element does not cause
5701 a new element to be treated as also switched directly after relocation
5702 (this is important for teleporter switches that teleport the player to
5703 a place where another teleporter switch is in the same direction, which
5704 would then incorrectly be treated as immediately switched before the
5705 direction key that caused the switch was released) */
5707 player->switch_x += jx - old_jx;
5708 player->switch_y += jy - old_jy;
5713 void Explode(int ex, int ey, int phase, int mode)
5719 /* !!! eliminate this variable !!! */
5720 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5722 if (game.explosions_delayed)
5724 ExplodeField[ex][ey] = mode;
5728 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5730 int center_element = Feld[ex][ey];
5731 int artwork_element, explosion_element; /* set these values later */
5734 /* --- This is only really needed (and now handled) in "Impact()". --- */
5735 /* do not explode moving elements that left the explode field in time */
5736 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5737 center_element == EL_EMPTY &&
5738 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5743 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5744 if (mode == EX_TYPE_NORMAL ||
5745 mode == EX_TYPE_CENTER ||
5746 mode == EX_TYPE_CROSS)
5747 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5750 /* remove things displayed in background while burning dynamite */
5751 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5754 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5756 /* put moving element to center field (and let it explode there) */
5757 center_element = MovingOrBlocked2Element(ex, ey);
5758 RemoveMovingField(ex, ey);
5759 Feld[ex][ey] = center_element;
5762 /* now "center_element" is finally determined -- set related values now */
5763 artwork_element = center_element; /* for custom player artwork */
5764 explosion_element = center_element; /* for custom player artwork */
5766 if (IS_PLAYER(ex, ey))
5768 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5770 artwork_element = stored_player[player_nr].artwork_element;
5772 if (level.use_explosion_element[player_nr])
5774 explosion_element = level.explosion_element[player_nr];
5775 artwork_element = explosion_element;
5780 if (mode == EX_TYPE_NORMAL ||
5781 mode == EX_TYPE_CENTER ||
5782 mode == EX_TYPE_CROSS)
5783 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5786 last_phase = element_info[explosion_element].explosion_delay + 1;
5788 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5790 int xx = x - ex + 1;
5791 int yy = y - ey + 1;
5794 if (!IN_LEV_FIELD(x, y) ||
5795 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5796 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5799 element = Feld[x][y];
5801 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5803 element = MovingOrBlocked2Element(x, y);
5805 if (!IS_EXPLOSION_PROOF(element))
5806 RemoveMovingField(x, y);
5809 /* indestructible elements can only explode in center (but not flames) */
5810 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5811 mode == EX_TYPE_BORDER)) ||
5812 element == EL_FLAMES)
5815 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5816 behaviour, for example when touching a yamyam that explodes to rocks
5817 with active deadly shield, a rock is created under the player !!! */
5818 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5820 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5821 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5822 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5824 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5827 if (IS_ACTIVE_BOMB(element))
5829 /* re-activate things under the bomb like gate or penguin */
5830 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5837 /* save walkable background elements while explosion on same tile */
5838 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5839 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5840 Back[x][y] = element;
5842 /* ignite explodable elements reached by other explosion */
5843 if (element == EL_EXPLOSION)
5844 element = Store2[x][y];
5846 if (AmoebaNr[x][y] &&
5847 (element == EL_AMOEBA_FULL ||
5848 element == EL_BD_AMOEBA ||
5849 element == EL_AMOEBA_GROWING))
5851 AmoebaCnt[AmoebaNr[x][y]]--;
5852 AmoebaCnt2[AmoebaNr[x][y]]--;
5857 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5859 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5861 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5863 if (PLAYERINFO(ex, ey)->use_murphy)
5864 Store[x][y] = EL_EMPTY;
5867 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5868 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5869 else if (ELEM_IS_PLAYER(center_element))
5870 Store[x][y] = EL_EMPTY;
5871 else if (center_element == EL_YAMYAM)
5872 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5873 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5874 Store[x][y] = element_info[center_element].content.e[xx][yy];
5876 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5877 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5878 otherwise) -- FIX THIS !!! */
5879 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5880 Store[x][y] = element_info[element].content.e[1][1];
5882 else if (!CAN_EXPLODE(element))
5883 Store[x][y] = element_info[element].content.e[1][1];
5886 Store[x][y] = EL_EMPTY;
5888 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5889 center_element == EL_AMOEBA_TO_DIAMOND)
5890 Store2[x][y] = element;
5892 Feld[x][y] = EL_EXPLOSION;
5893 GfxElement[x][y] = artwork_element;
5895 ExplodePhase[x][y] = 1;
5896 ExplodeDelay[x][y] = last_phase;
5901 if (center_element == EL_YAMYAM)
5902 game.yamyam_content_nr =
5903 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5915 GfxFrame[x][y] = 0; /* restart explosion animation */
5917 last_phase = ExplodeDelay[x][y];
5919 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5923 /* activate this even in non-DEBUG version until cause for crash in
5924 getGraphicAnimationFrame() (see below) is found and eliminated */
5930 /* this can happen if the player leaves an explosion just in time */
5931 if (GfxElement[x][y] == EL_UNDEFINED)
5932 GfxElement[x][y] = EL_EMPTY;
5934 if (GfxElement[x][y] == EL_UNDEFINED)
5937 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5938 printf("Explode(): This should never happen!\n");
5941 GfxElement[x][y] = EL_EMPTY;
5947 border_element = Store2[x][y];
5948 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5949 border_element = StorePlayer[x][y];
5951 if (phase == element_info[border_element].ignition_delay ||
5952 phase == last_phase)
5954 boolean border_explosion = FALSE;
5956 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5957 !PLAYER_EXPLOSION_PROTECTED(x, y))
5959 KillPlayerUnlessExplosionProtected(x, y);
5960 border_explosion = TRUE;
5962 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5964 Feld[x][y] = Store2[x][y];
5967 border_explosion = TRUE;
5969 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5971 AmoebeUmwandeln(x, y);
5973 border_explosion = TRUE;
5976 /* if an element just explodes due to another explosion (chain-reaction),
5977 do not immediately end the new explosion when it was the last frame of
5978 the explosion (as it would be done in the following "if"-statement!) */
5979 if (border_explosion && phase == last_phase)
5983 if (phase == last_phase)
5987 element = Feld[x][y] = Store[x][y];
5988 Store[x][y] = Store2[x][y] = 0;
5989 GfxElement[x][y] = EL_UNDEFINED;
5991 /* player can escape from explosions and might therefore be still alive */
5992 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5993 element <= EL_PLAYER_IS_EXPLODING_4)
5995 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5996 int explosion_element = EL_PLAYER_1 + player_nr;
5997 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5998 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6000 if (level.use_explosion_element[player_nr])
6001 explosion_element = level.explosion_element[player_nr];
6003 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6004 element_info[explosion_element].content.e[xx][yy]);
6007 /* restore probably existing indestructible background element */
6008 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6009 element = Feld[x][y] = Back[x][y];
6012 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6013 GfxDir[x][y] = MV_NONE;
6014 ChangeDelay[x][y] = 0;
6015 ChangePage[x][y] = -1;
6017 #if USE_NEW_CUSTOM_VALUE
6018 CustomValue[x][y] = 0;
6021 InitField_WithBug2(x, y, FALSE);
6023 TEST_DrawLevelField(x, y);
6025 TestIfElementTouchesCustomElement(x, y);
6027 if (GFX_CRUMBLED(element))
6028 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6030 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6031 StorePlayer[x][y] = 0;
6033 if (ELEM_IS_PLAYER(element))
6034 RelocatePlayer(x, y, element);
6036 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6038 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6039 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6042 TEST_DrawLevelFieldCrumbled(x, y);
6044 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6046 DrawLevelElement(x, y, Back[x][y]);
6047 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6049 else if (IS_WALKABLE_UNDER(Back[x][y]))
6051 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6052 DrawLevelElementThruMask(x, y, Back[x][y]);
6054 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6055 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6059 void DynaExplode(int ex, int ey)
6062 int dynabomb_element = Feld[ex][ey];
6063 int dynabomb_size = 1;
6064 boolean dynabomb_xl = FALSE;
6065 struct PlayerInfo *player;
6066 static int xy[4][2] =
6074 if (IS_ACTIVE_BOMB(dynabomb_element))
6076 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6077 dynabomb_size = player->dynabomb_size;
6078 dynabomb_xl = player->dynabomb_xl;
6079 player->dynabombs_left++;
6082 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6084 for (i = 0; i < NUM_DIRECTIONS; i++)
6086 for (j = 1; j <= dynabomb_size; j++)
6088 int x = ex + j * xy[i][0];
6089 int y = ey + j * xy[i][1];
6092 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6095 element = Feld[x][y];
6097 /* do not restart explosions of fields with active bombs */
6098 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6101 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6103 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6104 !IS_DIGGABLE(element) && !dynabomb_xl)
6110 void Bang(int x, int y)
6112 int element = MovingOrBlocked2Element(x, y);
6113 int explosion_type = EX_TYPE_NORMAL;
6115 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6117 struct PlayerInfo *player = PLAYERINFO(x, y);
6119 #if USE_FIX_CE_ACTION_WITH_PLAYER
6120 element = Feld[x][y] = player->initial_element;
6122 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6123 player->element_nr);
6126 if (level.use_explosion_element[player->index_nr])
6128 int explosion_element = level.explosion_element[player->index_nr];
6130 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6131 explosion_type = EX_TYPE_CROSS;
6132 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6133 explosion_type = EX_TYPE_CENTER;
6141 case EL_BD_BUTTERFLY:
6144 case EL_DARK_YAMYAM:
6148 RaiseScoreElement(element);
6151 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6152 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6153 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6154 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6155 case EL_DYNABOMB_INCREASE_NUMBER:
6156 case EL_DYNABOMB_INCREASE_SIZE:
6157 case EL_DYNABOMB_INCREASE_POWER:
6158 explosion_type = EX_TYPE_DYNA;
6161 case EL_DC_LANDMINE:
6163 case EL_EM_EXIT_OPEN:
6164 case EL_EM_STEEL_EXIT_OPEN:
6166 explosion_type = EX_TYPE_CENTER;
6171 case EL_LAMP_ACTIVE:
6172 case EL_AMOEBA_TO_DIAMOND:
6173 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6174 explosion_type = EX_TYPE_CENTER;
6178 if (element_info[element].explosion_type == EXPLODES_CROSS)
6179 explosion_type = EX_TYPE_CROSS;
6180 else if (element_info[element].explosion_type == EXPLODES_1X1)
6181 explosion_type = EX_TYPE_CENTER;
6185 if (explosion_type == EX_TYPE_DYNA)
6188 Explode(x, y, EX_PHASE_START, explosion_type);
6190 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6193 void SplashAcid(int x, int y)
6195 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6196 (!IN_LEV_FIELD(x - 1, y - 2) ||
6197 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6198 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6200 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6201 (!IN_LEV_FIELD(x + 1, y - 2) ||
6202 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6203 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6205 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6208 static void InitBeltMovement()
6210 static int belt_base_element[4] =
6212 EL_CONVEYOR_BELT_1_LEFT,
6213 EL_CONVEYOR_BELT_2_LEFT,
6214 EL_CONVEYOR_BELT_3_LEFT,
6215 EL_CONVEYOR_BELT_4_LEFT
6217 static int belt_base_active_element[4] =
6219 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6220 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6221 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6222 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6227 /* set frame order for belt animation graphic according to belt direction */
6228 for (i = 0; i < NUM_BELTS; i++)
6232 for (j = 0; j < NUM_BELT_PARTS; j++)
6234 int element = belt_base_active_element[belt_nr] + j;
6235 int graphic_1 = el2img(element);
6236 int graphic_2 = el2panelimg(element);
6238 if (game.belt_dir[i] == MV_LEFT)
6240 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6241 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6245 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6246 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6251 SCAN_PLAYFIELD(x, y)
6253 int element = Feld[x][y];
6255 for (i = 0; i < NUM_BELTS; i++)
6257 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6259 int e_belt_nr = getBeltNrFromBeltElement(element);
6262 if (e_belt_nr == belt_nr)
6264 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6266 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6273 static void ToggleBeltSwitch(int x, int y)
6275 static int belt_base_element[4] =
6277 EL_CONVEYOR_BELT_1_LEFT,
6278 EL_CONVEYOR_BELT_2_LEFT,
6279 EL_CONVEYOR_BELT_3_LEFT,
6280 EL_CONVEYOR_BELT_4_LEFT
6282 static int belt_base_active_element[4] =
6284 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6285 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6286 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6287 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6289 static int belt_base_switch_element[4] =
6291 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6292 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6293 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6294 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6296 static int belt_move_dir[4] =
6304 int element = Feld[x][y];
6305 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6306 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6307 int belt_dir = belt_move_dir[belt_dir_nr];
6310 if (!IS_BELT_SWITCH(element))
6313 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6314 game.belt_dir[belt_nr] = belt_dir;
6316 if (belt_dir_nr == 3)
6319 /* set frame order for belt animation graphic according to belt direction */
6320 for (i = 0; i < NUM_BELT_PARTS; i++)
6322 int element = belt_base_active_element[belt_nr] + i;
6323 int graphic_1 = el2img(element);
6324 int graphic_2 = el2panelimg(element);
6326 if (belt_dir == MV_LEFT)
6328 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6329 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6333 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6334 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6338 SCAN_PLAYFIELD(xx, yy)
6340 int element = Feld[xx][yy];
6342 if (IS_BELT_SWITCH(element))
6344 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6346 if (e_belt_nr == belt_nr)
6348 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6349 TEST_DrawLevelField(xx, yy);
6352 else if (IS_BELT(element) && belt_dir != MV_NONE)
6354 int e_belt_nr = getBeltNrFromBeltElement(element);
6356 if (e_belt_nr == belt_nr)
6358 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6360 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6361 TEST_DrawLevelField(xx, yy);
6364 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6366 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6368 if (e_belt_nr == belt_nr)
6370 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6372 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6373 TEST_DrawLevelField(xx, yy);
6379 static void ToggleSwitchgateSwitch(int x, int y)
6383 game.switchgate_pos = !game.switchgate_pos;
6385 SCAN_PLAYFIELD(xx, yy)
6387 int element = Feld[xx][yy];
6389 #if !USE_BOTH_SWITCHGATE_SWITCHES
6390 if (element == EL_SWITCHGATE_SWITCH_UP ||
6391 element == EL_SWITCHGATE_SWITCH_DOWN)
6393 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6394 TEST_DrawLevelField(xx, yy);
6396 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6397 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6399 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6400 TEST_DrawLevelField(xx, yy);
6403 if (element == EL_SWITCHGATE_SWITCH_UP)
6405 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6406 TEST_DrawLevelField(xx, yy);
6408 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6410 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6411 TEST_DrawLevelField(xx, yy);
6413 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6415 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6416 TEST_DrawLevelField(xx, yy);
6418 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6420 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6421 TEST_DrawLevelField(xx, yy);
6424 else if (element == EL_SWITCHGATE_OPEN ||
6425 element == EL_SWITCHGATE_OPENING)
6427 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6429 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6431 else if (element == EL_SWITCHGATE_CLOSED ||
6432 element == EL_SWITCHGATE_CLOSING)
6434 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6436 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6441 static int getInvisibleActiveFromInvisibleElement(int element)
6443 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6444 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6445 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6449 static int getInvisibleFromInvisibleActiveElement(int element)
6451 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6452 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6453 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6457 static void RedrawAllLightSwitchesAndInvisibleElements()
6461 SCAN_PLAYFIELD(x, y)
6463 int element = Feld[x][y];
6465 if (element == EL_LIGHT_SWITCH &&
6466 game.light_time_left > 0)
6468 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6469 TEST_DrawLevelField(x, y);
6471 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6472 game.light_time_left == 0)
6474 Feld[x][y] = EL_LIGHT_SWITCH;
6475 TEST_DrawLevelField(x, y);
6477 else if (element == EL_EMC_DRIPPER &&
6478 game.light_time_left > 0)
6480 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6481 TEST_DrawLevelField(x, y);
6483 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6484 game.light_time_left == 0)
6486 Feld[x][y] = EL_EMC_DRIPPER;
6487 TEST_DrawLevelField(x, y);
6489 else if (element == EL_INVISIBLE_STEELWALL ||
6490 element == EL_INVISIBLE_WALL ||
6491 element == EL_INVISIBLE_SAND)
6493 if (game.light_time_left > 0)
6494 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6496 TEST_DrawLevelField(x, y);
6498 /* uncrumble neighbour fields, if needed */
6499 if (element == EL_INVISIBLE_SAND)
6500 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6502 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6503 element == EL_INVISIBLE_WALL_ACTIVE ||
6504 element == EL_INVISIBLE_SAND_ACTIVE)
6506 if (game.light_time_left == 0)
6507 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6509 TEST_DrawLevelField(x, y);
6511 /* re-crumble neighbour fields, if needed */
6512 if (element == EL_INVISIBLE_SAND)
6513 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6518 static void RedrawAllInvisibleElementsForLenses()
6522 SCAN_PLAYFIELD(x, y)
6524 int element = Feld[x][y];
6526 if (element == EL_EMC_DRIPPER &&
6527 game.lenses_time_left > 0)
6529 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6530 TEST_DrawLevelField(x, y);
6532 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6533 game.lenses_time_left == 0)
6535 Feld[x][y] = EL_EMC_DRIPPER;
6536 TEST_DrawLevelField(x, y);
6538 else if (element == EL_INVISIBLE_STEELWALL ||
6539 element == EL_INVISIBLE_WALL ||
6540 element == EL_INVISIBLE_SAND)
6542 if (game.lenses_time_left > 0)
6543 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6545 TEST_DrawLevelField(x, y);
6547 /* uncrumble neighbour fields, if needed */
6548 if (element == EL_INVISIBLE_SAND)
6549 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6551 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6552 element == EL_INVISIBLE_WALL_ACTIVE ||
6553 element == EL_INVISIBLE_SAND_ACTIVE)
6555 if (game.lenses_time_left == 0)
6556 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6558 TEST_DrawLevelField(x, y);
6560 /* re-crumble neighbour fields, if needed */
6561 if (element == EL_INVISIBLE_SAND)
6562 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6567 static void RedrawAllInvisibleElementsForMagnifier()
6571 SCAN_PLAYFIELD(x, y)
6573 int element = Feld[x][y];
6575 if (element == EL_EMC_FAKE_GRASS &&
6576 game.magnify_time_left > 0)
6578 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6579 TEST_DrawLevelField(x, y);
6581 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6582 game.magnify_time_left == 0)
6584 Feld[x][y] = EL_EMC_FAKE_GRASS;
6585 TEST_DrawLevelField(x, y);
6587 else if (IS_GATE_GRAY(element) &&
6588 game.magnify_time_left > 0)
6590 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6591 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6592 IS_EM_GATE_GRAY(element) ?
6593 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6594 IS_EMC_GATE_GRAY(element) ?
6595 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6596 IS_DC_GATE_GRAY(element) ?
6597 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6599 TEST_DrawLevelField(x, y);
6601 else if (IS_GATE_GRAY_ACTIVE(element) &&
6602 game.magnify_time_left == 0)
6604 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6605 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6606 IS_EM_GATE_GRAY_ACTIVE(element) ?
6607 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6608 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6609 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6610 IS_DC_GATE_GRAY_ACTIVE(element) ?
6611 EL_DC_GATE_WHITE_GRAY :
6613 TEST_DrawLevelField(x, y);
6618 static void ToggleLightSwitch(int x, int y)
6620 int element = Feld[x][y];
6622 game.light_time_left =
6623 (element == EL_LIGHT_SWITCH ?
6624 level.time_light * FRAMES_PER_SECOND : 0);
6626 RedrawAllLightSwitchesAndInvisibleElements();
6629 static void ActivateTimegateSwitch(int x, int y)
6633 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6635 SCAN_PLAYFIELD(xx, yy)
6637 int element = Feld[xx][yy];
6639 if (element == EL_TIMEGATE_CLOSED ||
6640 element == EL_TIMEGATE_CLOSING)
6642 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6643 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6647 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6649 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6650 TEST_DrawLevelField(xx, yy);
6657 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6658 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6660 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6664 void Impact(int x, int y)
6666 boolean last_line = (y == lev_fieldy - 1);
6667 boolean object_hit = FALSE;
6668 boolean impact = (last_line || object_hit);
6669 int element = Feld[x][y];
6670 int smashed = EL_STEELWALL;
6672 if (!last_line) /* check if element below was hit */
6674 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6677 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6678 MovDir[x][y + 1] != MV_DOWN ||
6679 MovPos[x][y + 1] <= TILEY / 2));
6681 /* do not smash moving elements that left the smashed field in time */
6682 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6683 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6686 #if USE_QUICKSAND_IMPACT_BUGFIX
6687 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6689 RemoveMovingField(x, y + 1);
6690 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6691 Feld[x][y + 2] = EL_ROCK;
6692 TEST_DrawLevelField(x, y + 2);
6697 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6699 RemoveMovingField(x, y + 1);
6700 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6701 Feld[x][y + 2] = EL_ROCK;
6702 TEST_DrawLevelField(x, y + 2);
6709 smashed = MovingOrBlocked2Element(x, y + 1);
6711 impact = (last_line || object_hit);
6714 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6716 SplashAcid(x, y + 1);
6720 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6721 /* only reset graphic animation if graphic really changes after impact */
6723 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6725 ResetGfxAnimation(x, y);
6726 TEST_DrawLevelField(x, y);
6729 if (impact && CAN_EXPLODE_IMPACT(element))
6734 else if (impact && element == EL_PEARL &&
6735 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6737 ResetGfxAnimation(x, y);
6739 Feld[x][y] = EL_PEARL_BREAKING;
6740 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6743 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6745 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6750 if (impact && element == EL_AMOEBA_DROP)
6752 if (object_hit && IS_PLAYER(x, y + 1))
6753 KillPlayerUnlessEnemyProtected(x, y + 1);
6754 else if (object_hit && smashed == EL_PENGUIN)
6758 Feld[x][y] = EL_AMOEBA_GROWING;
6759 Store[x][y] = EL_AMOEBA_WET;
6761 ResetRandomAnimationValue(x, y);
6766 if (object_hit) /* check which object was hit */
6768 if ((CAN_PASS_MAGIC_WALL(element) &&
6769 (smashed == EL_MAGIC_WALL ||
6770 smashed == EL_BD_MAGIC_WALL)) ||
6771 (CAN_PASS_DC_MAGIC_WALL(element) &&
6772 smashed == EL_DC_MAGIC_WALL))
6775 int activated_magic_wall =
6776 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6777 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6778 EL_DC_MAGIC_WALL_ACTIVE);
6780 /* activate magic wall / mill */
6781 SCAN_PLAYFIELD(xx, yy)
6783 if (Feld[xx][yy] == smashed)
6784 Feld[xx][yy] = activated_magic_wall;
6787 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6788 game.magic_wall_active = TRUE;
6790 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6791 SND_MAGIC_WALL_ACTIVATING :
6792 smashed == EL_BD_MAGIC_WALL ?
6793 SND_BD_MAGIC_WALL_ACTIVATING :
6794 SND_DC_MAGIC_WALL_ACTIVATING));
6797 if (IS_PLAYER(x, y + 1))
6799 if (CAN_SMASH_PLAYER(element))
6801 KillPlayerUnlessEnemyProtected(x, y + 1);
6805 else if (smashed == EL_PENGUIN)
6807 if (CAN_SMASH_PLAYER(element))
6813 else if (element == EL_BD_DIAMOND)
6815 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6821 else if (((element == EL_SP_INFOTRON ||
6822 element == EL_SP_ZONK) &&
6823 (smashed == EL_SP_SNIKSNAK ||
6824 smashed == EL_SP_ELECTRON ||
6825 smashed == EL_SP_DISK_ORANGE)) ||
6826 (element == EL_SP_INFOTRON &&
6827 smashed == EL_SP_DISK_YELLOW))
6832 else if (CAN_SMASH_EVERYTHING(element))
6834 if (IS_CLASSIC_ENEMY(smashed) ||
6835 CAN_EXPLODE_SMASHED(smashed))
6840 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6842 if (smashed == EL_LAMP ||
6843 smashed == EL_LAMP_ACTIVE)
6848 else if (smashed == EL_NUT)
6850 Feld[x][y + 1] = EL_NUT_BREAKING;
6851 PlayLevelSound(x, y, SND_NUT_BREAKING);
6852 RaiseScoreElement(EL_NUT);
6855 else if (smashed == EL_PEARL)
6857 ResetGfxAnimation(x, y);
6859 Feld[x][y + 1] = EL_PEARL_BREAKING;
6860 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6863 else if (smashed == EL_DIAMOND)
6865 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6866 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6869 else if (IS_BELT_SWITCH(smashed))
6871 ToggleBeltSwitch(x, y + 1);
6873 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6874 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6875 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6876 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6878 ToggleSwitchgateSwitch(x, y + 1);
6880 else if (smashed == EL_LIGHT_SWITCH ||
6881 smashed == EL_LIGHT_SWITCH_ACTIVE)
6883 ToggleLightSwitch(x, y + 1);
6888 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6891 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6893 CheckElementChangeBySide(x, y + 1, smashed, element,
6894 CE_SWITCHED, CH_SIDE_TOP);
6895 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6901 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6906 /* play sound of magic wall / mill */
6908 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6912 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6922 /* play sound of object that hits the ground */
6923 if (last_line || object_hit)
6924 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6927 inline static void TurnRoundExt(int x, int y)
6939 { 0, 0 }, { 0, 0 }, { 0, 0 },
6944 int left, right, back;
6948 { MV_DOWN, MV_UP, MV_RIGHT },
6949 { MV_UP, MV_DOWN, MV_LEFT },
6951 { MV_LEFT, MV_RIGHT, MV_DOWN },
6955 { MV_RIGHT, MV_LEFT, MV_UP }
6958 int element = Feld[x][y];
6959 int move_pattern = element_info[element].move_pattern;
6961 int old_move_dir = MovDir[x][y];
6962 int left_dir = turn[old_move_dir].left;
6963 int right_dir = turn[old_move_dir].right;
6964 int back_dir = turn[old_move_dir].back;
6966 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6967 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6968 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6969 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6971 int left_x = x + left_dx, left_y = y + left_dy;
6972 int right_x = x + right_dx, right_y = y + right_dy;
6973 int move_x = x + move_dx, move_y = y + move_dy;
6977 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6979 TestIfBadThingTouchesOtherBadThing(x, y);
6981 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982 MovDir[x][y] = right_dir;
6983 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984 MovDir[x][y] = left_dir;
6986 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6988 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6991 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6993 TestIfBadThingTouchesOtherBadThing(x, y);
6995 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996 MovDir[x][y] = left_dir;
6997 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998 MovDir[x][y] = right_dir;
7000 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7002 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7005 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7007 TestIfBadThingTouchesOtherBadThing(x, y);
7009 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010 MovDir[x][y] = left_dir;
7011 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012 MovDir[x][y] = right_dir;
7014 if (MovDir[x][y] != old_move_dir)
7017 else if (element == EL_YAMYAM)
7019 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7022 if (can_turn_left && can_turn_right)
7023 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024 else if (can_turn_left)
7025 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026 else if (can_turn_right)
7027 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7029 MovDir[x][y] = back_dir;
7031 MovDelay[x][y] = 16 + 16 * RND(3);
7033 else if (element == EL_DARK_YAMYAM)
7035 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7037 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7040 if (can_turn_left && can_turn_right)
7041 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042 else if (can_turn_left)
7043 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044 else if (can_turn_right)
7045 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7047 MovDir[x][y] = back_dir;
7049 MovDelay[x][y] = 16 + 16 * RND(3);
7051 else if (element == EL_PACMAN)
7053 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7056 if (can_turn_left && can_turn_right)
7057 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058 else if (can_turn_left)
7059 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060 else if (can_turn_right)
7061 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7063 MovDir[x][y] = back_dir;
7065 MovDelay[x][y] = 6 + RND(40);
7067 else if (element == EL_PIG)
7069 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072 boolean should_turn_left, should_turn_right, should_move_on;
7074 int rnd = RND(rnd_value);
7076 should_turn_left = (can_turn_left &&
7078 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079 y + back_dy + left_dy)));
7080 should_turn_right = (can_turn_right &&
7082 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083 y + back_dy + right_dy)));
7084 should_move_on = (can_move_on &&
7087 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088 y + move_dy + left_dy) ||
7089 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090 y + move_dy + right_dy)));
7092 if (should_turn_left || should_turn_right || should_move_on)
7094 if (should_turn_left && should_turn_right && should_move_on)
7095 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7096 rnd < 2 * rnd_value / 3 ? right_dir :
7098 else if (should_turn_left && should_turn_right)
7099 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100 else if (should_turn_left && should_move_on)
7101 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102 else if (should_turn_right && should_move_on)
7103 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104 else if (should_turn_left)
7105 MovDir[x][y] = left_dir;
7106 else if (should_turn_right)
7107 MovDir[x][y] = right_dir;
7108 else if (should_move_on)
7109 MovDir[x][y] = old_move_dir;
7111 else if (can_move_on && rnd > rnd_value / 8)
7112 MovDir[x][y] = old_move_dir;
7113 else if (can_turn_left && can_turn_right)
7114 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115 else if (can_turn_left && rnd > rnd_value / 8)
7116 MovDir[x][y] = left_dir;
7117 else if (can_turn_right && rnd > rnd_value/8)
7118 MovDir[x][y] = right_dir;
7120 MovDir[x][y] = back_dir;
7122 xx = x + move_xy[MovDir[x][y]].dx;
7123 yy = y + move_xy[MovDir[x][y]].dy;
7125 if (!IN_LEV_FIELD(xx, yy) ||
7126 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7127 MovDir[x][y] = old_move_dir;
7131 else if (element == EL_DRAGON)
7133 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7137 int rnd = RND(rnd_value);
7139 if (can_move_on && rnd > rnd_value / 8)
7140 MovDir[x][y] = old_move_dir;
7141 else if (can_turn_left && can_turn_right)
7142 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143 else if (can_turn_left && rnd > rnd_value / 8)
7144 MovDir[x][y] = left_dir;
7145 else if (can_turn_right && rnd > rnd_value / 8)
7146 MovDir[x][y] = right_dir;
7148 MovDir[x][y] = back_dir;
7150 xx = x + move_xy[MovDir[x][y]].dx;
7151 yy = y + move_xy[MovDir[x][y]].dy;
7153 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154 MovDir[x][y] = old_move_dir;
7158 else if (element == EL_MOLE)
7160 boolean can_move_on =
7161 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162 IS_AMOEBOID(Feld[move_x][move_y]) ||
7163 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7166 boolean can_turn_left =
7167 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168 IS_AMOEBOID(Feld[left_x][left_y])));
7170 boolean can_turn_right =
7171 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172 IS_AMOEBOID(Feld[right_x][right_y])));
7174 if (can_turn_left && can_turn_right)
7175 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176 else if (can_turn_left)
7177 MovDir[x][y] = left_dir;
7179 MovDir[x][y] = right_dir;
7182 if (MovDir[x][y] != old_move_dir)
7185 else if (element == EL_BALLOON)
7187 MovDir[x][y] = game.wind_direction;
7190 else if (element == EL_SPRING)
7192 #if USE_NEW_SPRING_BUMPER
7193 if (MovDir[x][y] & MV_HORIZONTAL)
7195 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7196 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7198 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7199 ResetGfxAnimation(move_x, move_y);
7200 TEST_DrawLevelField(move_x, move_y);
7202 MovDir[x][y] = back_dir;
7204 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7205 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7206 MovDir[x][y] = MV_NONE;
7209 if (MovDir[x][y] & MV_HORIZONTAL &&
7210 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7211 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7212 MovDir[x][y] = MV_NONE;
7217 else if (element == EL_ROBOT ||
7218 element == EL_SATELLITE ||
7219 element == EL_PENGUIN ||
7220 element == EL_EMC_ANDROID)
7222 int attr_x = -1, attr_y = -1;
7233 for (i = 0; i < MAX_PLAYERS; i++)
7235 struct PlayerInfo *player = &stored_player[i];
7236 int jx = player->jx, jy = player->jy;
7238 if (!player->active)
7242 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7250 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7251 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7252 game.engine_version < VERSION_IDENT(3,1,0,0)))
7258 if (element == EL_PENGUIN)
7261 static int xy[4][2] =
7269 for (i = 0; i < NUM_DIRECTIONS; i++)
7271 int ex = x + xy[i][0];
7272 int ey = y + xy[i][1];
7274 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7275 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7276 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7277 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7286 MovDir[x][y] = MV_NONE;
7288 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7289 else if (attr_x > x)
7290 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7292 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7293 else if (attr_y > y)
7294 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7296 if (element == EL_ROBOT)
7300 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7302 Moving2Blocked(x, y, &newx, &newy);
7304 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7305 MovDelay[x][y] = 8 + 8 * !RND(3);
7307 MovDelay[x][y] = 16;
7309 else if (element == EL_PENGUIN)
7315 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7317 boolean first_horiz = RND(2);
7318 int new_move_dir = MovDir[x][y];
7321 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322 Moving2Blocked(x, y, &newx, &newy);
7324 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7328 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329 Moving2Blocked(x, y, &newx, &newy);
7331 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7334 MovDir[x][y] = old_move_dir;
7338 else if (element == EL_SATELLITE)
7344 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7346 boolean first_horiz = RND(2);
7347 int new_move_dir = MovDir[x][y];
7350 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7351 Moving2Blocked(x, y, &newx, &newy);
7353 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7357 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358 Moving2Blocked(x, y, &newx, &newy);
7360 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7363 MovDir[x][y] = old_move_dir;
7367 else if (element == EL_EMC_ANDROID)
7369 static int check_pos[16] =
7371 -1, /* 0 => (invalid) */
7372 7, /* 1 => MV_LEFT */
7373 3, /* 2 => MV_RIGHT */
7374 -1, /* 3 => (invalid) */
7376 0, /* 5 => MV_LEFT | MV_UP */
7377 2, /* 6 => MV_RIGHT | MV_UP */
7378 -1, /* 7 => (invalid) */
7379 5, /* 8 => MV_DOWN */
7380 6, /* 9 => MV_LEFT | MV_DOWN */
7381 4, /* 10 => MV_RIGHT | MV_DOWN */
7382 -1, /* 11 => (invalid) */
7383 -1, /* 12 => (invalid) */
7384 -1, /* 13 => (invalid) */
7385 -1, /* 14 => (invalid) */
7386 -1, /* 15 => (invalid) */
7394 { -1, -1, MV_LEFT | MV_UP },
7396 { +1, -1, MV_RIGHT | MV_UP },
7397 { +1, 0, MV_RIGHT },
7398 { +1, +1, MV_RIGHT | MV_DOWN },
7400 { -1, +1, MV_LEFT | MV_DOWN },
7403 int start_pos, check_order;
7404 boolean can_clone = FALSE;
7407 /* check if there is any free field around current position */
7408 for (i = 0; i < 8; i++)
7410 int newx = x + check_xy[i].dx;
7411 int newy = y + check_xy[i].dy;
7413 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7421 if (can_clone) /* randomly find an element to clone */
7425 start_pos = check_pos[RND(8)];
7426 check_order = (RND(2) ? -1 : +1);
7428 for (i = 0; i < 8; i++)
7430 int pos_raw = start_pos + i * check_order;
7431 int pos = (pos_raw + 8) % 8;
7432 int newx = x + check_xy[pos].dx;
7433 int newy = y + check_xy[pos].dy;
7435 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7437 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7438 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7440 Store[x][y] = Feld[newx][newy];
7449 if (can_clone) /* randomly find a direction to move */
7453 start_pos = check_pos[RND(8)];
7454 check_order = (RND(2) ? -1 : +1);
7456 for (i = 0; i < 8; i++)
7458 int pos_raw = start_pos + i * check_order;
7459 int pos = (pos_raw + 8) % 8;
7460 int newx = x + check_xy[pos].dx;
7461 int newy = y + check_xy[pos].dy;
7462 int new_move_dir = check_xy[pos].dir;
7464 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7466 MovDir[x][y] = new_move_dir;
7467 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7476 if (can_clone) /* cloning and moving successful */
7479 /* cannot clone -- try to move towards player */
7481 start_pos = check_pos[MovDir[x][y] & 0x0f];
7482 check_order = (RND(2) ? -1 : +1);
7484 for (i = 0; i < 3; i++)
7486 /* first check start_pos, then previous/next or (next/previous) pos */
7487 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7488 int pos = (pos_raw + 8) % 8;
7489 int newx = x + check_xy[pos].dx;
7490 int newy = y + check_xy[pos].dy;
7491 int new_move_dir = check_xy[pos].dir;
7493 if (IS_PLAYER(newx, newy))
7496 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7498 MovDir[x][y] = new_move_dir;
7499 MovDelay[x][y] = level.android_move_time * 8 + 1;
7506 else if (move_pattern == MV_TURNING_LEFT ||
7507 move_pattern == MV_TURNING_RIGHT ||
7508 move_pattern == MV_TURNING_LEFT_RIGHT ||
7509 move_pattern == MV_TURNING_RIGHT_LEFT ||
7510 move_pattern == MV_TURNING_RANDOM ||
7511 move_pattern == MV_ALL_DIRECTIONS)
7513 boolean can_turn_left =
7514 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7515 boolean can_turn_right =
7516 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7518 if (element_info[element].move_stepsize == 0) /* "not moving" */
7521 if (move_pattern == MV_TURNING_LEFT)
7522 MovDir[x][y] = left_dir;
7523 else if (move_pattern == MV_TURNING_RIGHT)
7524 MovDir[x][y] = right_dir;
7525 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7526 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7527 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7528 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7529 else if (move_pattern == MV_TURNING_RANDOM)
7530 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7531 can_turn_right && !can_turn_left ? right_dir :
7532 RND(2) ? left_dir : right_dir);
7533 else if (can_turn_left && can_turn_right)
7534 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7535 else if (can_turn_left)
7536 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7537 else if (can_turn_right)
7538 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7540 MovDir[x][y] = back_dir;
7542 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7544 else if (move_pattern == MV_HORIZONTAL ||
7545 move_pattern == MV_VERTICAL)
7547 if (move_pattern & old_move_dir)
7548 MovDir[x][y] = back_dir;
7549 else if (move_pattern == MV_HORIZONTAL)
7550 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7551 else if (move_pattern == MV_VERTICAL)
7552 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7554 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7556 else if (move_pattern & MV_ANY_DIRECTION)
7558 MovDir[x][y] = move_pattern;
7559 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7561 else if (move_pattern & MV_WIND_DIRECTION)
7563 MovDir[x][y] = game.wind_direction;
7564 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7566 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7568 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7569 MovDir[x][y] = left_dir;
7570 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7571 MovDir[x][y] = right_dir;
7573 if (MovDir[x][y] != old_move_dir)
7574 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7576 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7578 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7579 MovDir[x][y] = right_dir;
7580 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7581 MovDir[x][y] = left_dir;
7583 if (MovDir[x][y] != old_move_dir)
7584 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7586 else if (move_pattern == MV_TOWARDS_PLAYER ||
7587 move_pattern == MV_AWAY_FROM_PLAYER)
7589 int attr_x = -1, attr_y = -1;
7591 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7602 for (i = 0; i < MAX_PLAYERS; i++)
7604 struct PlayerInfo *player = &stored_player[i];
7605 int jx = player->jx, jy = player->jy;
7607 if (!player->active)
7611 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7619 MovDir[x][y] = MV_NONE;
7621 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7622 else if (attr_x > x)
7623 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7625 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7626 else if (attr_y > y)
7627 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7629 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7631 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7633 boolean first_horiz = RND(2);
7634 int new_move_dir = MovDir[x][y];
7636 if (element_info[element].move_stepsize == 0) /* "not moving" */
7638 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7639 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7645 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7646 Moving2Blocked(x, y, &newx, &newy);
7648 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7652 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7653 Moving2Blocked(x, y, &newx, &newy);
7655 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7658 MovDir[x][y] = old_move_dir;
7661 else if (move_pattern == MV_WHEN_PUSHED ||
7662 move_pattern == MV_WHEN_DROPPED)
7664 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7665 MovDir[x][y] = MV_NONE;
7669 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7671 static int test_xy[7][2] =
7681 static int test_dir[7] =
7691 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7692 int move_preference = -1000000; /* start with very low preference */
7693 int new_move_dir = MV_NONE;
7694 int start_test = RND(4);
7697 for (i = 0; i < NUM_DIRECTIONS; i++)
7699 int move_dir = test_dir[start_test + i];
7700 int move_dir_preference;
7702 xx = x + test_xy[start_test + i][0];
7703 yy = y + test_xy[start_test + i][1];
7705 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7708 new_move_dir = move_dir;
7713 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7716 move_dir_preference = -1 * RunnerVisit[xx][yy];
7717 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718 move_dir_preference = PlayerVisit[xx][yy];
7720 if (move_dir_preference > move_preference)
7722 /* prefer field that has not been visited for the longest time */
7723 move_preference = move_dir_preference;
7724 new_move_dir = move_dir;
7726 else if (move_dir_preference == move_preference &&
7727 move_dir == old_move_dir)
7729 /* prefer last direction when all directions are preferred equally */
7730 move_preference = move_dir_preference;
7731 new_move_dir = move_dir;
7735 MovDir[x][y] = new_move_dir;
7736 if (old_move_dir != new_move_dir)
7737 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7741 static void TurnRound(int x, int y)
7743 int direction = MovDir[x][y];
7747 GfxDir[x][y] = MovDir[x][y];
7749 if (direction != MovDir[x][y])
7753 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7755 ResetGfxFrame(x, y, FALSE);
7758 static boolean JustBeingPushed(int x, int y)
7762 for (i = 0; i < MAX_PLAYERS; i++)
7764 struct PlayerInfo *player = &stored_player[i];
7766 if (player->active && player->is_pushing && player->MovPos)
7768 int next_jx = player->jx + (player->jx - player->last_jx);
7769 int next_jy = player->jy + (player->jy - player->last_jy);
7771 if (x == next_jx && y == next_jy)
7779 void StartMoving(int x, int y)
7781 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7782 int element = Feld[x][y];
7787 if (MovDelay[x][y] == 0)
7788 GfxAction[x][y] = ACTION_DEFAULT;
7790 if (CAN_FALL(element) && y < lev_fieldy - 1)
7792 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7793 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794 if (JustBeingPushed(x, y))
7797 if (element == EL_QUICKSAND_FULL)
7799 if (IS_FREE(x, y + 1))
7801 InitMovingField(x, y, MV_DOWN);
7802 started_moving = TRUE;
7804 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807 Store[x][y] = EL_ROCK;
7809 Store[x][y] = EL_ROCK;
7812 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7814 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7816 if (!MovDelay[x][y])
7818 MovDelay[x][y] = TILEY + 1;
7820 ResetGfxAnimation(x, y);
7821 ResetGfxAnimation(x, y + 1);
7826 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7834 Feld[x][y] = EL_QUICKSAND_EMPTY;
7835 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7836 Store[x][y + 1] = Store[x][y];
7839 PlayLevelSoundAction(x, y, ACTION_FILLING);
7841 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7843 if (!MovDelay[x][y])
7845 MovDelay[x][y] = TILEY + 1;
7847 ResetGfxAnimation(x, y);
7848 ResetGfxAnimation(x, y + 1);
7853 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7861 Feld[x][y] = EL_QUICKSAND_EMPTY;
7862 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863 Store[x][y + 1] = Store[x][y];
7866 PlayLevelSoundAction(x, y, ACTION_FILLING);
7869 else if (element == EL_QUICKSAND_FAST_FULL)
7871 if (IS_FREE(x, y + 1))
7873 InitMovingField(x, y, MV_DOWN);
7874 started_moving = TRUE;
7876 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879 Store[x][y] = EL_ROCK;
7881 Store[x][y] = EL_ROCK;
7884 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7886 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7888 if (!MovDelay[x][y])
7890 MovDelay[x][y] = TILEY + 1;
7892 ResetGfxAnimation(x, y);
7893 ResetGfxAnimation(x, y + 1);
7898 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7906 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908 Store[x][y + 1] = Store[x][y];
7911 PlayLevelSoundAction(x, y, ACTION_FILLING);
7913 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7915 if (!MovDelay[x][y])
7917 MovDelay[x][y] = TILEY + 1;
7919 ResetGfxAnimation(x, y);
7920 ResetGfxAnimation(x, y + 1);
7925 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7933 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7935 Store[x][y + 1] = Store[x][y];
7938 PlayLevelSoundAction(x, y, ACTION_FILLING);
7941 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7944 InitMovingField(x, y, MV_DOWN);
7945 started_moving = TRUE;
7947 Feld[x][y] = EL_QUICKSAND_FILLING;
7948 Store[x][y] = element;
7950 PlayLevelSoundAction(x, y, ACTION_FILLING);
7952 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7955 InitMovingField(x, y, MV_DOWN);
7956 started_moving = TRUE;
7958 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7959 Store[x][y] = element;
7961 PlayLevelSoundAction(x, y, ACTION_FILLING);
7963 else if (element == EL_MAGIC_WALL_FULL)
7965 if (IS_FREE(x, y + 1))
7967 InitMovingField(x, y, MV_DOWN);
7968 started_moving = TRUE;
7970 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7971 Store[x][y] = EL_CHANGED(Store[x][y]);
7973 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7975 if (!MovDelay[x][y])
7976 MovDelay[x][y] = TILEY / 4 + 1;
7985 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7986 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7987 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7991 else if (element == EL_BD_MAGIC_WALL_FULL)
7993 if (IS_FREE(x, y + 1))
7995 InitMovingField(x, y, MV_DOWN);
7996 started_moving = TRUE;
7998 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8001 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8003 if (!MovDelay[x][y])
8004 MovDelay[x][y] = TILEY / 4 + 1;
8013 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8019 else if (element == EL_DC_MAGIC_WALL_FULL)
8021 if (IS_FREE(x, y + 1))
8023 InitMovingField(x, y, MV_DOWN);
8024 started_moving = TRUE;
8026 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8029 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8031 if (!MovDelay[x][y])
8032 MovDelay[x][y] = TILEY / 4 + 1;
8041 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8047 else if ((CAN_PASS_MAGIC_WALL(element) &&
8048 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050 (CAN_PASS_DC_MAGIC_WALL(element) &&
8051 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8054 InitMovingField(x, y, MV_DOWN);
8055 started_moving = TRUE;
8058 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060 EL_DC_MAGIC_WALL_FILLING);
8061 Store[x][y] = element;
8063 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8065 SplashAcid(x, y + 1);
8067 InitMovingField(x, y, MV_DOWN);
8068 started_moving = TRUE;
8070 Store[x][y] = EL_ACID;
8073 #if USE_FIX_IMPACT_COLLISION
8074 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8075 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8077 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8078 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8080 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8081 CAN_FALL(element) && WasJustFalling[x][y] &&
8082 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8084 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8085 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8086 (Feld[x][y + 1] == EL_BLOCKED)))
8088 /* this is needed for a special case not covered by calling "Impact()"
8089 from "ContinueMoving()": if an element moves to a tile directly below
8090 another element which was just falling on that tile (which was empty
8091 in the previous frame), the falling element above would just stop
8092 instead of smashing the element below (in previous version, the above
8093 element was just checked for "moving" instead of "falling", resulting
8094 in incorrect smashes caused by horizontal movement of the above
8095 element; also, the case of the player being the element to smash was
8096 simply not covered here... :-/ ) */
8098 CheckCollision[x][y] = 0;
8099 CheckImpact[x][y] = 0;
8103 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8105 if (MovDir[x][y] == MV_NONE)
8107 InitMovingField(x, y, MV_DOWN);
8108 started_moving = TRUE;
8111 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8113 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8114 MovDir[x][y] = MV_DOWN;
8116 InitMovingField(x, y, MV_DOWN);
8117 started_moving = TRUE;
8119 else if (element == EL_AMOEBA_DROP)
8121 Feld[x][y] = EL_AMOEBA_GROWING;
8122 Store[x][y] = EL_AMOEBA_WET;
8124 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8125 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8126 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8127 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8129 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8130 (IS_FREE(x - 1, y + 1) ||
8131 Feld[x - 1][y + 1] == EL_ACID));
8132 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8133 (IS_FREE(x + 1, y + 1) ||
8134 Feld[x + 1][y + 1] == EL_ACID));
8135 boolean can_fall_any = (can_fall_left || can_fall_right);
8136 boolean can_fall_both = (can_fall_left && can_fall_right);
8137 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8139 #if USE_NEW_ALL_SLIPPERY
8140 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8142 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143 can_fall_right = FALSE;
8144 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145 can_fall_left = FALSE;
8146 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147 can_fall_right = FALSE;
8148 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149 can_fall_left = FALSE;
8151 can_fall_any = (can_fall_left || can_fall_right);
8152 can_fall_both = FALSE;
8155 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8157 if (slippery_type == SLIPPERY_ONLY_LEFT)
8158 can_fall_right = FALSE;
8159 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8160 can_fall_left = FALSE;
8161 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162 can_fall_right = FALSE;
8163 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164 can_fall_left = FALSE;
8166 can_fall_any = (can_fall_left || can_fall_right);
8167 can_fall_both = (can_fall_left && can_fall_right);
8171 #if USE_NEW_ALL_SLIPPERY
8173 #if USE_NEW_SP_SLIPPERY
8174 /* !!! better use the same properties as for custom elements here !!! */
8175 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8176 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8178 can_fall_right = FALSE; /* slip down on left side */
8179 can_fall_both = FALSE;
8184 #if USE_NEW_ALL_SLIPPERY
8187 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8188 can_fall_right = FALSE; /* slip down on left side */
8190 can_fall_left = !(can_fall_right = RND(2));
8192 can_fall_both = FALSE;
8197 if (game.emulation == EMU_BOULDERDASH ||
8198 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8199 can_fall_right = FALSE; /* slip down on left side */
8201 can_fall_left = !(can_fall_right = RND(2));
8203 can_fall_both = FALSE;
8209 /* if not determined otherwise, prefer left side for slipping down */
8210 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8211 started_moving = TRUE;
8215 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8217 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8220 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8221 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8222 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8223 int belt_dir = game.belt_dir[belt_nr];
8225 if ((belt_dir == MV_LEFT && left_is_free) ||
8226 (belt_dir == MV_RIGHT && right_is_free))
8228 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8230 InitMovingField(x, y, belt_dir);
8231 started_moving = TRUE;
8233 Pushed[x][y] = TRUE;
8234 Pushed[nextx][y] = TRUE;
8236 GfxAction[x][y] = ACTION_DEFAULT;
8240 MovDir[x][y] = 0; /* if element was moving, stop it */
8245 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8247 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8249 if (CAN_MOVE(element) && !started_moving)
8252 int move_pattern = element_info[element].move_pattern;
8257 if (MovDir[x][y] == MV_NONE)
8259 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8260 x, y, element, element_info[element].token_name);
8261 printf("StartMoving(): This should never happen!\n");
8266 Moving2Blocked(x, y, &newx, &newy);
8268 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8271 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8272 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8274 WasJustMoving[x][y] = 0;
8275 CheckCollision[x][y] = 0;
8277 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8279 if (Feld[x][y] != element) /* element has changed */
8283 if (!MovDelay[x][y]) /* start new movement phase */
8285 /* all objects that can change their move direction after each step
8286 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8288 if (element != EL_YAMYAM &&
8289 element != EL_DARK_YAMYAM &&
8290 element != EL_PACMAN &&
8291 !(move_pattern & MV_ANY_DIRECTION) &&
8292 move_pattern != MV_TURNING_LEFT &&
8293 move_pattern != MV_TURNING_RIGHT &&
8294 move_pattern != MV_TURNING_LEFT_RIGHT &&
8295 move_pattern != MV_TURNING_RIGHT_LEFT &&
8296 move_pattern != MV_TURNING_RANDOM)
8300 if (MovDelay[x][y] && (element == EL_BUG ||
8301 element == EL_SPACESHIP ||
8302 element == EL_SP_SNIKSNAK ||
8303 element == EL_SP_ELECTRON ||
8304 element == EL_MOLE))
8305 TEST_DrawLevelField(x, y);
8309 if (MovDelay[x][y]) /* wait some time before next movement */
8313 if (element == EL_ROBOT ||
8314 element == EL_YAMYAM ||
8315 element == EL_DARK_YAMYAM)
8317 DrawLevelElementAnimationIfNeeded(x, y, element);
8318 PlayLevelSoundAction(x, y, ACTION_WAITING);
8320 else if (element == EL_SP_ELECTRON)
8321 DrawLevelElementAnimationIfNeeded(x, y, element);
8322 else if (element == EL_DRAGON)
8325 int dir = MovDir[x][y];
8326 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8327 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8328 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8329 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8330 dir == MV_UP ? IMG_FLAMES_1_UP :
8331 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8332 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8334 GfxAction[x][y] = ACTION_ATTACKING;
8336 if (IS_PLAYER(x, y))
8337 DrawPlayerField(x, y);
8339 TEST_DrawLevelField(x, y);
8341 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8343 for (i = 1; i <= 3; i++)
8345 int xx = x + i * dx;
8346 int yy = y + i * dy;
8347 int sx = SCREENX(xx);
8348 int sy = SCREENY(yy);
8349 int flame_graphic = graphic + (i - 1);
8351 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8356 int flamed = MovingOrBlocked2Element(xx, yy);
8360 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8362 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8363 RemoveMovingField(xx, yy);
8365 RemoveField(xx, yy);
8367 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8370 RemoveMovingField(xx, yy);
8373 ChangeDelay[xx][yy] = 0;
8375 Feld[xx][yy] = EL_FLAMES;
8377 if (IN_SCR_FIELD(sx, sy))
8379 TEST_DrawLevelFieldCrumbled(xx, yy);
8380 DrawGraphic(sx, sy, flame_graphic, frame);
8385 if (Feld[xx][yy] == EL_FLAMES)
8386 Feld[xx][yy] = EL_EMPTY;
8387 TEST_DrawLevelField(xx, yy);
8392 if (MovDelay[x][y]) /* element still has to wait some time */
8394 PlayLevelSoundAction(x, y, ACTION_WAITING);
8400 /* now make next step */
8402 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8404 if (DONT_COLLIDE_WITH(element) &&
8405 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8406 !PLAYER_ENEMY_PROTECTED(newx, newy))
8408 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8413 else if (CAN_MOVE_INTO_ACID(element) &&
8414 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8415 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8416 (MovDir[x][y] == MV_DOWN ||
8417 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8419 SplashAcid(newx, newy);
8420 Store[x][y] = EL_ACID;
8422 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8424 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8425 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8426 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8427 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8430 TEST_DrawLevelField(x, y);
8432 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8433 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8434 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8436 local_player->friends_still_needed--;
8437 if (!local_player->friends_still_needed &&
8438 !local_player->GameOver && AllPlayersGone)
8439 PlayerWins(local_player);
8443 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8445 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8446 TEST_DrawLevelField(newx, newy);
8448 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8450 else if (!IS_FREE(newx, newy))
8452 GfxAction[x][y] = ACTION_WAITING;
8454 if (IS_PLAYER(x, y))
8455 DrawPlayerField(x, y);
8457 TEST_DrawLevelField(x, y);
8462 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8464 if (IS_FOOD_PIG(Feld[newx][newy]))
8466 if (IS_MOVING(newx, newy))
8467 RemoveMovingField(newx, newy);
8470 Feld[newx][newy] = EL_EMPTY;
8471 TEST_DrawLevelField(newx, newy);
8474 PlayLevelSound(x, y, SND_PIG_DIGGING);
8476 else if (!IS_FREE(newx, newy))
8478 if (IS_PLAYER(x, y))
8479 DrawPlayerField(x, y);
8481 TEST_DrawLevelField(x, y);
8486 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8488 if (Store[x][y] != EL_EMPTY)
8490 boolean can_clone = FALSE;
8493 /* check if element to clone is still there */
8494 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8496 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8504 /* cannot clone or target field not free anymore -- do not clone */
8505 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8506 Store[x][y] = EL_EMPTY;
8509 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8511 if (IS_MV_DIAGONAL(MovDir[x][y]))
8513 int diagonal_move_dir = MovDir[x][y];
8514 int stored = Store[x][y];
8515 int change_delay = 8;
8518 /* android is moving diagonally */
8520 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8522 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8523 GfxElement[x][y] = EL_EMC_ANDROID;
8524 GfxAction[x][y] = ACTION_SHRINKING;
8525 GfxDir[x][y] = diagonal_move_dir;
8526 ChangeDelay[x][y] = change_delay;
8528 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8531 DrawLevelGraphicAnimation(x, y, graphic);
8532 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8534 if (Feld[newx][newy] == EL_ACID)
8536 SplashAcid(newx, newy);
8541 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8543 Store[newx][newy] = EL_EMC_ANDROID;
8544 GfxElement[newx][newy] = EL_EMC_ANDROID;
8545 GfxAction[newx][newy] = ACTION_GROWING;
8546 GfxDir[newx][newy] = diagonal_move_dir;
8547 ChangeDelay[newx][newy] = change_delay;
8549 graphic = el_act_dir2img(GfxElement[newx][newy],
8550 GfxAction[newx][newy], GfxDir[newx][newy]);
8552 DrawLevelGraphicAnimation(newx, newy, graphic);
8553 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8559 Feld[newx][newy] = EL_EMPTY;
8560 TEST_DrawLevelField(newx, newy);
8562 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8565 else if (!IS_FREE(newx, newy))
8568 if (IS_PLAYER(x, y))
8569 DrawPlayerField(x, y);
8571 TEST_DrawLevelField(x, y);
8577 else if (IS_CUSTOM_ELEMENT(element) &&
8578 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8581 if (!DigFieldByCE(newx, newy, element))
8584 int new_element = Feld[newx][newy];
8586 if (!IS_FREE(newx, newy))
8588 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8589 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8592 /* no element can dig solid indestructible elements */
8593 if (IS_INDESTRUCTIBLE(new_element) &&
8594 !IS_DIGGABLE(new_element) &&
8595 !IS_COLLECTIBLE(new_element))
8598 if (AmoebaNr[newx][newy] &&
8599 (new_element == EL_AMOEBA_FULL ||
8600 new_element == EL_BD_AMOEBA ||
8601 new_element == EL_AMOEBA_GROWING))
8603 AmoebaCnt[AmoebaNr[newx][newy]]--;
8604 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8607 if (IS_MOVING(newx, newy))
8608 RemoveMovingField(newx, newy);
8611 RemoveField(newx, newy);
8612 TEST_DrawLevelField(newx, newy);
8615 /* if digged element was about to explode, prevent the explosion */
8616 ExplodeField[newx][newy] = EX_TYPE_NONE;
8618 PlayLevelSoundAction(x, y, action);
8621 Store[newx][newy] = EL_EMPTY;
8624 /* this makes it possible to leave the removed element again */
8625 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8626 Store[newx][newy] = new_element;
8628 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8630 int move_leave_element = element_info[element].move_leave_element;
8632 /* this makes it possible to leave the removed element again */
8633 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8634 new_element : move_leave_element);
8640 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8642 RunnerVisit[x][y] = FrameCounter;
8643 PlayerVisit[x][y] /= 8; /* expire player visit path */
8646 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8648 if (!IS_FREE(newx, newy))
8650 if (IS_PLAYER(x, y))
8651 DrawPlayerField(x, y);
8653 TEST_DrawLevelField(x, y);
8659 boolean wanna_flame = !RND(10);
8660 int dx = newx - x, dy = newy - y;
8661 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8662 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8663 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8664 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8665 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8666 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8669 IS_CLASSIC_ENEMY(element1) ||
8670 IS_CLASSIC_ENEMY(element2)) &&
8671 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8672 element1 != EL_FLAMES && element2 != EL_FLAMES)
8674 ResetGfxAnimation(x, y);
8675 GfxAction[x][y] = ACTION_ATTACKING;
8677 if (IS_PLAYER(x, y))
8678 DrawPlayerField(x, y);
8680 TEST_DrawLevelField(x, y);
8682 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8684 MovDelay[x][y] = 50;
8688 RemoveField(newx, newy);
8690 Feld[newx][newy] = EL_FLAMES;
8691 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8694 RemoveField(newx1, newy1);
8696 Feld[newx1][newy1] = EL_FLAMES;
8698 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8701 RemoveField(newx2, newy2);
8703 Feld[newx2][newy2] = EL_FLAMES;
8710 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8711 Feld[newx][newy] == EL_DIAMOND)
8713 if (IS_MOVING(newx, newy))
8714 RemoveMovingField(newx, newy);
8717 Feld[newx][newy] = EL_EMPTY;
8718 TEST_DrawLevelField(newx, newy);
8721 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8723 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8724 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8726 if (AmoebaNr[newx][newy])
8728 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8729 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8730 Feld[newx][newy] == EL_BD_AMOEBA)
8731 AmoebaCnt[AmoebaNr[newx][newy]]--;
8736 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8738 RemoveMovingField(newx, newy);
8741 if (IS_MOVING(newx, newy))
8743 RemoveMovingField(newx, newy);
8748 Feld[newx][newy] = EL_EMPTY;
8749 TEST_DrawLevelField(newx, newy);
8752 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8754 else if ((element == EL_PACMAN || element == EL_MOLE)
8755 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8757 if (AmoebaNr[newx][newy])
8759 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8760 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8761 Feld[newx][newy] == EL_BD_AMOEBA)
8762 AmoebaCnt[AmoebaNr[newx][newy]]--;
8765 if (element == EL_MOLE)
8767 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8768 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8770 ResetGfxAnimation(x, y);
8771 GfxAction[x][y] = ACTION_DIGGING;
8772 TEST_DrawLevelField(x, y);
8774 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8776 return; /* wait for shrinking amoeba */
8778 else /* element == EL_PACMAN */
8780 Feld[newx][newy] = EL_EMPTY;
8781 TEST_DrawLevelField(newx, newy);
8782 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8785 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8786 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8787 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8789 /* wait for shrinking amoeba to completely disappear */
8792 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8794 /* object was running against a wall */
8799 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8800 if (move_pattern & MV_ANY_DIRECTION &&
8801 move_pattern == MovDir[x][y])
8803 int blocking_element =
8804 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8806 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8809 element = Feld[x][y]; /* element might have changed */
8813 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8814 DrawLevelElementAnimation(x, y, element);
8816 if (DONT_TOUCH(element))
8817 TestIfBadThingTouchesPlayer(x, y);
8822 InitMovingField(x, y, MovDir[x][y]);
8824 PlayLevelSoundAction(x, y, ACTION_MOVING);
8828 ContinueMoving(x, y);
8831 void ContinueMoving(int x, int y)
8833 int element = Feld[x][y];
8834 struct ElementInfo *ei = &element_info[element];
8835 int direction = MovDir[x][y];
8836 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8837 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8838 int newx = x + dx, newy = y + dy;
8839 int stored = Store[x][y];
8840 int stored_new = Store[newx][newy];
8841 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8842 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8843 boolean last_line = (newy == lev_fieldy - 1);
8845 MovPos[x][y] += getElementMoveStepsize(x, y);
8847 if (pushed_by_player) /* special case: moving object pushed by player */
8848 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8850 if (ABS(MovPos[x][y]) < TILEX)
8853 int ee = Feld[x][y];
8854 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8855 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8857 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8858 x, y, ABS(MovPos[x][y]),
8860 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8863 TEST_DrawLevelField(x, y);
8865 return; /* element is still moving */
8868 /* element reached destination field */
8870 Feld[x][y] = EL_EMPTY;
8871 Feld[newx][newy] = element;
8872 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8874 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8876 element = Feld[newx][newy] = EL_ACID;
8878 else if (element == EL_MOLE)
8880 Feld[x][y] = EL_SAND;
8882 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8884 else if (element == EL_QUICKSAND_FILLING)
8886 element = Feld[newx][newy] = get_next_element(element);
8887 Store[newx][newy] = Store[x][y];
8889 else if (element == EL_QUICKSAND_EMPTYING)
8891 Feld[x][y] = get_next_element(element);
8892 element = Feld[newx][newy] = Store[x][y];
8894 else if (element == EL_QUICKSAND_FAST_FILLING)
8896 element = Feld[newx][newy] = get_next_element(element);
8897 Store[newx][newy] = Store[x][y];
8899 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8901 Feld[x][y] = get_next_element(element);
8902 element = Feld[newx][newy] = Store[x][y];
8904 else if (element == EL_MAGIC_WALL_FILLING)
8906 element = Feld[newx][newy] = get_next_element(element);
8907 if (!game.magic_wall_active)
8908 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8909 Store[newx][newy] = Store[x][y];
8911 else if (element == EL_MAGIC_WALL_EMPTYING)
8913 Feld[x][y] = get_next_element(element);
8914 if (!game.magic_wall_active)
8915 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8916 element = Feld[newx][newy] = Store[x][y];
8918 #if USE_NEW_CUSTOM_VALUE
8919 InitField(newx, newy, FALSE);
8922 else if (element == EL_BD_MAGIC_WALL_FILLING)
8924 element = Feld[newx][newy] = get_next_element(element);
8925 if (!game.magic_wall_active)
8926 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8927 Store[newx][newy] = Store[x][y];
8929 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8931 Feld[x][y] = get_next_element(element);
8932 if (!game.magic_wall_active)
8933 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8934 element = Feld[newx][newy] = Store[x][y];
8936 #if USE_NEW_CUSTOM_VALUE
8937 InitField(newx, newy, FALSE);
8940 else if (element == EL_DC_MAGIC_WALL_FILLING)
8942 element = Feld[newx][newy] = get_next_element(element);
8943 if (!game.magic_wall_active)
8944 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8945 Store[newx][newy] = Store[x][y];
8947 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8949 Feld[x][y] = get_next_element(element);
8950 if (!game.magic_wall_active)
8951 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8952 element = Feld[newx][newy] = Store[x][y];
8954 #if USE_NEW_CUSTOM_VALUE
8955 InitField(newx, newy, FALSE);
8958 else if (element == EL_AMOEBA_DROPPING)
8960 Feld[x][y] = get_next_element(element);
8961 element = Feld[newx][newy] = Store[x][y];
8963 else if (element == EL_SOKOBAN_OBJECT)
8966 Feld[x][y] = Back[x][y];
8968 if (Back[newx][newy])
8969 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8971 Back[x][y] = Back[newx][newy] = 0;
8974 Store[x][y] = EL_EMPTY;
8979 MovDelay[newx][newy] = 0;
8981 if (CAN_CHANGE_OR_HAS_ACTION(element))
8983 /* copy element change control values to new field */
8984 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8985 ChangePage[newx][newy] = ChangePage[x][y];
8986 ChangeCount[newx][newy] = ChangeCount[x][y];
8987 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8990 #if USE_NEW_CUSTOM_VALUE
8991 CustomValue[newx][newy] = CustomValue[x][y];
8994 ChangeDelay[x][y] = 0;
8995 ChangePage[x][y] = -1;
8996 ChangeCount[x][y] = 0;
8997 ChangeEvent[x][y] = -1;
8999 #if USE_NEW_CUSTOM_VALUE
9000 CustomValue[x][y] = 0;
9003 /* copy animation control values to new field */
9004 GfxFrame[newx][newy] = GfxFrame[x][y];
9005 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9006 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9007 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9009 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9011 /* some elements can leave other elements behind after moving */
9013 if (ei->move_leave_element != EL_EMPTY &&
9014 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9015 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9017 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9018 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9019 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9022 int move_leave_element = ei->move_leave_element;
9026 /* this makes it possible to leave the removed element again */
9027 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9028 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9030 /* this makes it possible to leave the removed element again */
9031 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9032 move_leave_element = stored;
9035 /* this makes it possible to leave the removed element again */
9036 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9037 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9038 move_leave_element = stored;
9041 Feld[x][y] = move_leave_element;
9043 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9044 MovDir[x][y] = direction;
9046 InitField(x, y, FALSE);
9048 if (GFX_CRUMBLED(Feld[x][y]))
9049 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9051 if (ELEM_IS_PLAYER(move_leave_element))
9052 RelocatePlayer(x, y, move_leave_element);
9055 /* do this after checking for left-behind element */
9056 ResetGfxAnimation(x, y); /* reset animation values for old field */
9058 if (!CAN_MOVE(element) ||
9059 (CAN_FALL(element) && direction == MV_DOWN &&
9060 (element == EL_SPRING ||
9061 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9062 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9063 GfxDir[x][y] = MovDir[newx][newy] = 0;
9065 TEST_DrawLevelField(x, y);
9066 TEST_DrawLevelField(newx, newy);
9068 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9070 /* prevent pushed element from moving on in pushed direction */
9071 if (pushed_by_player && CAN_MOVE(element) &&
9072 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9073 !(element_info[element].move_pattern & direction))
9074 TurnRound(newx, newy);
9076 /* prevent elements on conveyor belt from moving on in last direction */
9077 if (pushed_by_conveyor && CAN_FALL(element) &&
9078 direction & MV_HORIZONTAL)
9079 MovDir[newx][newy] = 0;
9081 if (!pushed_by_player)
9083 int nextx = newx + dx, nexty = newy + dy;
9084 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9086 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9088 if (CAN_FALL(element) && direction == MV_DOWN)
9089 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9091 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9092 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9094 #if USE_FIX_IMPACT_COLLISION
9095 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9096 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9100 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9102 TestIfBadThingTouchesPlayer(newx, newy);
9103 TestIfBadThingTouchesFriend(newx, newy);
9105 if (!IS_CUSTOM_ELEMENT(element))
9106 TestIfBadThingTouchesOtherBadThing(newx, newy);
9108 else if (element == EL_PENGUIN)
9109 TestIfFriendTouchesBadThing(newx, newy);
9111 if (DONT_GET_HIT_BY(element))
9113 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9116 /* give the player one last chance (one more frame) to move away */
9117 if (CAN_FALL(element) && direction == MV_DOWN &&
9118 (last_line || (!IS_FREE(x, newy + 1) &&
9119 (!IS_PLAYER(x, newy + 1) ||
9120 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9123 if (pushed_by_player && !game.use_change_when_pushing_bug)
9125 int push_side = MV_DIR_OPPOSITE(direction);
9126 struct PlayerInfo *player = PLAYERINFO(x, y);
9128 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9129 player->index_bit, push_side);
9130 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9131 player->index_bit, push_side);
9134 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9135 MovDelay[newx][newy] = 1;
9137 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9139 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9142 if (ChangePage[newx][newy] != -1) /* delayed change */
9144 int page = ChangePage[newx][newy];
9145 struct ElementChangeInfo *change = &ei->change_page[page];
9147 ChangePage[newx][newy] = -1;
9149 if (change->can_change)
9151 if (ChangeElement(newx, newy, element, page))
9153 if (change->post_change_function)
9154 change->post_change_function(newx, newy);
9158 if (change->has_action)
9159 ExecuteCustomElementAction(newx, newy, element, page);
9163 TestIfElementHitsCustomElement(newx, newy, direction);
9164 TestIfPlayerTouchesCustomElement(newx, newy);
9165 TestIfElementTouchesCustomElement(newx, newy);
9167 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9168 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9169 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9170 MV_DIR_OPPOSITE(direction));
9173 int AmoebeNachbarNr(int ax, int ay)
9176 int element = Feld[ax][ay];
9178 static int xy[4][2] =
9186 for (i = 0; i < NUM_DIRECTIONS; i++)
9188 int x = ax + xy[i][0];
9189 int y = ay + xy[i][1];
9191 if (!IN_LEV_FIELD(x, y))
9194 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9195 group_nr = AmoebaNr[x][y];
9201 void AmoebenVereinigen(int ax, int ay)
9203 int i, x, y, xx, yy;
9204 int new_group_nr = AmoebaNr[ax][ay];
9205 static int xy[4][2] =
9213 if (new_group_nr == 0)
9216 for (i = 0; i < NUM_DIRECTIONS; i++)
9221 if (!IN_LEV_FIELD(x, y))
9224 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9225 Feld[x][y] == EL_BD_AMOEBA ||
9226 Feld[x][y] == EL_AMOEBA_DEAD) &&
9227 AmoebaNr[x][y] != new_group_nr)
9229 int old_group_nr = AmoebaNr[x][y];
9231 if (old_group_nr == 0)
9234 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9235 AmoebaCnt[old_group_nr] = 0;
9236 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9237 AmoebaCnt2[old_group_nr] = 0;
9239 SCAN_PLAYFIELD(xx, yy)
9241 if (AmoebaNr[xx][yy] == old_group_nr)
9242 AmoebaNr[xx][yy] = new_group_nr;
9248 void AmoebeUmwandeln(int ax, int ay)
9252 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9254 int group_nr = AmoebaNr[ax][ay];
9259 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9260 printf("AmoebeUmwandeln(): This should never happen!\n");
9265 SCAN_PLAYFIELD(x, y)
9267 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9270 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9274 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9275 SND_AMOEBA_TURNING_TO_GEM :
9276 SND_AMOEBA_TURNING_TO_ROCK));
9281 static int xy[4][2] =
9289 for (i = 0; i < NUM_DIRECTIONS; i++)
9294 if (!IN_LEV_FIELD(x, y))
9297 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9299 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9300 SND_AMOEBA_TURNING_TO_GEM :
9301 SND_AMOEBA_TURNING_TO_ROCK));
9308 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9311 int group_nr = AmoebaNr[ax][ay];
9312 boolean done = FALSE;
9317 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9318 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9323 SCAN_PLAYFIELD(x, y)
9325 if (AmoebaNr[x][y] == group_nr &&
9326 (Feld[x][y] == EL_AMOEBA_DEAD ||
9327 Feld[x][y] == EL_BD_AMOEBA ||
9328 Feld[x][y] == EL_AMOEBA_GROWING))
9331 Feld[x][y] = new_element;
9332 InitField(x, y, FALSE);
9333 TEST_DrawLevelField(x, y);
9339 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9340 SND_BD_AMOEBA_TURNING_TO_ROCK :
9341 SND_BD_AMOEBA_TURNING_TO_GEM));
9344 void AmoebeWaechst(int x, int y)
9346 static unsigned int sound_delay = 0;
9347 static unsigned int sound_delay_value = 0;
9349 if (!MovDelay[x][y]) /* start new growing cycle */
9353 if (DelayReached(&sound_delay, sound_delay_value))
9355 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9356 sound_delay_value = 30;
9360 if (MovDelay[x][y]) /* wait some time before growing bigger */
9363 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9365 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9366 6 - MovDelay[x][y]);
9368 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9371 if (!MovDelay[x][y])
9373 Feld[x][y] = Store[x][y];
9375 TEST_DrawLevelField(x, y);
9380 void AmoebaDisappearing(int x, int y)
9382 static unsigned int sound_delay = 0;
9383 static unsigned int sound_delay_value = 0;
9385 if (!MovDelay[x][y]) /* start new shrinking cycle */
9389 if (DelayReached(&sound_delay, sound_delay_value))
9390 sound_delay_value = 30;
9393 if (MovDelay[x][y]) /* wait some time before shrinking */
9396 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9398 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9399 6 - MovDelay[x][y]);
9401 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9404 if (!MovDelay[x][y])
9406 Feld[x][y] = EL_EMPTY;
9407 TEST_DrawLevelField(x, y);
9409 /* don't let mole enter this field in this cycle;
9410 (give priority to objects falling to this field from above) */
9416 void AmoebeAbleger(int ax, int ay)
9419 int element = Feld[ax][ay];
9420 int graphic = el2img(element);
9421 int newax = ax, neway = ay;
9422 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9423 static int xy[4][2] =
9431 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9433 Feld[ax][ay] = EL_AMOEBA_DEAD;
9434 TEST_DrawLevelField(ax, ay);
9438 if (IS_ANIMATED(graphic))
9439 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9441 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9442 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9444 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9447 if (MovDelay[ax][ay])
9451 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9454 int x = ax + xy[start][0];
9455 int y = ay + xy[start][1];
9457 if (!IN_LEV_FIELD(x, y))
9460 if (IS_FREE(x, y) ||
9461 CAN_GROW_INTO(Feld[x][y]) ||
9462 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9463 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9469 if (newax == ax && neway == ay)
9472 else /* normal or "filled" (BD style) amoeba */
9475 boolean waiting_for_player = FALSE;
9477 for (i = 0; i < NUM_DIRECTIONS; i++)
9479 int j = (start + i) % 4;
9480 int x = ax + xy[j][0];
9481 int y = ay + xy[j][1];
9483 if (!IN_LEV_FIELD(x, y))
9486 if (IS_FREE(x, y) ||
9487 CAN_GROW_INTO(Feld[x][y]) ||
9488 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9489 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9495 else if (IS_PLAYER(x, y))
9496 waiting_for_player = TRUE;
9499 if (newax == ax && neway == ay) /* amoeba cannot grow */
9501 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9503 Feld[ax][ay] = EL_AMOEBA_DEAD;
9504 TEST_DrawLevelField(ax, ay);
9505 AmoebaCnt[AmoebaNr[ax][ay]]--;
9507 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9509 if (element == EL_AMOEBA_FULL)
9510 AmoebeUmwandeln(ax, ay);
9511 else if (element == EL_BD_AMOEBA)
9512 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9517 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9519 /* amoeba gets larger by growing in some direction */
9521 int new_group_nr = AmoebaNr[ax][ay];
9524 if (new_group_nr == 0)
9526 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9527 printf("AmoebeAbleger(): This should never happen!\n");
9532 AmoebaNr[newax][neway] = new_group_nr;
9533 AmoebaCnt[new_group_nr]++;
9534 AmoebaCnt2[new_group_nr]++;
9536 /* if amoeba touches other amoeba(s) after growing, unify them */
9537 AmoebenVereinigen(newax, neway);
9539 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9541 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9547 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9548 (neway == lev_fieldy - 1 && newax != ax))
9550 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9551 Store[newax][neway] = element;
9553 else if (neway == ay || element == EL_EMC_DRIPPER)
9555 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9557 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9561 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9562 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9563 Store[ax][ay] = EL_AMOEBA_DROP;
9564 ContinueMoving(ax, ay);
9568 TEST_DrawLevelField(newax, neway);
9571 void Life(int ax, int ay)
9575 int element = Feld[ax][ay];
9576 int graphic = el2img(element);
9577 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9579 boolean changed = FALSE;
9581 if (IS_ANIMATED(graphic))
9582 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9587 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9588 MovDelay[ax][ay] = life_time;
9590 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9593 if (MovDelay[ax][ay])
9597 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9599 int xx = ax+x1, yy = ay+y1;
9602 if (!IN_LEV_FIELD(xx, yy))
9605 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9607 int x = xx+x2, y = yy+y2;
9609 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9612 if (((Feld[x][y] == element ||
9613 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9615 (IS_FREE(x, y) && Stop[x][y]))
9619 if (xx == ax && yy == ay) /* field in the middle */
9621 if (nachbarn < life_parameter[0] ||
9622 nachbarn > life_parameter[1])
9624 Feld[xx][yy] = EL_EMPTY;
9626 TEST_DrawLevelField(xx, yy);
9627 Stop[xx][yy] = TRUE;
9631 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9632 { /* free border field */
9633 if (nachbarn >= life_parameter[2] &&
9634 nachbarn <= life_parameter[3])
9636 Feld[xx][yy] = element;
9637 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9639 TEST_DrawLevelField(xx, yy);
9640 Stop[xx][yy] = TRUE;
9647 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9648 SND_GAME_OF_LIFE_GROWING);
9651 static void InitRobotWheel(int x, int y)
9653 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9656 static void RunRobotWheel(int x, int y)
9658 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9661 static void StopRobotWheel(int x, int y)
9663 if (ZX == x && ZY == y)
9667 game.robot_wheel_active = FALSE;
9671 static void InitTimegateWheel(int x, int y)
9673 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9676 static void RunTimegateWheel(int x, int y)
9678 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9681 static void InitMagicBallDelay(int x, int y)
9684 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9686 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9690 static void ActivateMagicBall(int bx, int by)
9694 if (level.ball_random)
9696 int pos_border = RND(8); /* select one of the eight border elements */
9697 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9698 int xx = pos_content % 3;
9699 int yy = pos_content / 3;
9704 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9705 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9709 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9711 int xx = x - bx + 1;
9712 int yy = y - by + 1;
9714 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9715 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9719 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9722 void CheckExit(int x, int y)
9724 if (local_player->gems_still_needed > 0 ||
9725 local_player->sokobanfields_still_needed > 0 ||
9726 local_player->lights_still_needed > 0)
9728 int element = Feld[x][y];
9729 int graphic = el2img(element);
9731 if (IS_ANIMATED(graphic))
9732 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9737 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9740 Feld[x][y] = EL_EXIT_OPENING;
9742 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9745 void CheckExitEM(int x, int y)
9747 if (local_player->gems_still_needed > 0 ||
9748 local_player->sokobanfields_still_needed > 0 ||
9749 local_player->lights_still_needed > 0)
9751 int element = Feld[x][y];
9752 int graphic = el2img(element);
9754 if (IS_ANIMATED(graphic))
9755 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9760 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9763 Feld[x][y] = EL_EM_EXIT_OPENING;
9765 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9768 void CheckExitSteel(int x, int y)
9770 if (local_player->gems_still_needed > 0 ||
9771 local_player->sokobanfields_still_needed > 0 ||
9772 local_player->lights_still_needed > 0)
9774 int element = Feld[x][y];
9775 int graphic = el2img(element);
9777 if (IS_ANIMATED(graphic))
9778 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9783 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9786 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9788 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9791 void CheckExitSteelEM(int x, int y)
9793 if (local_player->gems_still_needed > 0 ||
9794 local_player->sokobanfields_still_needed > 0 ||
9795 local_player->lights_still_needed > 0)
9797 int element = Feld[x][y];
9798 int graphic = el2img(element);
9800 if (IS_ANIMATED(graphic))
9801 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9806 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9809 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9811 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9814 void CheckExitSP(int x, int y)
9816 if (local_player->gems_still_needed > 0)
9818 int element = Feld[x][y];
9819 int graphic = el2img(element);
9821 if (IS_ANIMATED(graphic))
9822 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9827 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9830 Feld[x][y] = EL_SP_EXIT_OPENING;
9832 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9835 static void CloseAllOpenTimegates()
9839 SCAN_PLAYFIELD(x, y)
9841 int element = Feld[x][y];
9843 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9845 Feld[x][y] = EL_TIMEGATE_CLOSING;
9847 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9852 void DrawTwinkleOnField(int x, int y)
9854 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9857 if (Feld[x][y] == EL_BD_DIAMOND)
9860 if (MovDelay[x][y] == 0) /* next animation frame */
9861 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9863 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9867 DrawLevelElementAnimation(x, y, Feld[x][y]);
9869 if (MovDelay[x][y] != 0)
9871 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9872 10 - MovDelay[x][y]);
9874 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9879 void MauerWaechst(int x, int y)
9883 if (!MovDelay[x][y]) /* next animation frame */
9884 MovDelay[x][y] = 3 * delay;
9886 if (MovDelay[x][y]) /* wait some time before next frame */
9890 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9892 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9893 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9895 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9898 if (!MovDelay[x][y])
9900 if (MovDir[x][y] == MV_LEFT)
9902 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9903 TEST_DrawLevelField(x - 1, y);
9905 else if (MovDir[x][y] == MV_RIGHT)
9907 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9908 TEST_DrawLevelField(x + 1, y);
9910 else if (MovDir[x][y] == MV_UP)
9912 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9913 TEST_DrawLevelField(x, y - 1);
9917 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9918 TEST_DrawLevelField(x, y + 1);
9921 Feld[x][y] = Store[x][y];
9923 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9924 TEST_DrawLevelField(x, y);
9929 void MauerAbleger(int ax, int ay)
9931 int element = Feld[ax][ay];
9932 int graphic = el2img(element);
9933 boolean oben_frei = FALSE, unten_frei = FALSE;
9934 boolean links_frei = FALSE, rechts_frei = FALSE;
9935 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9936 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9937 boolean new_wall = FALSE;
9939 if (IS_ANIMATED(graphic))
9940 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9942 if (!MovDelay[ax][ay]) /* start building new wall */
9943 MovDelay[ax][ay] = 6;
9945 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9948 if (MovDelay[ax][ay])
9952 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9954 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9956 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9958 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9961 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9962 element == EL_EXPANDABLE_WALL_ANY)
9966 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9967 Store[ax][ay-1] = element;
9968 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9969 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9970 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9971 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9976 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9977 Store[ax][ay+1] = element;
9978 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9979 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9980 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9981 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9986 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9987 element == EL_EXPANDABLE_WALL_ANY ||
9988 element == EL_EXPANDABLE_WALL ||
9989 element == EL_BD_EXPANDABLE_WALL)
9993 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9994 Store[ax-1][ay] = element;
9995 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9996 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9997 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9998 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10004 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10005 Store[ax+1][ay] = element;
10006 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10007 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10008 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10009 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10014 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10015 TEST_DrawLevelField(ax, ay);
10017 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10018 oben_massiv = TRUE;
10019 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10020 unten_massiv = TRUE;
10021 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10022 links_massiv = TRUE;
10023 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10024 rechts_massiv = TRUE;
10026 if (((oben_massiv && unten_massiv) ||
10027 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10028 element == EL_EXPANDABLE_WALL) &&
10029 ((links_massiv && rechts_massiv) ||
10030 element == EL_EXPANDABLE_WALL_VERTICAL))
10031 Feld[ax][ay] = EL_WALL;
10034 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10037 void MauerAblegerStahl(int ax, int ay)
10039 int element = Feld[ax][ay];
10040 int graphic = el2img(element);
10041 boolean oben_frei = FALSE, unten_frei = FALSE;
10042 boolean links_frei = FALSE, rechts_frei = FALSE;
10043 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10044 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10045 boolean new_wall = FALSE;
10047 if (IS_ANIMATED(graphic))
10048 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10050 if (!MovDelay[ax][ay]) /* start building new wall */
10051 MovDelay[ax][ay] = 6;
10053 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10055 MovDelay[ax][ay]--;
10056 if (MovDelay[ax][ay])
10060 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10062 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10064 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10066 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10067 rechts_frei = TRUE;
10069 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10070 element == EL_EXPANDABLE_STEELWALL_ANY)
10074 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10075 Store[ax][ay-1] = element;
10076 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10077 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10078 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10079 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10084 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10085 Store[ax][ay+1] = element;
10086 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10087 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10088 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10089 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10094 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10095 element == EL_EXPANDABLE_STEELWALL_ANY)
10099 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10100 Store[ax-1][ay] = element;
10101 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10102 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10103 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10104 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10110 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10111 Store[ax+1][ay] = element;
10112 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10113 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10114 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10115 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10120 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10121 oben_massiv = TRUE;
10122 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10123 unten_massiv = TRUE;
10124 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10125 links_massiv = TRUE;
10126 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10127 rechts_massiv = TRUE;
10129 if (((oben_massiv && unten_massiv) ||
10130 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10131 ((links_massiv && rechts_massiv) ||
10132 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10133 Feld[ax][ay] = EL_STEELWALL;
10136 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10139 void CheckForDragon(int x, int y)
10142 boolean dragon_found = FALSE;
10143 static int xy[4][2] =
10151 for (i = 0; i < NUM_DIRECTIONS; i++)
10153 for (j = 0; j < 4; j++)
10155 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10157 if (IN_LEV_FIELD(xx, yy) &&
10158 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10160 if (Feld[xx][yy] == EL_DRAGON)
10161 dragon_found = TRUE;
10170 for (i = 0; i < NUM_DIRECTIONS; i++)
10172 for (j = 0; j < 3; j++)
10174 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10176 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10178 Feld[xx][yy] = EL_EMPTY;
10179 TEST_DrawLevelField(xx, yy);
10188 static void InitBuggyBase(int x, int y)
10190 int element = Feld[x][y];
10191 int activating_delay = FRAMES_PER_SECOND / 4;
10193 ChangeDelay[x][y] =
10194 (element == EL_SP_BUGGY_BASE ?
10195 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10196 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10198 element == EL_SP_BUGGY_BASE_ACTIVE ?
10199 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10202 static void WarnBuggyBase(int x, int y)
10205 static int xy[4][2] =
10213 for (i = 0; i < NUM_DIRECTIONS; i++)
10215 int xx = x + xy[i][0];
10216 int yy = y + xy[i][1];
10218 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10220 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10227 static void InitTrap(int x, int y)
10229 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10232 static void ActivateTrap(int x, int y)
10234 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10237 static void ChangeActiveTrap(int x, int y)
10239 int graphic = IMG_TRAP_ACTIVE;
10241 /* if new animation frame was drawn, correct crumbled sand border */
10242 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10243 TEST_DrawLevelFieldCrumbled(x, y);
10246 static int getSpecialActionElement(int element, int number, int base_element)
10248 return (element != EL_EMPTY ? element :
10249 number != -1 ? base_element + number - 1 :
10253 static int getModifiedActionNumber(int value_old, int operator, int operand,
10254 int value_min, int value_max)
10256 int value_new = (operator == CA_MODE_SET ? operand :
10257 operator == CA_MODE_ADD ? value_old + operand :
10258 operator == CA_MODE_SUBTRACT ? value_old - operand :
10259 operator == CA_MODE_MULTIPLY ? value_old * operand :
10260 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10261 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10264 return (value_new < value_min ? value_min :
10265 value_new > value_max ? value_max :
10269 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10271 struct ElementInfo *ei = &element_info[element];
10272 struct ElementChangeInfo *change = &ei->change_page[page];
10273 int target_element = change->target_element;
10274 int action_type = change->action_type;
10275 int action_mode = change->action_mode;
10276 int action_arg = change->action_arg;
10277 int action_element = change->action_element;
10280 if (!change->has_action)
10283 /* ---------- determine action paramater values -------------------------- */
10285 int level_time_value =
10286 (level.time > 0 ? TimeLeft :
10289 int action_arg_element_raw =
10290 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10291 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10292 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10293 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10294 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10295 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10296 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10298 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10301 if (action_arg_element_raw == EL_GROUP_START)
10302 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10305 int action_arg_direction =
10306 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10307 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10308 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10309 change->actual_trigger_side :
10310 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10311 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10314 int action_arg_number_min =
10315 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10318 int action_arg_number_max =
10319 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10320 action_type == CA_SET_LEVEL_GEMS ? 999 :
10321 action_type == CA_SET_LEVEL_TIME ? 9999 :
10322 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10323 action_type == CA_SET_CE_VALUE ? 9999 :
10324 action_type == CA_SET_CE_SCORE ? 9999 :
10327 int action_arg_number_reset =
10328 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10329 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10330 action_type == CA_SET_LEVEL_TIME ? level.time :
10331 action_type == CA_SET_LEVEL_SCORE ? 0 :
10332 #if USE_NEW_CUSTOM_VALUE
10333 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10335 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10337 action_type == CA_SET_CE_SCORE ? 0 :
10340 int action_arg_number =
10341 (action_arg <= CA_ARG_MAX ? action_arg :
10342 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10343 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10344 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10345 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10346 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10347 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10348 #if USE_NEW_CUSTOM_VALUE
10349 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10351 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10353 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10354 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10355 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10356 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10357 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10358 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10359 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10360 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10361 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10362 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10363 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10364 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10365 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10366 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10369 int action_arg_number_old =
10370 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10371 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10372 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10373 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10374 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10377 int action_arg_number_new =
10378 getModifiedActionNumber(action_arg_number_old,
10379 action_mode, action_arg_number,
10380 action_arg_number_min, action_arg_number_max);
10383 int trigger_player_bits =
10384 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10385 change->actual_trigger_player_bits : change->trigger_player);
10387 int trigger_player_bits =
10388 (change->actual_trigger_player >= EL_PLAYER_1 &&
10389 change->actual_trigger_player <= EL_PLAYER_4 ?
10390 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10394 int action_arg_player_bits =
10395 (action_arg >= CA_ARG_PLAYER_1 &&
10396 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10397 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10398 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10401 /* ---------- execute action -------------------------------------------- */
10403 switch (action_type)
10410 /* ---------- level actions ------------------------------------------- */
10412 case CA_RESTART_LEVEL:
10414 game.restart_level = TRUE;
10419 case CA_SHOW_ENVELOPE:
10421 int element = getSpecialActionElement(action_arg_element,
10422 action_arg_number, EL_ENVELOPE_1);
10424 if (IS_ENVELOPE(element))
10425 local_player->show_envelope = element;
10430 case CA_SET_LEVEL_TIME:
10432 if (level.time > 0) /* only modify limited time value */
10434 TimeLeft = action_arg_number_new;
10437 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10439 DisplayGameControlValues();
10441 DrawGameValue_Time(TimeLeft);
10444 if (!TimeLeft && setup.time_limit)
10445 for (i = 0; i < MAX_PLAYERS; i++)
10446 KillPlayer(&stored_player[i]);
10452 case CA_SET_LEVEL_SCORE:
10454 local_player->score = action_arg_number_new;
10457 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10459 DisplayGameControlValues();
10461 DrawGameValue_Score(local_player->score);
10467 case CA_SET_LEVEL_GEMS:
10469 local_player->gems_still_needed = action_arg_number_new;
10472 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10474 DisplayGameControlValues();
10476 DrawGameValue_Emeralds(local_player->gems_still_needed);
10482 #if !USE_PLAYER_GRAVITY
10483 case CA_SET_LEVEL_GRAVITY:
10485 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10486 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10487 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10493 case CA_SET_LEVEL_WIND:
10495 game.wind_direction = action_arg_direction;
10500 case CA_SET_LEVEL_RANDOM_SEED:
10503 /* ensure that setting a new random seed while playing is predictable */
10504 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10506 InitRND(action_arg_number_new);
10510 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10518 for (i = 0; i < 9; i++)
10519 printf("%d, ", RND(2));
10527 /* ---------- player actions ------------------------------------------ */
10529 case CA_MOVE_PLAYER:
10531 /* automatically move to the next field in specified direction */
10532 for (i = 0; i < MAX_PLAYERS; i++)
10533 if (trigger_player_bits & (1 << i))
10534 stored_player[i].programmed_action = action_arg_direction;
10539 case CA_EXIT_PLAYER:
10541 for (i = 0; i < MAX_PLAYERS; i++)
10542 if (action_arg_player_bits & (1 << i))
10543 PlayerWins(&stored_player[i]);
10548 case CA_KILL_PLAYER:
10550 for (i = 0; i < MAX_PLAYERS; i++)
10551 if (action_arg_player_bits & (1 << i))
10552 KillPlayer(&stored_player[i]);
10557 case CA_SET_PLAYER_KEYS:
10559 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10560 int element = getSpecialActionElement(action_arg_element,
10561 action_arg_number, EL_KEY_1);
10563 if (IS_KEY(element))
10565 for (i = 0; i < MAX_PLAYERS; i++)
10567 if (trigger_player_bits & (1 << i))
10569 stored_player[i].key[KEY_NR(element)] = key_state;
10571 DrawGameDoorValues();
10579 case CA_SET_PLAYER_SPEED:
10582 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10585 for (i = 0; i < MAX_PLAYERS; i++)
10587 if (trigger_player_bits & (1 << i))
10589 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10591 if (action_arg == CA_ARG_SPEED_FASTER &&
10592 stored_player[i].cannot_move)
10594 action_arg_number = STEPSIZE_VERY_SLOW;
10596 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10597 action_arg == CA_ARG_SPEED_FASTER)
10599 action_arg_number = 2;
10600 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10603 else if (action_arg == CA_ARG_NUMBER_RESET)
10605 action_arg_number = level.initial_player_stepsize[i];
10609 getModifiedActionNumber(move_stepsize,
10612 action_arg_number_min,
10613 action_arg_number_max);
10615 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10622 case CA_SET_PLAYER_SHIELD:
10624 for (i = 0; i < MAX_PLAYERS; i++)
10626 if (trigger_player_bits & (1 << i))
10628 if (action_arg == CA_ARG_SHIELD_OFF)
10630 stored_player[i].shield_normal_time_left = 0;
10631 stored_player[i].shield_deadly_time_left = 0;
10633 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10635 stored_player[i].shield_normal_time_left = 999999;
10637 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10639 stored_player[i].shield_normal_time_left = 999999;
10640 stored_player[i].shield_deadly_time_left = 999999;
10648 #if USE_PLAYER_GRAVITY
10649 case CA_SET_PLAYER_GRAVITY:
10651 for (i = 0; i < MAX_PLAYERS; i++)
10653 if (trigger_player_bits & (1 << i))
10655 stored_player[i].gravity =
10656 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10657 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10658 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10659 stored_player[i].gravity);
10667 case CA_SET_PLAYER_ARTWORK:
10669 for (i = 0; i < MAX_PLAYERS; i++)
10671 if (trigger_player_bits & (1 << i))
10673 int artwork_element = action_arg_element;
10675 if (action_arg == CA_ARG_ELEMENT_RESET)
10677 (level.use_artwork_element[i] ? level.artwork_element[i] :
10678 stored_player[i].element_nr);
10680 #if USE_GFX_RESET_PLAYER_ARTWORK
10681 if (stored_player[i].artwork_element != artwork_element)
10682 stored_player[i].Frame = 0;
10685 stored_player[i].artwork_element = artwork_element;
10687 SetPlayerWaiting(&stored_player[i], FALSE);
10689 /* set number of special actions for bored and sleeping animation */
10690 stored_player[i].num_special_action_bored =
10691 get_num_special_action(artwork_element,
10692 ACTION_BORING_1, ACTION_BORING_LAST);
10693 stored_player[i].num_special_action_sleeping =
10694 get_num_special_action(artwork_element,
10695 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10702 case CA_SET_PLAYER_INVENTORY:
10704 for (i = 0; i < MAX_PLAYERS; i++)
10706 struct PlayerInfo *player = &stored_player[i];
10709 if (trigger_player_bits & (1 << i))
10711 int inventory_element = action_arg_element;
10713 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10714 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10715 action_arg == CA_ARG_ELEMENT_ACTION)
10717 int element = inventory_element;
10718 int collect_count = element_info[element].collect_count_initial;
10720 if (!IS_CUSTOM_ELEMENT(element))
10723 if (collect_count == 0)
10724 player->inventory_infinite_element = element;
10726 for (k = 0; k < collect_count; k++)
10727 if (player->inventory_size < MAX_INVENTORY_SIZE)
10728 player->inventory_element[player->inventory_size++] =
10731 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10732 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10733 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10735 if (player->inventory_infinite_element != EL_UNDEFINED &&
10736 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10737 action_arg_element_raw))
10738 player->inventory_infinite_element = EL_UNDEFINED;
10740 for (k = 0, j = 0; j < player->inventory_size; j++)
10742 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10743 action_arg_element_raw))
10744 player->inventory_element[k++] = player->inventory_element[j];
10747 player->inventory_size = k;
10749 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10751 if (player->inventory_size > 0)
10753 for (j = 0; j < player->inventory_size - 1; j++)
10754 player->inventory_element[j] = player->inventory_element[j + 1];
10756 player->inventory_size--;
10759 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10761 if (player->inventory_size > 0)
10762 player->inventory_size--;
10764 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10766 player->inventory_infinite_element = EL_UNDEFINED;
10767 player->inventory_size = 0;
10769 else if (action_arg == CA_ARG_INVENTORY_RESET)
10771 player->inventory_infinite_element = EL_UNDEFINED;
10772 player->inventory_size = 0;
10774 if (level.use_initial_inventory[i])
10776 for (j = 0; j < level.initial_inventory_size[i]; j++)
10778 int element = level.initial_inventory_content[i][j];
10779 int collect_count = element_info[element].collect_count_initial;
10781 if (!IS_CUSTOM_ELEMENT(element))
10784 if (collect_count == 0)
10785 player->inventory_infinite_element = element;
10787 for (k = 0; k < collect_count; k++)
10788 if (player->inventory_size < MAX_INVENTORY_SIZE)
10789 player->inventory_element[player->inventory_size++] =
10800 /* ---------- CE actions ---------------------------------------------- */
10802 case CA_SET_CE_VALUE:
10804 #if USE_NEW_CUSTOM_VALUE
10805 int last_ce_value = CustomValue[x][y];
10807 CustomValue[x][y] = action_arg_number_new;
10809 if (CustomValue[x][y] != last_ce_value)
10811 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10812 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10814 if (CustomValue[x][y] == 0)
10816 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10817 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10825 case CA_SET_CE_SCORE:
10827 #if USE_NEW_CUSTOM_VALUE
10828 int last_ce_score = ei->collect_score;
10830 ei->collect_score = action_arg_number_new;
10832 if (ei->collect_score != last_ce_score)
10834 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10835 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10837 if (ei->collect_score == 0)
10841 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10842 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10845 This is a very special case that seems to be a mixture between
10846 CheckElementChange() and CheckTriggeredElementChange(): while
10847 the first one only affects single elements that are triggered
10848 directly, the second one affects multiple elements in the playfield
10849 that are triggered indirectly by another element. This is a third
10850 case: Changing the CE score always affects multiple identical CEs,
10851 so every affected CE must be checked, not only the single CE for
10852 which the CE score was changed in the first place (as every instance
10853 of that CE shares the same CE score, and therefore also can change)!
10855 SCAN_PLAYFIELD(xx, yy)
10857 if (Feld[xx][yy] == element)
10858 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10859 CE_SCORE_GETS_ZERO);
10868 case CA_SET_CE_ARTWORK:
10870 int artwork_element = action_arg_element;
10871 boolean reset_frame = FALSE;
10874 if (action_arg == CA_ARG_ELEMENT_RESET)
10875 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10878 if (ei->gfx_element != artwork_element)
10879 reset_frame = TRUE;
10881 ei->gfx_element = artwork_element;
10883 SCAN_PLAYFIELD(xx, yy)
10885 if (Feld[xx][yy] == element)
10889 ResetGfxAnimation(xx, yy);
10890 ResetRandomAnimationValue(xx, yy);
10893 TEST_DrawLevelField(xx, yy);
10900 /* ---------- engine actions ------------------------------------------ */
10902 case CA_SET_ENGINE_SCAN_MODE:
10904 InitPlayfieldScanMode(action_arg);
10914 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10916 int old_element = Feld[x][y];
10917 int new_element = GetElementFromGroupElement(element);
10918 int previous_move_direction = MovDir[x][y];
10919 #if USE_NEW_CUSTOM_VALUE
10920 int last_ce_value = CustomValue[x][y];
10922 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10923 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10924 boolean add_player_onto_element = (new_element_is_player &&
10925 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10926 /* this breaks SnakeBite when a snake is
10927 halfway through a door that closes */
10928 /* NOW FIXED AT LEVEL INIT IN files.c */
10929 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10931 IS_WALKABLE(old_element));
10934 /* check if element under the player changes from accessible to unaccessible
10935 (needed for special case of dropping element which then changes) */
10936 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10937 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10945 if (!add_player_onto_element)
10947 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10948 RemoveMovingField(x, y);
10952 Feld[x][y] = new_element;
10954 #if !USE_GFX_RESET_GFX_ANIMATION
10955 ResetGfxAnimation(x, y);
10956 ResetRandomAnimationValue(x, y);
10959 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10960 MovDir[x][y] = previous_move_direction;
10962 #if USE_NEW_CUSTOM_VALUE
10963 if (element_info[new_element].use_last_ce_value)
10964 CustomValue[x][y] = last_ce_value;
10967 InitField_WithBug1(x, y, FALSE);
10969 new_element = Feld[x][y]; /* element may have changed */
10971 #if USE_GFX_RESET_GFX_ANIMATION
10972 ResetGfxAnimation(x, y);
10973 ResetRandomAnimationValue(x, y);
10976 TEST_DrawLevelField(x, y);
10978 if (GFX_CRUMBLED(new_element))
10979 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10983 /* check if element under the player changes from accessible to unaccessible
10984 (needed for special case of dropping element which then changes) */
10985 /* (must be checked after creating new element for walkable group elements) */
10986 #if USE_FIX_KILLED_BY_NON_WALKABLE
10987 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10988 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10995 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10996 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11005 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11006 if (new_element_is_player)
11007 RelocatePlayer(x, y, new_element);
11010 ChangeCount[x][y]++; /* count number of changes in the same frame */
11012 TestIfBadThingTouchesPlayer(x, y);
11013 TestIfPlayerTouchesCustomElement(x, y);
11014 TestIfElementTouchesCustomElement(x, y);
11017 static void CreateField(int x, int y, int element)
11019 CreateFieldExt(x, y, element, FALSE);
11022 static void CreateElementFromChange(int x, int y, int element)
11024 element = GET_VALID_RUNTIME_ELEMENT(element);
11026 #if USE_STOP_CHANGED_ELEMENTS
11027 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11029 int old_element = Feld[x][y];
11031 /* prevent changed element from moving in same engine frame
11032 unless both old and new element can either fall or move */
11033 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11034 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11039 CreateFieldExt(x, y, element, TRUE);
11042 static boolean ChangeElement(int x, int y, int element, int page)
11044 struct ElementInfo *ei = &element_info[element];
11045 struct ElementChangeInfo *change = &ei->change_page[page];
11046 int ce_value = CustomValue[x][y];
11047 int ce_score = ei->collect_score;
11048 int target_element;
11049 int old_element = Feld[x][y];
11051 /* always use default change event to prevent running into a loop */
11052 if (ChangeEvent[x][y] == -1)
11053 ChangeEvent[x][y] = CE_DELAY;
11055 if (ChangeEvent[x][y] == CE_DELAY)
11057 /* reset actual trigger element, trigger player and action element */
11058 change->actual_trigger_element = EL_EMPTY;
11059 change->actual_trigger_player = EL_EMPTY;
11060 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11061 change->actual_trigger_side = CH_SIDE_NONE;
11062 change->actual_trigger_ce_value = 0;
11063 change->actual_trigger_ce_score = 0;
11066 /* do not change elements more than a specified maximum number of changes */
11067 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11070 ChangeCount[x][y]++; /* count number of changes in the same frame */
11072 if (change->explode)
11079 if (change->use_target_content)
11081 boolean complete_replace = TRUE;
11082 boolean can_replace[3][3];
11085 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11088 boolean is_walkable;
11089 boolean is_diggable;
11090 boolean is_collectible;
11091 boolean is_removable;
11092 boolean is_destructible;
11093 int ex = x + xx - 1;
11094 int ey = y + yy - 1;
11095 int content_element = change->target_content.e[xx][yy];
11098 can_replace[xx][yy] = TRUE;
11100 if (ex == x && ey == y) /* do not check changing element itself */
11103 if (content_element == EL_EMPTY_SPACE)
11105 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11110 if (!IN_LEV_FIELD(ex, ey))
11112 can_replace[xx][yy] = FALSE;
11113 complete_replace = FALSE;
11120 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11121 e = MovingOrBlocked2Element(ex, ey);
11123 is_empty = (IS_FREE(ex, ey) ||
11124 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11126 is_walkable = (is_empty || IS_WALKABLE(e));
11127 is_diggable = (is_empty || IS_DIGGABLE(e));
11128 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11129 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11130 is_removable = (is_diggable || is_collectible);
11132 can_replace[xx][yy] =
11133 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11134 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11135 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11136 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11137 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11138 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11139 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11141 if (!can_replace[xx][yy])
11142 complete_replace = FALSE;
11145 if (!change->only_if_complete || complete_replace)
11147 boolean something_has_changed = FALSE;
11149 if (change->only_if_complete && change->use_random_replace &&
11150 RND(100) < change->random_percentage)
11153 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11155 int ex = x + xx - 1;
11156 int ey = y + yy - 1;
11157 int content_element;
11159 if (can_replace[xx][yy] && (!change->use_random_replace ||
11160 RND(100) < change->random_percentage))
11162 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11163 RemoveMovingField(ex, ey);
11165 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11167 content_element = change->target_content.e[xx][yy];
11168 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11169 ce_value, ce_score);
11171 CreateElementFromChange(ex, ey, target_element);
11173 something_has_changed = TRUE;
11175 /* for symmetry reasons, freeze newly created border elements */
11176 if (ex != x || ey != y)
11177 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11181 if (something_has_changed)
11183 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11184 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11190 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11191 ce_value, ce_score);
11193 if (element == EL_DIAGONAL_GROWING ||
11194 element == EL_DIAGONAL_SHRINKING)
11196 target_element = Store[x][y];
11198 Store[x][y] = EL_EMPTY;
11201 CreateElementFromChange(x, y, target_element);
11203 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11204 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11207 /* this uses direct change before indirect change */
11208 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11213 #if USE_NEW_DELAYED_ACTION
11215 static void HandleElementChange(int x, int y, int page)
11217 int element = MovingOrBlocked2Element(x, y);
11218 struct ElementInfo *ei = &element_info[element];
11219 struct ElementChangeInfo *change = &ei->change_page[page];
11220 boolean handle_action_before_change = FALSE;
11223 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11224 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11227 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11228 x, y, element, element_info[element].token_name);
11229 printf("HandleElementChange(): This should never happen!\n");
11234 /* this can happen with classic bombs on walkable, changing elements */
11235 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11238 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11239 ChangeDelay[x][y] = 0;
11245 if (ChangeDelay[x][y] == 0) /* initialize element change */
11247 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11249 if (change->can_change)
11252 /* !!! not clear why graphic animation should be reset at all here !!! */
11253 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11254 #if USE_GFX_RESET_WHEN_NOT_MOVING
11255 /* when a custom element is about to change (for example by change delay),
11256 do not reset graphic animation when the custom element is moving */
11257 if (!IS_MOVING(x, y))
11260 ResetGfxAnimation(x, y);
11261 ResetRandomAnimationValue(x, y);
11265 if (change->pre_change_function)
11266 change->pre_change_function(x, y);
11270 ChangeDelay[x][y]--;
11272 if (ChangeDelay[x][y] != 0) /* continue element change */
11274 if (change->can_change)
11276 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11278 if (IS_ANIMATED(graphic))
11279 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11281 if (change->change_function)
11282 change->change_function(x, y);
11285 else /* finish element change */
11287 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11289 page = ChangePage[x][y];
11290 ChangePage[x][y] = -1;
11292 change = &ei->change_page[page];
11295 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11297 ChangeDelay[x][y] = 1; /* try change after next move step */
11298 ChangePage[x][y] = page; /* remember page to use for change */
11304 /* special case: set new level random seed before changing element */
11305 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11306 handle_action_before_change = TRUE;
11308 if (change->has_action && handle_action_before_change)
11309 ExecuteCustomElementAction(x, y, element, page);
11312 if (change->can_change)
11314 if (ChangeElement(x, y, element, page))
11316 if (change->post_change_function)
11317 change->post_change_function(x, y);
11321 if (change->has_action && !handle_action_before_change)
11322 ExecuteCustomElementAction(x, y, element, page);
11328 static void HandleElementChange(int x, int y, int page)
11330 int element = MovingOrBlocked2Element(x, y);
11331 struct ElementInfo *ei = &element_info[element];
11332 struct ElementChangeInfo *change = &ei->change_page[page];
11335 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11338 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11339 x, y, element, element_info[element].token_name);
11340 printf("HandleElementChange(): This should never happen!\n");
11345 /* this can happen with classic bombs on walkable, changing elements */
11346 if (!CAN_CHANGE(element))
11349 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11350 ChangeDelay[x][y] = 0;
11356 if (ChangeDelay[x][y] == 0) /* initialize element change */
11358 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11360 ResetGfxAnimation(x, y);
11361 ResetRandomAnimationValue(x, y);
11363 if (change->pre_change_function)
11364 change->pre_change_function(x, y);
11367 ChangeDelay[x][y]--;
11369 if (ChangeDelay[x][y] != 0) /* continue element change */
11371 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11373 if (IS_ANIMATED(graphic))
11374 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11376 if (change->change_function)
11377 change->change_function(x, y);
11379 else /* finish element change */
11381 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11383 page = ChangePage[x][y];
11384 ChangePage[x][y] = -1;
11386 change = &ei->change_page[page];
11389 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11391 ChangeDelay[x][y] = 1; /* try change after next move step */
11392 ChangePage[x][y] = page; /* remember page to use for change */
11397 if (ChangeElement(x, y, element, page))
11399 if (change->post_change_function)
11400 change->post_change_function(x, y);
11407 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11408 int trigger_element,
11410 int trigger_player,
11414 boolean change_done_any = FALSE;
11415 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11418 if (!(trigger_events[trigger_element][trigger_event]))
11422 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11423 trigger_event, recursion_loop_depth, recursion_loop_detected,
11424 recursion_loop_element, EL_NAME(recursion_loop_element));
11427 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11429 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11431 int element = EL_CUSTOM_START + i;
11432 boolean change_done = FALSE;
11435 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11436 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11439 for (p = 0; p < element_info[element].num_change_pages; p++)
11441 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11443 if (change->can_change_or_has_action &&
11444 change->has_event[trigger_event] &&
11445 change->trigger_side & trigger_side &&
11446 change->trigger_player & trigger_player &&
11447 change->trigger_page & trigger_page_bits &&
11448 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11450 change->actual_trigger_element = trigger_element;
11451 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11452 change->actual_trigger_player_bits = trigger_player;
11453 change->actual_trigger_side = trigger_side;
11454 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11455 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11458 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11459 element, EL_NAME(element), p);
11462 if ((change->can_change && !change_done) || change->has_action)
11466 SCAN_PLAYFIELD(x, y)
11468 if (Feld[x][y] == element)
11470 if (change->can_change && !change_done)
11472 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11473 /* if element already changed in this frame, not only prevent
11474 another element change (checked in ChangeElement()), but
11475 also prevent additional element actions for this element */
11477 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11478 !level.use_action_after_change_bug)
11483 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11484 element, EL_NAME(element), p);
11487 ChangeDelay[x][y] = 1;
11488 ChangeEvent[x][y] = trigger_event;
11490 HandleElementChange(x, y, p);
11492 #if USE_NEW_DELAYED_ACTION
11493 else if (change->has_action)
11495 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11496 /* if element already changed in this frame, not only prevent
11497 another element change (checked in ChangeElement()), but
11498 also prevent additional element actions for this element */
11500 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11501 !level.use_action_after_change_bug)
11507 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11508 element, EL_NAME(element), p);
11511 ExecuteCustomElementAction(x, y, element, p);
11512 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11515 if (change->has_action)
11517 ExecuteCustomElementAction(x, y, element, p);
11518 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11524 if (change->can_change)
11526 change_done = TRUE;
11527 change_done_any = TRUE;
11530 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11531 element, EL_NAME(element), p);
11540 RECURSION_LOOP_DETECTION_END();
11542 return change_done_any;
11545 static boolean CheckElementChangeExt(int x, int y,
11547 int trigger_element,
11549 int trigger_player,
11552 boolean change_done = FALSE;
11555 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11556 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11559 if (Feld[x][y] == EL_BLOCKED)
11561 Blocked2Moving(x, y, &x, &y);
11562 element = Feld[x][y];
11566 /* check if element has already changed */
11567 if (Feld[x][y] != element)
11570 /* check if element has already changed or is about to change after moving */
11571 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11572 Feld[x][y] != element) ||
11574 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11575 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11576 ChangePage[x][y] != -1)))
11581 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11582 trigger_event, recursion_loop_depth, recursion_loop_detected,
11583 recursion_loop_element, EL_NAME(recursion_loop_element));
11586 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11589 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11592 for (p = 0; p < element_info[element].num_change_pages; p++)
11594 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11596 /* check trigger element for all events where the element that is checked
11597 for changing interacts with a directly adjacent element -- this is
11598 different to element changes that affect other elements to change on the
11599 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11600 boolean check_trigger_element =
11601 (trigger_event == CE_TOUCHING_X ||
11602 trigger_event == CE_HITTING_X ||
11603 trigger_event == CE_HIT_BY_X ||
11605 /* this one was forgotten until 3.2.3 */
11606 trigger_event == CE_DIGGING_X);
11609 if (change->can_change_or_has_action &&
11610 change->has_event[trigger_event] &&
11611 change->trigger_side & trigger_side &&
11612 change->trigger_player & trigger_player &&
11613 (!check_trigger_element ||
11614 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11616 change->actual_trigger_element = trigger_element;
11617 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11618 change->actual_trigger_player_bits = trigger_player;
11619 change->actual_trigger_side = trigger_side;
11620 change->actual_trigger_ce_value = CustomValue[x][y];
11621 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11623 /* special case: trigger element not at (x,y) position for some events */
11624 if (check_trigger_element)
11636 { 0, 0 }, { 0, 0 }, { 0, 0 },
11640 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11641 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11643 change->actual_trigger_ce_value = CustomValue[xx][yy];
11644 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11647 if (change->can_change && !change_done)
11649 ChangeDelay[x][y] = 1;
11650 ChangeEvent[x][y] = trigger_event;
11652 HandleElementChange(x, y, p);
11654 change_done = TRUE;
11656 #if USE_NEW_DELAYED_ACTION
11657 else if (change->has_action)
11659 ExecuteCustomElementAction(x, y, element, p);
11660 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11663 if (change->has_action)
11665 ExecuteCustomElementAction(x, y, element, p);
11666 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11672 RECURSION_LOOP_DETECTION_END();
11674 return change_done;
11677 static void PlayPlayerSound(struct PlayerInfo *player)
11679 int jx = player->jx, jy = player->jy;
11680 int sound_element = player->artwork_element;
11681 int last_action = player->last_action_waiting;
11682 int action = player->action_waiting;
11684 if (player->is_waiting)
11686 if (action != last_action)
11687 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11689 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11693 if (action != last_action)
11694 StopSound(element_info[sound_element].sound[last_action]);
11696 if (last_action == ACTION_SLEEPING)
11697 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11701 static void PlayAllPlayersSound()
11705 for (i = 0; i < MAX_PLAYERS; i++)
11706 if (stored_player[i].active)
11707 PlayPlayerSound(&stored_player[i]);
11710 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11712 boolean last_waiting = player->is_waiting;
11713 int move_dir = player->MovDir;
11715 player->dir_waiting = move_dir;
11716 player->last_action_waiting = player->action_waiting;
11720 if (!last_waiting) /* not waiting -> waiting */
11722 player->is_waiting = TRUE;
11724 player->frame_counter_bored =
11726 game.player_boring_delay_fixed +
11727 GetSimpleRandom(game.player_boring_delay_random);
11728 player->frame_counter_sleeping =
11730 game.player_sleeping_delay_fixed +
11731 GetSimpleRandom(game.player_sleeping_delay_random);
11733 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11736 if (game.player_sleeping_delay_fixed +
11737 game.player_sleeping_delay_random > 0 &&
11738 player->anim_delay_counter == 0 &&
11739 player->post_delay_counter == 0 &&
11740 FrameCounter >= player->frame_counter_sleeping)
11741 player->is_sleeping = TRUE;
11742 else if (game.player_boring_delay_fixed +
11743 game.player_boring_delay_random > 0 &&
11744 FrameCounter >= player->frame_counter_bored)
11745 player->is_bored = TRUE;
11747 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11748 player->is_bored ? ACTION_BORING :
11751 if (player->is_sleeping && player->use_murphy)
11753 /* special case for sleeping Murphy when leaning against non-free tile */
11755 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11756 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11757 !IS_MOVING(player->jx - 1, player->jy)))
11758 move_dir = MV_LEFT;
11759 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11760 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11761 !IS_MOVING(player->jx + 1, player->jy)))
11762 move_dir = MV_RIGHT;
11764 player->is_sleeping = FALSE;
11766 player->dir_waiting = move_dir;
11769 if (player->is_sleeping)
11771 if (player->num_special_action_sleeping > 0)
11773 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11775 int last_special_action = player->special_action_sleeping;
11776 int num_special_action = player->num_special_action_sleeping;
11777 int special_action =
11778 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11779 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11780 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11781 last_special_action + 1 : ACTION_SLEEPING);
11782 int special_graphic =
11783 el_act_dir2img(player->artwork_element, special_action, move_dir);
11785 player->anim_delay_counter =
11786 graphic_info[special_graphic].anim_delay_fixed +
11787 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11788 player->post_delay_counter =
11789 graphic_info[special_graphic].post_delay_fixed +
11790 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11792 player->special_action_sleeping = special_action;
11795 if (player->anim_delay_counter > 0)
11797 player->action_waiting = player->special_action_sleeping;
11798 player->anim_delay_counter--;
11800 else if (player->post_delay_counter > 0)
11802 player->post_delay_counter--;
11806 else if (player->is_bored)
11808 if (player->num_special_action_bored > 0)
11810 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11812 int special_action =
11813 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11814 int special_graphic =
11815 el_act_dir2img(player->artwork_element, special_action, move_dir);
11817 player->anim_delay_counter =
11818 graphic_info[special_graphic].anim_delay_fixed +
11819 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11820 player->post_delay_counter =
11821 graphic_info[special_graphic].post_delay_fixed +
11822 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11824 player->special_action_bored = special_action;
11827 if (player->anim_delay_counter > 0)
11829 player->action_waiting = player->special_action_bored;
11830 player->anim_delay_counter--;
11832 else if (player->post_delay_counter > 0)
11834 player->post_delay_counter--;
11839 else if (last_waiting) /* waiting -> not waiting */
11841 player->is_waiting = FALSE;
11842 player->is_bored = FALSE;
11843 player->is_sleeping = FALSE;
11845 player->frame_counter_bored = -1;
11846 player->frame_counter_sleeping = -1;
11848 player->anim_delay_counter = 0;
11849 player->post_delay_counter = 0;
11851 player->dir_waiting = player->MovDir;
11852 player->action_waiting = ACTION_DEFAULT;
11854 player->special_action_bored = ACTION_DEFAULT;
11855 player->special_action_sleeping = ACTION_DEFAULT;
11859 static void CheckSingleStepMode(struct PlayerInfo *player)
11861 if (tape.single_step && tape.recording && !tape.pausing)
11863 /* as it is called "single step mode", just return to pause mode when the
11864 player stopped moving after one tile (or never starts moving at all) */
11865 if (!player->is_moving && !player->is_pushing)
11867 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11868 SnapField(player, 0, 0); /* stop snapping */
11873 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11875 int left = player_action & JOY_LEFT;
11876 int right = player_action & JOY_RIGHT;
11877 int up = player_action & JOY_UP;
11878 int down = player_action & JOY_DOWN;
11879 int button1 = player_action & JOY_BUTTON_1;
11880 int button2 = player_action & JOY_BUTTON_2;
11881 int dx = (left ? -1 : right ? 1 : 0);
11882 int dy = (up ? -1 : down ? 1 : 0);
11884 if (!player->active || tape.pausing)
11890 SnapField(player, dx, dy);
11894 DropElement(player);
11896 MovePlayer(player, dx, dy);
11899 CheckSingleStepMode(player);
11901 SetPlayerWaiting(player, FALSE);
11903 return player_action;
11907 /* no actions for this player (no input at player's configured device) */
11909 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11910 SnapField(player, 0, 0);
11911 CheckGravityMovementWhenNotMoving(player);
11913 if (player->MovPos == 0)
11914 SetPlayerWaiting(player, TRUE);
11916 if (player->MovPos == 0) /* needed for tape.playing */
11917 player->is_moving = FALSE;
11919 player->is_dropping = FALSE;
11920 player->is_dropping_pressed = FALSE;
11921 player->drop_pressed_delay = 0;
11923 CheckSingleStepMode(player);
11929 static void CheckLevelTime()
11933 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11934 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11936 if (level.native_em_level->lev->home == 0) /* all players at home */
11938 PlayerWins(local_player);
11940 AllPlayersGone = TRUE;
11942 level.native_em_level->lev->home = -1;
11945 if (level.native_em_level->ply[0]->alive == 0 &&
11946 level.native_em_level->ply[1]->alive == 0 &&
11947 level.native_em_level->ply[2]->alive == 0 &&
11948 level.native_em_level->ply[3]->alive == 0) /* all dead */
11949 AllPlayersGone = TRUE;
11951 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11953 if (game_sp.LevelSolved &&
11954 !game_sp.GameOver) /* game won */
11956 PlayerWins(local_player);
11958 game_sp.GameOver = TRUE;
11960 AllPlayersGone = TRUE;
11963 if (game_sp.GameOver) /* game lost */
11964 AllPlayersGone = TRUE;
11967 if (TimeFrames >= FRAMES_PER_SECOND)
11972 for (i = 0; i < MAX_PLAYERS; i++)
11974 struct PlayerInfo *player = &stored_player[i];
11976 if (SHIELD_ON(player))
11978 player->shield_normal_time_left--;
11980 if (player->shield_deadly_time_left > 0)
11981 player->shield_deadly_time_left--;
11985 if (!local_player->LevelSolved && !level.use_step_counter)
11993 if (TimeLeft <= 10 && setup.time_limit)
11994 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11997 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11998 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12000 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12002 /* (already called by UpdateAndDisplayGameControlValues() below) */
12003 // DisplayGameControlValues();
12005 DrawGameValue_Time(TimeLeft);
12008 if (!TimeLeft && setup.time_limit)
12010 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12011 level.native_em_level->lev->killed_out_of_time = TRUE;
12013 for (i = 0; i < MAX_PLAYERS; i++)
12014 KillPlayer(&stored_player[i]);
12018 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12020 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12022 /* (already called by UpdateAndDisplayGameControlValues() below) */
12023 // DisplayGameControlValues();
12026 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12027 DrawGameValue_Time(TimePlayed);
12030 level.native_em_level->lev->time =
12031 (game.no_time_limit ? TimePlayed : TimeLeft);
12034 if (tape.recording || tape.playing)
12035 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12039 UpdateAndDisplayGameControlValues();
12041 UpdateGameDoorValues();
12042 DrawGameDoorValues();
12046 void AdvanceFrameAndPlayerCounters(int player_nr)
12050 /* advance frame counters (global frame counter and time frame counter) */
12054 /* advance player counters (counters for move delay, move animation etc.) */
12055 for (i = 0; i < MAX_PLAYERS; i++)
12057 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12058 int move_delay_value = stored_player[i].move_delay_value;
12059 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12061 if (!advance_player_counters) /* not all players may be affected */
12064 #if USE_NEW_PLAYER_ANIM
12065 if (move_frames == 0) /* less than one move per game frame */
12067 int stepsize = TILEX / move_delay_value;
12068 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12069 int count = (stored_player[i].is_moving ?
12070 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12072 if (count % delay == 0)
12077 stored_player[i].Frame += move_frames;
12079 if (stored_player[i].MovPos != 0)
12080 stored_player[i].StepFrame += move_frames;
12082 if (stored_player[i].move_delay > 0)
12083 stored_player[i].move_delay--;
12085 /* due to bugs in previous versions, counter must count up, not down */
12086 if (stored_player[i].push_delay != -1)
12087 stored_player[i].push_delay++;
12089 if (stored_player[i].drop_delay > 0)
12090 stored_player[i].drop_delay--;
12092 if (stored_player[i].is_dropping_pressed)
12093 stored_player[i].drop_pressed_delay++;
12097 void StartGameActions(boolean init_network_game, boolean record_tape,
12100 unsigned int new_random_seed = InitRND(random_seed);
12103 TapeStartRecording(new_random_seed);
12105 #if defined(NETWORK_AVALIABLE)
12106 if (init_network_game)
12108 SendToServer_StartPlaying();
12119 static unsigned int game_frame_delay = 0;
12120 unsigned int game_frame_delay_value;
12121 byte *recorded_player_action;
12122 byte summarized_player_action = 0;
12123 byte tape_action[MAX_PLAYERS];
12126 /* detect endless loops, caused by custom element programming */
12127 if (recursion_loop_detected && recursion_loop_depth == 0)
12129 char *message = getStringCat3("Internal Error ! Element ",
12130 EL_NAME(recursion_loop_element),
12131 " caused endless loop ! Quit the game ?");
12133 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12134 EL_NAME(recursion_loop_element));
12136 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12138 recursion_loop_detected = FALSE; /* if game should be continued */
12145 if (game.restart_level)
12146 StartGameActions(options.network, setup.autorecord, level.random_seed);
12148 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12149 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12151 if (level.native_em_level->lev->home == 0) /* all players at home */
12153 PlayerWins(local_player);
12155 AllPlayersGone = TRUE;
12157 level.native_em_level->lev->home = -1;
12160 if (level.native_em_level->ply[0]->alive == 0 &&
12161 level.native_em_level->ply[1]->alive == 0 &&
12162 level.native_em_level->ply[2]->alive == 0 &&
12163 level.native_em_level->ply[3]->alive == 0) /* all dead */
12164 AllPlayersGone = TRUE;
12166 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12168 if (game_sp.LevelSolved &&
12169 !game_sp.GameOver) /* game won */
12171 PlayerWins(local_player);
12173 game_sp.GameOver = TRUE;
12175 AllPlayersGone = TRUE;
12178 if (game_sp.GameOver) /* game lost */
12179 AllPlayersGone = TRUE;
12182 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12185 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12188 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12191 game_frame_delay_value =
12192 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12194 if (tape.playing && tape.warp_forward && !tape.pausing)
12195 game_frame_delay_value = 0;
12197 /* ---------- main game synchronization point ---------- */
12199 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12201 if (network_playing && !network_player_action_received)
12203 /* try to get network player actions in time */
12205 #if defined(NETWORK_AVALIABLE)
12206 /* last chance to get network player actions without main loop delay */
12207 HandleNetworking();
12210 /* game was quit by network peer */
12211 if (game_status != GAME_MODE_PLAYING)
12214 if (!network_player_action_received)
12215 return; /* failed to get network player actions in time */
12217 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12223 /* at this point we know that we really continue executing the game */
12225 network_player_action_received = FALSE;
12227 /* when playing tape, read previously recorded player input from tape data */
12228 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12231 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12236 if (tape.set_centered_player)
12238 game.centered_player_nr_next = tape.centered_player_nr_next;
12239 game.set_centered_player = TRUE;
12242 for (i = 0; i < MAX_PLAYERS; i++)
12244 summarized_player_action |= stored_player[i].action;
12246 if (!network_playing)
12247 stored_player[i].effective_action = stored_player[i].action;
12250 #if defined(NETWORK_AVALIABLE)
12251 if (network_playing)
12252 SendToServer_MovePlayer(summarized_player_action);
12255 if (!options.network && !setup.team_mode)
12256 local_player->effective_action = summarized_player_action;
12258 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12260 for (i = 0; i < MAX_PLAYERS; i++)
12261 stored_player[i].effective_action =
12262 (i == game.centered_player_nr ? summarized_player_action : 0);
12265 if (recorded_player_action != NULL)
12266 for (i = 0; i < MAX_PLAYERS; i++)
12267 stored_player[i].effective_action = recorded_player_action[i];
12269 for (i = 0; i < MAX_PLAYERS; i++)
12271 tape_action[i] = stored_player[i].effective_action;
12273 /* (this can only happen in the R'n'D game engine) */
12274 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12275 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12278 /* only record actions from input devices, but not programmed actions */
12279 if (tape.recording)
12280 TapeRecordAction(tape_action);
12282 #if USE_NEW_PLAYER_ASSIGNMENTS
12284 byte mapped_action[MAX_PLAYERS];
12286 for (i = 0; i < MAX_PLAYERS; i++)
12287 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12289 for (i = 0; i < MAX_PLAYERS; i++)
12290 stored_player[i].effective_action = mapped_action[i];
12294 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12296 GameActions_EM_Main();
12298 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12300 GameActions_SP_Main();
12308 void GameActions_EM_Main()
12310 byte effective_action[MAX_PLAYERS];
12311 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12314 for (i = 0; i < MAX_PLAYERS; i++)
12315 effective_action[i] = stored_player[i].effective_action;
12317 GameActions_EM(effective_action, warp_mode);
12321 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12324 void GameActions_SP_Main()
12326 byte effective_action[MAX_PLAYERS];
12327 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12330 for (i = 0; i < MAX_PLAYERS; i++)
12331 effective_action[i] = stored_player[i].effective_action;
12333 GameActions_SP(effective_action, warp_mode);
12337 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12340 void GameActions_RND()
12342 int magic_wall_x = 0, magic_wall_y = 0;
12343 int i, x, y, element, graphic;
12345 InitPlayfieldScanModeVars();
12347 #if USE_ONE_MORE_CHANGE_PER_FRAME
12348 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12350 SCAN_PLAYFIELD(x, y)
12352 ChangeCount[x][y] = 0;
12353 ChangeEvent[x][y] = -1;
12358 if (game.set_centered_player)
12360 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12362 /* switching to "all players" only possible if all players fit to screen */
12363 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12365 game.centered_player_nr_next = game.centered_player_nr;
12366 game.set_centered_player = FALSE;
12369 /* do not switch focus to non-existing (or non-active) player */
12370 if (game.centered_player_nr_next >= 0 &&
12371 !stored_player[game.centered_player_nr_next].active)
12373 game.centered_player_nr_next = game.centered_player_nr;
12374 game.set_centered_player = FALSE;
12378 if (game.set_centered_player &&
12379 ScreenMovPos == 0) /* screen currently aligned at tile position */
12383 if (game.centered_player_nr_next == -1)
12385 setScreenCenteredToAllPlayers(&sx, &sy);
12389 sx = stored_player[game.centered_player_nr_next].jx;
12390 sy = stored_player[game.centered_player_nr_next].jy;
12393 game.centered_player_nr = game.centered_player_nr_next;
12394 game.set_centered_player = FALSE;
12396 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12397 DrawGameDoorValues();
12400 for (i = 0; i < MAX_PLAYERS; i++)
12402 int actual_player_action = stored_player[i].effective_action;
12405 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12406 - rnd_equinox_tetrachloride 048
12407 - rnd_equinox_tetrachloride_ii 096
12408 - rnd_emanuel_schmieg 002
12409 - doctor_sloan_ww 001, 020
12411 if (stored_player[i].MovPos == 0)
12412 CheckGravityMovement(&stored_player[i]);
12415 /* overwrite programmed action with tape action */
12416 if (stored_player[i].programmed_action)
12417 actual_player_action = stored_player[i].programmed_action;
12419 PlayerActions(&stored_player[i], actual_player_action);
12421 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12424 ScrollScreen(NULL, SCROLL_GO_ON);
12426 /* for backwards compatibility, the following code emulates a fixed bug that
12427 occured when pushing elements (causing elements that just made their last
12428 pushing step to already (if possible) make their first falling step in the
12429 same game frame, which is bad); this code is also needed to use the famous
12430 "spring push bug" which is used in older levels and might be wanted to be
12431 used also in newer levels, but in this case the buggy pushing code is only
12432 affecting the "spring" element and no other elements */
12434 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12436 for (i = 0; i < MAX_PLAYERS; i++)
12438 struct PlayerInfo *player = &stored_player[i];
12439 int x = player->jx;
12440 int y = player->jy;
12442 if (player->active && player->is_pushing && player->is_moving &&
12444 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12445 Feld[x][y] == EL_SPRING))
12447 ContinueMoving(x, y);
12449 /* continue moving after pushing (this is actually a bug) */
12450 if (!IS_MOVING(x, y))
12451 Stop[x][y] = FALSE;
12457 debug_print_timestamp(0, "start main loop profiling");
12460 SCAN_PLAYFIELD(x, y)
12462 ChangeCount[x][y] = 0;
12463 ChangeEvent[x][y] = -1;
12465 /* this must be handled before main playfield loop */
12466 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12469 if (MovDelay[x][y] <= 0)
12473 #if USE_NEW_SNAP_DELAY
12474 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12477 if (MovDelay[x][y] <= 0)
12480 TEST_DrawLevelField(x, y);
12482 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12488 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12490 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12491 printf("GameActions(): This should never happen!\n");
12493 ChangePage[x][y] = -1;
12497 Stop[x][y] = FALSE;
12498 if (WasJustMoving[x][y] > 0)
12499 WasJustMoving[x][y]--;
12500 if (WasJustFalling[x][y] > 0)
12501 WasJustFalling[x][y]--;
12502 if (CheckCollision[x][y] > 0)
12503 CheckCollision[x][y]--;
12504 if (CheckImpact[x][y] > 0)
12505 CheckImpact[x][y]--;
12509 /* reset finished pushing action (not done in ContinueMoving() to allow
12510 continuous pushing animation for elements with zero push delay) */
12511 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12513 ResetGfxAnimation(x, y);
12514 TEST_DrawLevelField(x, y);
12518 if (IS_BLOCKED(x, y))
12522 Blocked2Moving(x, y, &oldx, &oldy);
12523 if (!IS_MOVING(oldx, oldy))
12525 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12526 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12527 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12528 printf("GameActions(): This should never happen!\n");
12535 debug_print_timestamp(0, "- time for pre-main loop:");
12538 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12539 SCAN_PLAYFIELD(x, y)
12541 element = Feld[x][y];
12542 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12547 int element2 = element;
12548 int graphic2 = graphic;
12550 int element2 = Feld[x][y];
12551 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12553 int last_gfx_frame = GfxFrame[x][y];
12555 if (graphic_info[graphic2].anim_global_sync)
12556 GfxFrame[x][y] = FrameCounter;
12557 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12558 GfxFrame[x][y] = CustomValue[x][y];
12559 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12560 GfxFrame[x][y] = element_info[element2].collect_score;
12561 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12562 GfxFrame[x][y] = ChangeDelay[x][y];
12564 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12565 DrawLevelGraphicAnimation(x, y, graphic2);
12568 ResetGfxFrame(x, y, TRUE);
12572 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12573 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12574 ResetRandomAnimationValue(x, y);
12578 SetRandomAnimationValue(x, y);
12582 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12585 #endif // -------------------- !!! TEST ONLY !!! --------------------
12588 debug_print_timestamp(0, "- time for TEST loop: -->");
12591 SCAN_PLAYFIELD(x, y)
12593 element = Feld[x][y];
12594 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12596 ResetGfxFrame(x, y, TRUE);
12598 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12599 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12600 ResetRandomAnimationValue(x, y);
12602 SetRandomAnimationValue(x, y);
12604 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12606 if (IS_INACTIVE(element))
12608 if (IS_ANIMATED(graphic))
12609 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12614 /* this may take place after moving, so 'element' may have changed */
12615 if (IS_CHANGING(x, y) &&
12616 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12618 int page = element_info[element].event_page_nr[CE_DELAY];
12621 HandleElementChange(x, y, page);
12623 if (CAN_CHANGE(element))
12624 HandleElementChange(x, y, page);
12626 if (HAS_ACTION(element))
12627 ExecuteCustomElementAction(x, y, element, page);
12630 element = Feld[x][y];
12631 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12634 #if 0 // ---------------------------------------------------------------------
12636 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12640 element = Feld[x][y];
12641 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12643 if (IS_ANIMATED(graphic) &&
12644 !IS_MOVING(x, y) &&
12646 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12648 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12649 TEST_DrawTwinkleOnField(x, y);
12651 else if (IS_MOVING(x, y))
12652 ContinueMoving(x, y);
12659 case EL_EM_EXIT_OPEN:
12660 case EL_SP_EXIT_OPEN:
12661 case EL_STEEL_EXIT_OPEN:
12662 case EL_EM_STEEL_EXIT_OPEN:
12663 case EL_SP_TERMINAL:
12664 case EL_SP_TERMINAL_ACTIVE:
12665 case EL_EXTRA_TIME:
12666 case EL_SHIELD_NORMAL:
12667 case EL_SHIELD_DEADLY:
12668 if (IS_ANIMATED(graphic))
12669 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12672 case EL_DYNAMITE_ACTIVE:
12673 case EL_EM_DYNAMITE_ACTIVE:
12674 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12675 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12676 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12677 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12678 case EL_SP_DISK_RED_ACTIVE:
12679 CheckDynamite(x, y);
12682 case EL_AMOEBA_GROWING:
12683 AmoebeWaechst(x, y);
12686 case EL_AMOEBA_SHRINKING:
12687 AmoebaDisappearing(x, y);
12690 #if !USE_NEW_AMOEBA_CODE
12691 case EL_AMOEBA_WET:
12692 case EL_AMOEBA_DRY:
12693 case EL_AMOEBA_FULL:
12695 case EL_EMC_DRIPPER:
12696 AmoebeAbleger(x, y);
12700 case EL_GAME_OF_LIFE:
12705 case EL_EXIT_CLOSED:
12709 case EL_EM_EXIT_CLOSED:
12713 case EL_STEEL_EXIT_CLOSED:
12714 CheckExitSteel(x, y);
12717 case EL_EM_STEEL_EXIT_CLOSED:
12718 CheckExitSteelEM(x, y);
12721 case EL_SP_EXIT_CLOSED:
12725 case EL_EXPANDABLE_WALL_GROWING:
12726 case EL_EXPANDABLE_STEELWALL_GROWING:
12727 MauerWaechst(x, y);
12730 case EL_EXPANDABLE_WALL:
12731 case EL_EXPANDABLE_WALL_HORIZONTAL:
12732 case EL_EXPANDABLE_WALL_VERTICAL:
12733 case EL_EXPANDABLE_WALL_ANY:
12734 case EL_BD_EXPANDABLE_WALL:
12735 MauerAbleger(x, y);
12738 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12739 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12740 case EL_EXPANDABLE_STEELWALL_ANY:
12741 MauerAblegerStahl(x, y);
12745 CheckForDragon(x, y);
12751 case EL_ELEMENT_SNAPPING:
12752 case EL_DIAGONAL_SHRINKING:
12753 case EL_DIAGONAL_GROWING:
12756 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12758 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12763 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12764 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12769 #else // ---------------------------------------------------------------------
12771 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12775 element = Feld[x][y];
12776 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12778 if (IS_ANIMATED(graphic) &&
12779 !IS_MOVING(x, y) &&
12781 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12783 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12784 TEST_DrawTwinkleOnField(x, y);
12786 else if ((element == EL_ACID ||
12787 element == EL_EXIT_OPEN ||
12788 element == EL_EM_EXIT_OPEN ||
12789 element == EL_SP_EXIT_OPEN ||
12790 element == EL_STEEL_EXIT_OPEN ||
12791 element == EL_EM_STEEL_EXIT_OPEN ||
12792 element == EL_SP_TERMINAL ||
12793 element == EL_SP_TERMINAL_ACTIVE ||
12794 element == EL_EXTRA_TIME ||
12795 element == EL_SHIELD_NORMAL ||
12796 element == EL_SHIELD_DEADLY) &&
12797 IS_ANIMATED(graphic))
12798 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12799 else if (IS_MOVING(x, y))
12800 ContinueMoving(x, y);
12801 else if (IS_ACTIVE_BOMB(element))
12802 CheckDynamite(x, y);
12803 else if (element == EL_AMOEBA_GROWING)
12804 AmoebeWaechst(x, y);
12805 else if (element == EL_AMOEBA_SHRINKING)
12806 AmoebaDisappearing(x, y);
12808 #if !USE_NEW_AMOEBA_CODE
12809 else if (IS_AMOEBALIVE(element))
12810 AmoebeAbleger(x, y);
12813 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12815 else if (element == EL_EXIT_CLOSED)
12817 else if (element == EL_EM_EXIT_CLOSED)
12819 else if (element == EL_STEEL_EXIT_CLOSED)
12820 CheckExitSteel(x, y);
12821 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12822 CheckExitSteelEM(x, y);
12823 else if (element == EL_SP_EXIT_CLOSED)
12825 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12826 element == EL_EXPANDABLE_STEELWALL_GROWING)
12827 MauerWaechst(x, y);
12828 else if (element == EL_EXPANDABLE_WALL ||
12829 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12830 element == EL_EXPANDABLE_WALL_VERTICAL ||
12831 element == EL_EXPANDABLE_WALL_ANY ||
12832 element == EL_BD_EXPANDABLE_WALL)
12833 MauerAbleger(x, y);
12834 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12835 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12836 element == EL_EXPANDABLE_STEELWALL_ANY)
12837 MauerAblegerStahl(x, y);
12838 else if (element == EL_FLAMES)
12839 CheckForDragon(x, y);
12840 else if (element == EL_EXPLOSION)
12841 ; /* drawing of correct explosion animation is handled separately */
12842 else if (element == EL_ELEMENT_SNAPPING ||
12843 element == EL_DIAGONAL_SHRINKING ||
12844 element == EL_DIAGONAL_GROWING)
12846 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12848 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12850 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12853 #endif // ---------------------------------------------------------------------
12855 if (IS_BELT_ACTIVE(element))
12856 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12858 if (game.magic_wall_active)
12860 int jx = local_player->jx, jy = local_player->jy;
12862 /* play the element sound at the position nearest to the player */
12863 if ((element == EL_MAGIC_WALL_FULL ||
12864 element == EL_MAGIC_WALL_ACTIVE ||
12865 element == EL_MAGIC_WALL_EMPTYING ||
12866 element == EL_BD_MAGIC_WALL_FULL ||
12867 element == EL_BD_MAGIC_WALL_ACTIVE ||
12868 element == EL_BD_MAGIC_WALL_EMPTYING ||
12869 element == EL_DC_MAGIC_WALL_FULL ||
12870 element == EL_DC_MAGIC_WALL_ACTIVE ||
12871 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12872 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12881 debug_print_timestamp(0, "- time for MAIN loop: -->");
12884 #if USE_NEW_AMOEBA_CODE
12885 /* new experimental amoeba growth stuff */
12886 if (!(FrameCounter % 8))
12888 static unsigned int random = 1684108901;
12890 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12892 x = RND(lev_fieldx);
12893 y = RND(lev_fieldy);
12894 element = Feld[x][y];
12896 if (!IS_PLAYER(x,y) &&
12897 (element == EL_EMPTY ||
12898 CAN_GROW_INTO(element) ||
12899 element == EL_QUICKSAND_EMPTY ||
12900 element == EL_QUICKSAND_FAST_EMPTY ||
12901 element == EL_ACID_SPLASH_LEFT ||
12902 element == EL_ACID_SPLASH_RIGHT))
12904 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12905 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12906 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12907 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12908 Feld[x][y] = EL_AMOEBA_DROP;
12911 random = random * 129 + 1;
12917 if (game.explosions_delayed)
12920 game.explosions_delayed = FALSE;
12922 SCAN_PLAYFIELD(x, y)
12924 element = Feld[x][y];
12926 if (ExplodeField[x][y])
12927 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12928 else if (element == EL_EXPLOSION)
12929 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12931 ExplodeField[x][y] = EX_TYPE_NONE;
12934 game.explosions_delayed = TRUE;
12937 if (game.magic_wall_active)
12939 if (!(game.magic_wall_time_left % 4))
12941 int element = Feld[magic_wall_x][magic_wall_y];
12943 if (element == EL_BD_MAGIC_WALL_FULL ||
12944 element == EL_BD_MAGIC_WALL_ACTIVE ||
12945 element == EL_BD_MAGIC_WALL_EMPTYING)
12946 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12947 else if (element == EL_DC_MAGIC_WALL_FULL ||
12948 element == EL_DC_MAGIC_WALL_ACTIVE ||
12949 element == EL_DC_MAGIC_WALL_EMPTYING)
12950 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12952 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12955 if (game.magic_wall_time_left > 0)
12957 game.magic_wall_time_left--;
12959 if (!game.magic_wall_time_left)
12961 SCAN_PLAYFIELD(x, y)
12963 element = Feld[x][y];
12965 if (element == EL_MAGIC_WALL_ACTIVE ||
12966 element == EL_MAGIC_WALL_FULL)
12968 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12969 TEST_DrawLevelField(x, y);
12971 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12972 element == EL_BD_MAGIC_WALL_FULL)
12974 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12975 TEST_DrawLevelField(x, y);
12977 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12978 element == EL_DC_MAGIC_WALL_FULL)
12980 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12981 TEST_DrawLevelField(x, y);
12985 game.magic_wall_active = FALSE;
12990 if (game.light_time_left > 0)
12992 game.light_time_left--;
12994 if (game.light_time_left == 0)
12995 RedrawAllLightSwitchesAndInvisibleElements();
12998 if (game.timegate_time_left > 0)
13000 game.timegate_time_left--;
13002 if (game.timegate_time_left == 0)
13003 CloseAllOpenTimegates();
13006 if (game.lenses_time_left > 0)
13008 game.lenses_time_left--;
13010 if (game.lenses_time_left == 0)
13011 RedrawAllInvisibleElementsForLenses();
13014 if (game.magnify_time_left > 0)
13016 game.magnify_time_left--;
13018 if (game.magnify_time_left == 0)
13019 RedrawAllInvisibleElementsForMagnifier();
13022 for (i = 0; i < MAX_PLAYERS; i++)
13024 struct PlayerInfo *player = &stored_player[i];
13026 if (SHIELD_ON(player))
13028 if (player->shield_deadly_time_left)
13029 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13030 else if (player->shield_normal_time_left)
13031 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13035 #if USE_DELAYED_GFX_REDRAW
13036 SCAN_PLAYFIELD(x, y)
13039 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13041 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13042 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13045 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13046 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13048 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13049 DrawLevelField(x, y);
13051 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13052 DrawLevelFieldCrumbled(x, y);
13054 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13055 DrawLevelFieldCrumbledNeighbours(x, y);
13057 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13058 DrawTwinkleOnField(x, y);
13061 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13068 PlayAllPlayersSound();
13070 if (options.debug) /* calculate frames per second */
13072 static unsigned int fps_counter = 0;
13073 static int fps_frames = 0;
13074 unsigned int fps_delay_ms = Counter() - fps_counter;
13078 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13080 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13083 fps_counter = Counter();
13086 redraw_mask |= REDRAW_FPS;
13089 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13091 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13093 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13095 local_player->show_envelope = 0;
13099 debug_print_timestamp(0, "stop main loop profiling ");
13100 printf("----------------------------------------------------------\n");
13103 /* use random number generator in every frame to make it less predictable */
13104 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13108 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13110 int min_x = x, min_y = y, max_x = x, max_y = y;
13113 for (i = 0; i < MAX_PLAYERS; i++)
13115 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13117 if (!stored_player[i].active || &stored_player[i] == player)
13120 min_x = MIN(min_x, jx);
13121 min_y = MIN(min_y, jy);
13122 max_x = MAX(max_x, jx);
13123 max_y = MAX(max_y, jy);
13126 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13129 static boolean AllPlayersInVisibleScreen()
13133 for (i = 0; i < MAX_PLAYERS; i++)
13135 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13137 if (!stored_player[i].active)
13140 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13147 void ScrollLevel(int dx, int dy)
13150 /* (directly solved in BlitBitmap() now) */
13151 static Bitmap *bitmap_db_field2 = NULL;
13152 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13159 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13160 /* only horizontal XOR vertical scroll direction allowed */
13161 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13166 /* (directly solved in BlitBitmap() now) */
13167 if (bitmap_db_field2 == NULL)
13168 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13170 /* needed when blitting directly to same bitmap -- should not be needed with
13171 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13172 BlitBitmap(drawto_field, bitmap_db_field2,
13173 FX + TILEX * (dx == -1) - softscroll_offset,
13174 FY + TILEY * (dy == -1) - softscroll_offset,
13175 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13176 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13177 FX + TILEX * (dx == 1) - softscroll_offset,
13178 FY + TILEY * (dy == 1) - softscroll_offset);
13179 BlitBitmap(bitmap_db_field2, drawto_field,
13180 FX + TILEX * (dx == 1) - softscroll_offset,
13181 FY + TILEY * (dy == 1) - softscroll_offset,
13182 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13183 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13184 FX + TILEX * (dx == 1) - softscroll_offset,
13185 FY + TILEY * (dy == 1) - softscroll_offset);
13190 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13191 int xsize = (BX2 - BX1 + 1);
13192 int ysize = (BY2 - BY1 + 1);
13193 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13194 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13195 int step = (start < end ? +1 : -1);
13197 for (i = start; i != end; i += step)
13199 BlitBitmap(drawto_field, drawto_field,
13200 FX + TILEX * (dx != 0 ? i + step : 0),
13201 FY + TILEY * (dy != 0 ? i + step : 0),
13202 TILEX * (dx != 0 ? 1 : xsize),
13203 TILEY * (dy != 0 ? 1 : ysize),
13204 FX + TILEX * (dx != 0 ? i : 0),
13205 FY + TILEY * (dy != 0 ? i : 0));
13212 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13214 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13218 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13220 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13225 BlitBitmap(drawto_field, drawto_field,
13226 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13227 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13228 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13229 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13230 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13231 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13233 BlitBitmap(drawto_field, drawto_field,
13234 FX + TILEX * (dx == -1) - softscroll_offset,
13235 FY + TILEY * (dy == -1) - softscroll_offset,
13236 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13237 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13238 FX + TILEX * (dx == 1) - softscroll_offset,
13239 FY + TILEY * (dy == 1) - softscroll_offset);
13247 x = (dx == 1 ? BX1 : BX2);
13248 for (y = BY1; y <= BY2; y++)
13249 DrawScreenField(x, y);
13254 y = (dy == 1 ? BY1 : BY2);
13255 for (x = BX1; x <= BX2; x++)
13256 DrawScreenField(x, y);
13259 redraw_mask |= REDRAW_FIELD;
13262 static boolean canFallDown(struct PlayerInfo *player)
13264 int jx = player->jx, jy = player->jy;
13266 return (IN_LEV_FIELD(jx, jy + 1) &&
13267 (IS_FREE(jx, jy + 1) ||
13268 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13269 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13270 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13273 static boolean canPassField(int x, int y, int move_dir)
13275 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13276 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13277 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13278 int nextx = x + dx;
13279 int nexty = y + dy;
13280 int element = Feld[x][y];
13282 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13283 !CAN_MOVE(element) &&
13284 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13285 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13286 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13291 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13292 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13293 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13297 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13298 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13299 (IS_DIGGABLE(Feld[newx][newy]) ||
13300 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13301 canPassField(newx, newy, move_dir)));
13304 static void CheckGravityMovement(struct PlayerInfo *player)
13306 #if USE_PLAYER_GRAVITY
13307 if (player->gravity && !player->programmed_action)
13309 if (game.gravity && !player->programmed_action)
13312 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13313 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13314 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13315 int jx = player->jx, jy = player->jy;
13316 boolean player_is_moving_to_valid_field =
13317 (!player_is_snapping &&
13318 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13319 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13320 boolean player_can_fall_down = canFallDown(player);
13322 if (player_can_fall_down &&
13323 !player_is_moving_to_valid_field)
13324 player->programmed_action = MV_DOWN;
13328 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13330 return CheckGravityMovement(player);
13332 #if USE_PLAYER_GRAVITY
13333 if (player->gravity && !player->programmed_action)
13335 if (game.gravity && !player->programmed_action)
13338 int jx = player->jx, jy = player->jy;
13339 boolean field_under_player_is_free =
13340 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13341 boolean player_is_standing_on_valid_field =
13342 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13343 (IS_WALKABLE(Feld[jx][jy]) &&
13344 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13346 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13347 player->programmed_action = MV_DOWN;
13352 MovePlayerOneStep()
13353 -----------------------------------------------------------------------------
13354 dx, dy: direction (non-diagonal) to try to move the player to
13355 real_dx, real_dy: direction as read from input device (can be diagonal)
13358 boolean MovePlayerOneStep(struct PlayerInfo *player,
13359 int dx, int dy, int real_dx, int real_dy)
13361 int jx = player->jx, jy = player->jy;
13362 int new_jx = jx + dx, new_jy = jy + dy;
13363 #if !USE_FIXED_DONT_RUN_INTO
13367 boolean player_can_move = !player->cannot_move;
13369 if (!player->active || (!dx && !dy))
13370 return MP_NO_ACTION;
13372 player->MovDir = (dx < 0 ? MV_LEFT :
13373 dx > 0 ? MV_RIGHT :
13375 dy > 0 ? MV_DOWN : MV_NONE);
13377 if (!IN_LEV_FIELD(new_jx, new_jy))
13378 return MP_NO_ACTION;
13380 if (!player_can_move)
13382 if (player->MovPos == 0)
13384 player->is_moving = FALSE;
13385 player->is_digging = FALSE;
13386 player->is_collecting = FALSE;
13387 player->is_snapping = FALSE;
13388 player->is_pushing = FALSE;
13393 if (!options.network && game.centered_player_nr == -1 &&
13394 !AllPlayersInSight(player, new_jx, new_jy))
13395 return MP_NO_ACTION;
13397 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13398 return MP_NO_ACTION;
13401 #if !USE_FIXED_DONT_RUN_INTO
13402 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13404 /* (moved to DigField()) */
13405 if (player_can_move && DONT_RUN_INTO(element))
13407 if (element == EL_ACID && dx == 0 && dy == 1)
13409 SplashAcid(new_jx, new_jy);
13410 Feld[jx][jy] = EL_PLAYER_1;
13411 InitMovingField(jx, jy, MV_DOWN);
13412 Store[jx][jy] = EL_ACID;
13413 ContinueMoving(jx, jy);
13414 BuryPlayer(player);
13417 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13423 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13424 if (can_move != MP_MOVING)
13427 /* check if DigField() has caused relocation of the player */
13428 if (player->jx != jx || player->jy != jy)
13429 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13431 StorePlayer[jx][jy] = 0;
13432 player->last_jx = jx;
13433 player->last_jy = jy;
13434 player->jx = new_jx;
13435 player->jy = new_jy;
13436 StorePlayer[new_jx][new_jy] = player->element_nr;
13438 if (player->move_delay_value_next != -1)
13440 player->move_delay_value = player->move_delay_value_next;
13441 player->move_delay_value_next = -1;
13445 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13447 player->step_counter++;
13449 PlayerVisit[jx][jy] = FrameCounter;
13451 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13452 player->is_moving = TRUE;
13456 /* should better be called in MovePlayer(), but this breaks some tapes */
13457 ScrollPlayer(player, SCROLL_INIT);
13463 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13465 int jx = player->jx, jy = player->jy;
13466 int old_jx = jx, old_jy = jy;
13467 int moved = MP_NO_ACTION;
13469 if (!player->active)
13474 if (player->MovPos == 0)
13476 player->is_moving = FALSE;
13477 player->is_digging = FALSE;
13478 player->is_collecting = FALSE;
13479 player->is_snapping = FALSE;
13480 player->is_pushing = FALSE;
13486 if (player->move_delay > 0)
13489 player->move_delay = -1; /* set to "uninitialized" value */
13491 /* store if player is automatically moved to next field */
13492 player->is_auto_moving = (player->programmed_action != MV_NONE);
13494 /* remove the last programmed player action */
13495 player->programmed_action = 0;
13497 if (player->MovPos)
13499 /* should only happen if pre-1.2 tape recordings are played */
13500 /* this is only for backward compatibility */
13502 int original_move_delay_value = player->move_delay_value;
13505 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13509 /* scroll remaining steps with finest movement resolution */
13510 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13512 while (player->MovPos)
13514 ScrollPlayer(player, SCROLL_GO_ON);
13515 ScrollScreen(NULL, SCROLL_GO_ON);
13517 AdvanceFrameAndPlayerCounters(player->index_nr);
13523 player->move_delay_value = original_move_delay_value;
13526 player->is_active = FALSE;
13528 if (player->last_move_dir & MV_HORIZONTAL)
13530 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13531 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13535 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13536 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13539 #if USE_FIXED_BORDER_RUNNING_GFX
13540 if (!moved && !player->is_active)
13542 player->is_moving = FALSE;
13543 player->is_digging = FALSE;
13544 player->is_collecting = FALSE;
13545 player->is_snapping = FALSE;
13546 player->is_pushing = FALSE;
13554 if (moved & MP_MOVING && !ScreenMovPos &&
13555 (player->index_nr == game.centered_player_nr ||
13556 game.centered_player_nr == -1))
13558 if (moved & MP_MOVING && !ScreenMovPos &&
13559 (player == local_player || !options.network))
13562 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13563 int offset = game.scroll_delay_value;
13565 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13567 /* actual player has left the screen -- scroll in that direction */
13568 if (jx != old_jx) /* player has moved horizontally */
13569 scroll_x += (jx - old_jx);
13570 else /* player has moved vertically */
13571 scroll_y += (jy - old_jy);
13575 if (jx != old_jx) /* player has moved horizontally */
13577 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13578 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13579 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13581 /* don't scroll over playfield boundaries */
13582 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13583 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13585 /* don't scroll more than one field at a time */
13586 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13588 /* don't scroll against the player's moving direction */
13589 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13590 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13591 scroll_x = old_scroll_x;
13593 else /* player has moved vertically */
13595 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13596 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13597 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13599 /* don't scroll over playfield boundaries */
13600 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13601 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13603 /* don't scroll more than one field at a time */
13604 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13606 /* don't scroll against the player's moving direction */
13607 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13608 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13609 scroll_y = old_scroll_y;
13613 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13616 if (!options.network && game.centered_player_nr == -1 &&
13617 !AllPlayersInVisibleScreen())
13619 scroll_x = old_scroll_x;
13620 scroll_y = old_scroll_y;
13624 if (!options.network && !AllPlayersInVisibleScreen())
13626 scroll_x = old_scroll_x;
13627 scroll_y = old_scroll_y;
13632 ScrollScreen(player, SCROLL_INIT);
13633 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13638 player->StepFrame = 0;
13640 if (moved & MP_MOVING)
13642 if (old_jx != jx && old_jy == jy)
13643 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13644 else if (old_jx == jx && old_jy != jy)
13645 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13647 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13649 player->last_move_dir = player->MovDir;
13650 player->is_moving = TRUE;
13651 player->is_snapping = FALSE;
13652 player->is_switching = FALSE;
13653 player->is_dropping = FALSE;
13654 player->is_dropping_pressed = FALSE;
13655 player->drop_pressed_delay = 0;
13658 /* should better be called here than above, but this breaks some tapes */
13659 ScrollPlayer(player, SCROLL_INIT);
13664 CheckGravityMovementWhenNotMoving(player);
13666 player->is_moving = FALSE;
13668 /* at this point, the player is allowed to move, but cannot move right now
13669 (e.g. because of something blocking the way) -- ensure that the player
13670 is also allowed to move in the next frame (in old versions before 3.1.1,
13671 the player was forced to wait again for eight frames before next try) */
13673 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13674 player->move_delay = 0; /* allow direct movement in the next frame */
13677 if (player->move_delay == -1) /* not yet initialized by DigField() */
13678 player->move_delay = player->move_delay_value;
13680 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13682 TestIfPlayerTouchesBadThing(jx, jy);
13683 TestIfPlayerTouchesCustomElement(jx, jy);
13686 if (!player->active)
13687 RemovePlayer(player);
13692 void ScrollPlayer(struct PlayerInfo *player, int mode)
13694 int jx = player->jx, jy = player->jy;
13695 int last_jx = player->last_jx, last_jy = player->last_jy;
13696 int move_stepsize = TILEX / player->move_delay_value;
13698 #if USE_NEW_PLAYER_SPEED
13699 if (!player->active)
13702 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13705 if (!player->active || player->MovPos == 0)
13709 if (mode == SCROLL_INIT)
13711 player->actual_frame_counter = FrameCounter;
13712 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13714 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13715 Feld[last_jx][last_jy] == EL_EMPTY)
13717 int last_field_block_delay = 0; /* start with no blocking at all */
13718 int block_delay_adjustment = player->block_delay_adjustment;
13720 /* if player blocks last field, add delay for exactly one move */
13721 if (player->block_last_field)
13723 last_field_block_delay += player->move_delay_value;
13725 /* when blocking enabled, prevent moving up despite gravity */
13726 #if USE_PLAYER_GRAVITY
13727 if (player->gravity && player->MovDir == MV_UP)
13728 block_delay_adjustment = -1;
13730 if (game.gravity && player->MovDir == MV_UP)
13731 block_delay_adjustment = -1;
13735 /* add block delay adjustment (also possible when not blocking) */
13736 last_field_block_delay += block_delay_adjustment;
13738 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13739 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13742 #if USE_NEW_PLAYER_SPEED
13743 if (player->MovPos != 0) /* player has not yet reached destination */
13749 else if (!FrameReached(&player->actual_frame_counter, 1))
13752 #if USE_NEW_PLAYER_SPEED
13753 if (player->MovPos != 0)
13755 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13756 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13758 /* before DrawPlayer() to draw correct player graphic for this case */
13759 if (player->MovPos == 0)
13760 CheckGravityMovement(player);
13763 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13764 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13766 /* before DrawPlayer() to draw correct player graphic for this case */
13767 if (player->MovPos == 0)
13768 CheckGravityMovement(player);
13771 if (player->MovPos == 0) /* player reached destination field */
13773 if (player->move_delay_reset_counter > 0)
13775 player->move_delay_reset_counter--;
13777 if (player->move_delay_reset_counter == 0)
13779 /* continue with normal speed after quickly moving through gate */
13780 HALVE_PLAYER_SPEED(player);
13782 /* be able to make the next move without delay */
13783 player->move_delay = 0;
13787 player->last_jx = jx;
13788 player->last_jy = jy;
13790 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13791 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13793 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13795 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13796 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13798 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13800 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13801 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13803 DrawPlayer(player); /* needed here only to cleanup last field */
13804 RemovePlayer(player);
13806 if (local_player->friends_still_needed == 0 ||
13807 IS_SP_ELEMENT(Feld[jx][jy]))
13808 PlayerWins(player);
13811 /* this breaks one level: "machine", level 000 */
13813 int move_direction = player->MovDir;
13814 int enter_side = MV_DIR_OPPOSITE(move_direction);
13815 int leave_side = move_direction;
13816 int old_jx = last_jx;
13817 int old_jy = last_jy;
13818 int old_element = Feld[old_jx][old_jy];
13819 int new_element = Feld[jx][jy];
13821 if (IS_CUSTOM_ELEMENT(old_element))
13822 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13824 player->index_bit, leave_side);
13826 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13827 CE_PLAYER_LEAVES_X,
13828 player->index_bit, leave_side);
13830 if (IS_CUSTOM_ELEMENT(new_element))
13831 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13832 player->index_bit, enter_side);
13834 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13835 CE_PLAYER_ENTERS_X,
13836 player->index_bit, enter_side);
13838 #if USE_FIX_CE_ACTION_WITH_PLAYER
13839 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13840 CE_MOVE_OF_X, move_direction);
13842 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13843 CE_MOVE_OF_X, move_direction);
13847 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13849 TestIfPlayerTouchesBadThing(jx, jy);
13850 TestIfPlayerTouchesCustomElement(jx, jy);
13852 /* needed because pushed element has not yet reached its destination,
13853 so it would trigger a change event at its previous field location */
13854 if (!player->is_pushing)
13855 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13857 if (!player->active)
13858 RemovePlayer(player);
13861 if (!local_player->LevelSolved && level.use_step_counter)
13871 if (TimeLeft <= 10 && setup.time_limit)
13872 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13875 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13877 DisplayGameControlValues();
13879 DrawGameValue_Time(TimeLeft);
13882 if (!TimeLeft && setup.time_limit)
13883 for (i = 0; i < MAX_PLAYERS; i++)
13884 KillPlayer(&stored_player[i]);
13887 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13889 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13891 DisplayGameControlValues();
13894 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13895 DrawGameValue_Time(TimePlayed);
13899 if (tape.single_step && tape.recording && !tape.pausing &&
13900 !player->programmed_action)
13901 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13905 void ScrollScreen(struct PlayerInfo *player, int mode)
13907 static unsigned int screen_frame_counter = 0;
13909 if (mode == SCROLL_INIT)
13911 /* set scrolling step size according to actual player's moving speed */
13912 ScrollStepSize = TILEX / player->move_delay_value;
13914 screen_frame_counter = FrameCounter;
13915 ScreenMovDir = player->MovDir;
13916 ScreenMovPos = player->MovPos;
13917 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13920 else if (!FrameReached(&screen_frame_counter, 1))
13925 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13926 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13927 redraw_mask |= REDRAW_FIELD;
13930 ScreenMovDir = MV_NONE;
13933 void TestIfPlayerTouchesCustomElement(int x, int y)
13935 static int xy[4][2] =
13942 static int trigger_sides[4][2] =
13944 /* center side border side */
13945 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13946 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13947 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13948 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13950 static int touch_dir[4] =
13952 MV_LEFT | MV_RIGHT,
13957 int center_element = Feld[x][y]; /* should always be non-moving! */
13960 for (i = 0; i < NUM_DIRECTIONS; i++)
13962 int xx = x + xy[i][0];
13963 int yy = y + xy[i][1];
13964 int center_side = trigger_sides[i][0];
13965 int border_side = trigger_sides[i][1];
13966 int border_element;
13968 if (!IN_LEV_FIELD(xx, yy))
13971 if (IS_PLAYER(x, y)) /* player found at center element */
13973 struct PlayerInfo *player = PLAYERINFO(x, y);
13975 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13976 border_element = Feld[xx][yy]; /* may be moving! */
13977 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13978 border_element = Feld[xx][yy];
13979 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13980 border_element = MovingOrBlocked2Element(xx, yy);
13982 continue; /* center and border element do not touch */
13984 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13985 player->index_bit, border_side);
13986 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13987 CE_PLAYER_TOUCHES_X,
13988 player->index_bit, border_side);
13990 #if USE_FIX_CE_ACTION_WITH_PLAYER
13992 /* use player element that is initially defined in the level playfield,
13993 not the player element that corresponds to the runtime player number
13994 (example: a level that contains EL_PLAYER_3 as the only player would
13995 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13996 int player_element = PLAYERINFO(x, y)->initial_element;
13998 CheckElementChangeBySide(xx, yy, border_element, player_element,
13999 CE_TOUCHING_X, border_side);
14003 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14005 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14007 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14009 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14010 continue; /* center and border element do not touch */
14013 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14014 player->index_bit, center_side);
14015 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14016 CE_PLAYER_TOUCHES_X,
14017 player->index_bit, center_side);
14019 #if USE_FIX_CE_ACTION_WITH_PLAYER
14021 /* use player element that is initially defined in the level playfield,
14022 not the player element that corresponds to the runtime player number
14023 (example: a level that contains EL_PLAYER_3 as the only player would
14024 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14025 int player_element = PLAYERINFO(xx, yy)->initial_element;
14027 CheckElementChangeBySide(x, y, center_element, player_element,
14028 CE_TOUCHING_X, center_side);
14037 #if USE_ELEMENT_TOUCHING_BUGFIX
14039 void TestIfElementTouchesCustomElement(int x, int y)
14041 static int xy[4][2] =
14048 static int trigger_sides[4][2] =
14050 /* center side border side */
14051 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14052 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14053 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14054 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14056 static int touch_dir[4] =
14058 MV_LEFT | MV_RIGHT,
14063 boolean change_center_element = FALSE;
14064 int center_element = Feld[x][y]; /* should always be non-moving! */
14065 int border_element_old[NUM_DIRECTIONS];
14068 for (i = 0; i < NUM_DIRECTIONS; i++)
14070 int xx = x + xy[i][0];
14071 int yy = y + xy[i][1];
14072 int border_element;
14074 border_element_old[i] = -1;
14076 if (!IN_LEV_FIELD(xx, yy))
14079 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14080 border_element = Feld[xx][yy]; /* may be moving! */
14081 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14082 border_element = Feld[xx][yy];
14083 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14084 border_element = MovingOrBlocked2Element(xx, yy);
14086 continue; /* center and border element do not touch */
14088 border_element_old[i] = border_element;
14091 for (i = 0; i < NUM_DIRECTIONS; i++)
14093 int xx = x + xy[i][0];
14094 int yy = y + xy[i][1];
14095 int center_side = trigger_sides[i][0];
14096 int border_element = border_element_old[i];
14098 if (border_element == -1)
14101 /* check for change of border element */
14102 CheckElementChangeBySide(xx, yy, border_element, center_element,
14103 CE_TOUCHING_X, center_side);
14105 /* (center element cannot be player, so we dont have to check this here) */
14108 for (i = 0; i < NUM_DIRECTIONS; i++)
14110 int xx = x + xy[i][0];
14111 int yy = y + xy[i][1];
14112 int border_side = trigger_sides[i][1];
14113 int border_element = border_element_old[i];
14115 if (border_element == -1)
14118 /* check for change of center element (but change it only once) */
14119 if (!change_center_element)
14120 change_center_element =
14121 CheckElementChangeBySide(x, y, center_element, border_element,
14122 CE_TOUCHING_X, border_side);
14124 #if USE_FIX_CE_ACTION_WITH_PLAYER
14125 if (IS_PLAYER(xx, yy))
14127 /* use player element that is initially defined in the level playfield,
14128 not the player element that corresponds to the runtime player number
14129 (example: a level that contains EL_PLAYER_3 as the only player would
14130 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14131 int player_element = PLAYERINFO(xx, yy)->initial_element;
14133 CheckElementChangeBySide(x, y, center_element, player_element,
14134 CE_TOUCHING_X, border_side);
14142 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14144 static int xy[4][2] =
14151 static int trigger_sides[4][2] =
14153 /* center side border side */
14154 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14155 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14156 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14157 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14159 static int touch_dir[4] =
14161 MV_LEFT | MV_RIGHT,
14166 boolean change_center_element = FALSE;
14167 int center_element = Feld[x][y]; /* should always be non-moving! */
14170 for (i = 0; i < NUM_DIRECTIONS; i++)
14172 int xx = x + xy[i][0];
14173 int yy = y + xy[i][1];
14174 int center_side = trigger_sides[i][0];
14175 int border_side = trigger_sides[i][1];
14176 int border_element;
14178 if (!IN_LEV_FIELD(xx, yy))
14181 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14182 border_element = Feld[xx][yy]; /* may be moving! */
14183 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14184 border_element = Feld[xx][yy];
14185 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14186 border_element = MovingOrBlocked2Element(xx, yy);
14188 continue; /* center and border element do not touch */
14190 /* check for change of center element (but change it only once) */
14191 if (!change_center_element)
14192 change_center_element =
14193 CheckElementChangeBySide(x, y, center_element, border_element,
14194 CE_TOUCHING_X, border_side);
14196 /* check for change of border element */
14197 CheckElementChangeBySide(xx, yy, border_element, center_element,
14198 CE_TOUCHING_X, center_side);
14204 void TestIfElementHitsCustomElement(int x, int y, int direction)
14206 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14207 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14208 int hitx = x + dx, hity = y + dy;
14209 int hitting_element = Feld[x][y];
14210 int touched_element;
14212 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14215 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14216 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14218 if (IN_LEV_FIELD(hitx, hity))
14220 int opposite_direction = MV_DIR_OPPOSITE(direction);
14221 int hitting_side = direction;
14222 int touched_side = opposite_direction;
14223 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14224 MovDir[hitx][hity] != direction ||
14225 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14231 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14232 CE_HITTING_X, touched_side);
14234 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14235 CE_HIT_BY_X, hitting_side);
14237 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14238 CE_HIT_BY_SOMETHING, opposite_direction);
14240 #if USE_FIX_CE_ACTION_WITH_PLAYER
14241 if (IS_PLAYER(hitx, hity))
14243 /* use player element that is initially defined in the level playfield,
14244 not the player element that corresponds to the runtime player number
14245 (example: a level that contains EL_PLAYER_3 as the only player would
14246 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14247 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14249 CheckElementChangeBySide(x, y, hitting_element, player_element,
14250 CE_HITTING_X, touched_side);
14256 /* "hitting something" is also true when hitting the playfield border */
14257 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14258 CE_HITTING_SOMETHING, direction);
14262 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14264 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14265 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14266 int hitx = x + dx, hity = y + dy;
14267 int hitting_element = Feld[x][y];
14268 int touched_element;
14270 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14271 !IS_FREE(hitx, hity) &&
14272 (!IS_MOVING(hitx, hity) ||
14273 MovDir[hitx][hity] != direction ||
14274 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14277 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14281 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14285 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14286 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14288 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14289 EP_CAN_SMASH_EVERYTHING, direction);
14291 if (IN_LEV_FIELD(hitx, hity))
14293 int opposite_direction = MV_DIR_OPPOSITE(direction);
14294 int hitting_side = direction;
14295 int touched_side = opposite_direction;
14297 int touched_element = MovingOrBlocked2Element(hitx, hity);
14300 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14301 MovDir[hitx][hity] != direction ||
14302 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14311 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14312 CE_SMASHED_BY_SOMETHING, opposite_direction);
14314 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14315 CE_OTHER_IS_SMASHING, touched_side);
14317 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14318 CE_OTHER_GETS_SMASHED, hitting_side);
14324 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14326 int i, kill_x = -1, kill_y = -1;
14328 int bad_element = -1;
14329 static int test_xy[4][2] =
14336 static int test_dir[4] =
14344 for (i = 0; i < NUM_DIRECTIONS; i++)
14346 int test_x, test_y, test_move_dir, test_element;
14348 test_x = good_x + test_xy[i][0];
14349 test_y = good_y + test_xy[i][1];
14351 if (!IN_LEV_FIELD(test_x, test_y))
14355 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14357 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14359 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14360 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14362 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14363 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14367 bad_element = test_element;
14373 if (kill_x != -1 || kill_y != -1)
14375 if (IS_PLAYER(good_x, good_y))
14377 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14379 if (player->shield_deadly_time_left > 0 &&
14380 !IS_INDESTRUCTIBLE(bad_element))
14381 Bang(kill_x, kill_y);
14382 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14383 KillPlayer(player);
14386 Bang(good_x, good_y);
14390 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14392 int i, kill_x = -1, kill_y = -1;
14393 int bad_element = Feld[bad_x][bad_y];
14394 static int test_xy[4][2] =
14401 static int touch_dir[4] =
14403 MV_LEFT | MV_RIGHT,
14408 static int test_dir[4] =
14416 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14419 for (i = 0; i < NUM_DIRECTIONS; i++)
14421 int test_x, test_y, test_move_dir, test_element;
14423 test_x = bad_x + test_xy[i][0];
14424 test_y = bad_y + test_xy[i][1];
14426 if (!IN_LEV_FIELD(test_x, test_y))
14430 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14432 test_element = Feld[test_x][test_y];
14434 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14435 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14437 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14438 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14440 /* good thing is player or penguin that does not move away */
14441 if (IS_PLAYER(test_x, test_y))
14443 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14445 if (bad_element == EL_ROBOT && player->is_moving)
14446 continue; /* robot does not kill player if he is moving */
14448 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14450 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14451 continue; /* center and border element do not touch */
14459 else if (test_element == EL_PENGUIN)
14469 if (kill_x != -1 || kill_y != -1)
14471 if (IS_PLAYER(kill_x, kill_y))
14473 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14475 if (player->shield_deadly_time_left > 0 &&
14476 !IS_INDESTRUCTIBLE(bad_element))
14477 Bang(bad_x, bad_y);
14478 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14479 KillPlayer(player);
14482 Bang(kill_x, kill_y);
14486 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14488 int bad_element = Feld[bad_x][bad_y];
14489 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14490 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14491 int test_x = bad_x + dx, test_y = bad_y + dy;
14492 int test_move_dir, test_element;
14493 int kill_x = -1, kill_y = -1;
14495 if (!IN_LEV_FIELD(test_x, test_y))
14499 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14501 test_element = Feld[test_x][test_y];
14503 if (test_move_dir != bad_move_dir)
14505 /* good thing can be player or penguin that does not move away */
14506 if (IS_PLAYER(test_x, test_y))
14508 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14510 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14511 player as being hit when he is moving towards the bad thing, because
14512 the "get hit by" condition would be lost after the player stops) */
14513 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14514 return; /* player moves away from bad thing */
14519 else if (test_element == EL_PENGUIN)
14526 if (kill_x != -1 || kill_y != -1)
14528 if (IS_PLAYER(kill_x, kill_y))
14530 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14532 if (player->shield_deadly_time_left > 0 &&
14533 !IS_INDESTRUCTIBLE(bad_element))
14534 Bang(bad_x, bad_y);
14535 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14536 KillPlayer(player);
14539 Bang(kill_x, kill_y);
14543 void TestIfPlayerTouchesBadThing(int x, int y)
14545 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14548 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14550 TestIfGoodThingHitsBadThing(x, y, move_dir);
14553 void TestIfBadThingTouchesPlayer(int x, int y)
14555 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14558 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14560 TestIfBadThingHitsGoodThing(x, y, move_dir);
14563 void TestIfFriendTouchesBadThing(int x, int y)
14565 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14568 void TestIfBadThingTouchesFriend(int x, int y)
14570 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14573 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14575 int i, kill_x = bad_x, kill_y = bad_y;
14576 static int xy[4][2] =
14584 for (i = 0; i < NUM_DIRECTIONS; i++)
14588 x = bad_x + xy[i][0];
14589 y = bad_y + xy[i][1];
14590 if (!IN_LEV_FIELD(x, y))
14593 element = Feld[x][y];
14594 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14595 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14603 if (kill_x != bad_x || kill_y != bad_y)
14604 Bang(bad_x, bad_y);
14607 void KillPlayer(struct PlayerInfo *player)
14609 int jx = player->jx, jy = player->jy;
14611 if (!player->active)
14615 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14616 player->killed, player->active, player->reanimated);
14619 /* the following code was introduced to prevent an infinite loop when calling
14621 -> CheckTriggeredElementChangeExt()
14622 -> ExecuteCustomElementAction()
14624 -> (infinitely repeating the above sequence of function calls)
14625 which occurs when killing the player while having a CE with the setting
14626 "kill player X when explosion of <player X>"; the solution using a new
14627 field "player->killed" was chosen for backwards compatibility, although
14628 clever use of the fields "player->active" etc. would probably also work */
14630 if (player->killed)
14634 player->killed = TRUE;
14636 /* remove accessible field at the player's position */
14637 Feld[jx][jy] = EL_EMPTY;
14639 /* deactivate shield (else Bang()/Explode() would not work right) */
14640 player->shield_normal_time_left = 0;
14641 player->shield_deadly_time_left = 0;
14644 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14645 player->killed, player->active, player->reanimated);
14651 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14652 player->killed, player->active, player->reanimated);
14655 #if USE_PLAYER_REANIMATION
14657 if (player->reanimated) /* killed player may have been reanimated */
14658 player->killed = player->reanimated = FALSE;
14660 BuryPlayer(player);
14662 if (player->killed) /* player may have been reanimated */
14663 BuryPlayer(player);
14666 BuryPlayer(player);
14670 static void KillPlayerUnlessEnemyProtected(int x, int y)
14672 if (!PLAYER_ENEMY_PROTECTED(x, y))
14673 KillPlayer(PLAYERINFO(x, y));
14676 static void KillPlayerUnlessExplosionProtected(int x, int y)
14678 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14679 KillPlayer(PLAYERINFO(x, y));
14682 void BuryPlayer(struct PlayerInfo *player)
14684 int jx = player->jx, jy = player->jy;
14686 if (!player->active)
14689 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14690 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14692 player->GameOver = TRUE;
14693 RemovePlayer(player);
14696 void RemovePlayer(struct PlayerInfo *player)
14698 int jx = player->jx, jy = player->jy;
14699 int i, found = FALSE;
14701 player->present = FALSE;
14702 player->active = FALSE;
14704 if (!ExplodeField[jx][jy])
14705 StorePlayer[jx][jy] = 0;
14707 if (player->is_moving)
14708 TEST_DrawLevelField(player->last_jx, player->last_jy);
14710 for (i = 0; i < MAX_PLAYERS; i++)
14711 if (stored_player[i].active)
14715 AllPlayersGone = TRUE;
14721 #if USE_NEW_SNAP_DELAY
14722 static void setFieldForSnapping(int x, int y, int element, int direction)
14724 struct ElementInfo *ei = &element_info[element];
14725 int direction_bit = MV_DIR_TO_BIT(direction);
14726 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14727 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14728 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14730 Feld[x][y] = EL_ELEMENT_SNAPPING;
14731 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14733 ResetGfxAnimation(x, y);
14735 GfxElement[x][y] = element;
14736 GfxAction[x][y] = action;
14737 GfxDir[x][y] = direction;
14738 GfxFrame[x][y] = -1;
14743 =============================================================================
14744 checkDiagonalPushing()
14745 -----------------------------------------------------------------------------
14746 check if diagonal input device direction results in pushing of object
14747 (by checking if the alternative direction is walkable, diggable, ...)
14748 =============================================================================
14751 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14752 int x, int y, int real_dx, int real_dy)
14754 int jx, jy, dx, dy, xx, yy;
14756 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14759 /* diagonal direction: check alternative direction */
14764 xx = jx + (dx == 0 ? real_dx : 0);
14765 yy = jy + (dy == 0 ? real_dy : 0);
14767 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14771 =============================================================================
14773 -----------------------------------------------------------------------------
14774 x, y: field next to player (non-diagonal) to try to dig to
14775 real_dx, real_dy: direction as read from input device (can be diagonal)
14776 =============================================================================
14779 static int DigField(struct PlayerInfo *player,
14780 int oldx, int oldy, int x, int y,
14781 int real_dx, int real_dy, int mode)
14783 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14784 boolean player_was_pushing = player->is_pushing;
14785 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14786 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14787 int jx = oldx, jy = oldy;
14788 int dx = x - jx, dy = y - jy;
14789 int nextx = x + dx, nexty = y + dy;
14790 int move_direction = (dx == -1 ? MV_LEFT :
14791 dx == +1 ? MV_RIGHT :
14793 dy == +1 ? MV_DOWN : MV_NONE);
14794 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14795 int dig_side = MV_DIR_OPPOSITE(move_direction);
14796 int old_element = Feld[jx][jy];
14797 #if USE_FIXED_DONT_RUN_INTO
14798 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14804 if (is_player) /* function can also be called by EL_PENGUIN */
14806 if (player->MovPos == 0)
14808 player->is_digging = FALSE;
14809 player->is_collecting = FALSE;
14812 if (player->MovPos == 0) /* last pushing move finished */
14813 player->is_pushing = FALSE;
14815 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14817 player->is_switching = FALSE;
14818 player->push_delay = -1;
14820 return MP_NO_ACTION;
14824 #if !USE_FIXED_DONT_RUN_INTO
14825 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14826 return MP_NO_ACTION;
14829 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14830 old_element = Back[jx][jy];
14832 /* in case of element dropped at player position, check background */
14833 else if (Back[jx][jy] != EL_EMPTY &&
14834 game.engine_version >= VERSION_IDENT(2,2,0,0))
14835 old_element = Back[jx][jy];
14837 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14838 return MP_NO_ACTION; /* field has no opening in this direction */
14840 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14841 return MP_NO_ACTION; /* field has no opening in this direction */
14843 #if USE_FIXED_DONT_RUN_INTO
14844 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14848 Feld[jx][jy] = player->artwork_element;
14849 InitMovingField(jx, jy, MV_DOWN);
14850 Store[jx][jy] = EL_ACID;
14851 ContinueMoving(jx, jy);
14852 BuryPlayer(player);
14854 return MP_DONT_RUN_INTO;
14858 #if USE_FIXED_DONT_RUN_INTO
14859 if (player_can_move && DONT_RUN_INTO(element))
14861 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14863 return MP_DONT_RUN_INTO;
14867 #if USE_FIXED_DONT_RUN_INTO
14868 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14869 return MP_NO_ACTION;
14872 #if !USE_FIXED_DONT_RUN_INTO
14873 element = Feld[x][y];
14876 collect_count = element_info[element].collect_count_initial;
14878 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14879 return MP_NO_ACTION;
14881 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14882 player_can_move = player_can_move_or_snap;
14884 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14885 game.engine_version >= VERSION_IDENT(2,2,0,0))
14887 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14888 player->index_bit, dig_side);
14889 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14890 player->index_bit, dig_side);
14892 if (element == EL_DC_LANDMINE)
14895 if (Feld[x][y] != element) /* field changed by snapping */
14898 return MP_NO_ACTION;
14901 #if USE_PLAYER_GRAVITY
14902 if (player->gravity && is_player && !player->is_auto_moving &&
14903 canFallDown(player) && move_direction != MV_DOWN &&
14904 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14905 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14907 if (game.gravity && is_player && !player->is_auto_moving &&
14908 canFallDown(player) && move_direction != MV_DOWN &&
14909 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14910 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14913 if (player_can_move &&
14914 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14916 int sound_element = SND_ELEMENT(element);
14917 int sound_action = ACTION_WALKING;
14919 if (IS_RND_GATE(element))
14921 if (!player->key[RND_GATE_NR(element)])
14922 return MP_NO_ACTION;
14924 else if (IS_RND_GATE_GRAY(element))
14926 if (!player->key[RND_GATE_GRAY_NR(element)])
14927 return MP_NO_ACTION;
14929 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14931 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14932 return MP_NO_ACTION;
14934 else if (element == EL_EXIT_OPEN ||
14935 element == EL_EM_EXIT_OPEN ||
14937 element == EL_EM_EXIT_OPENING ||
14939 element == EL_STEEL_EXIT_OPEN ||
14940 element == EL_EM_STEEL_EXIT_OPEN ||
14942 element == EL_EM_STEEL_EXIT_OPENING ||
14944 element == EL_SP_EXIT_OPEN ||
14945 element == EL_SP_EXIT_OPENING)
14947 sound_action = ACTION_PASSING; /* player is passing exit */
14949 else if (element == EL_EMPTY)
14951 sound_action = ACTION_MOVING; /* nothing to walk on */
14954 /* play sound from background or player, whatever is available */
14955 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14956 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14958 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14960 else if (player_can_move &&
14961 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14963 if (!ACCESS_FROM(element, opposite_direction))
14964 return MP_NO_ACTION; /* field not accessible from this direction */
14966 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14967 return MP_NO_ACTION;
14969 if (IS_EM_GATE(element))
14971 if (!player->key[EM_GATE_NR(element)])
14972 return MP_NO_ACTION;
14974 else if (IS_EM_GATE_GRAY(element))
14976 if (!player->key[EM_GATE_GRAY_NR(element)])
14977 return MP_NO_ACTION;
14979 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14981 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14982 return MP_NO_ACTION;
14984 else if (IS_EMC_GATE(element))
14986 if (!player->key[EMC_GATE_NR(element)])
14987 return MP_NO_ACTION;
14989 else if (IS_EMC_GATE_GRAY(element))
14991 if (!player->key[EMC_GATE_GRAY_NR(element)])
14992 return MP_NO_ACTION;
14994 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14996 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14997 return MP_NO_ACTION;
14999 else if (element == EL_DC_GATE_WHITE ||
15000 element == EL_DC_GATE_WHITE_GRAY ||
15001 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15003 if (player->num_white_keys == 0)
15004 return MP_NO_ACTION;
15006 player->num_white_keys--;
15008 else if (IS_SP_PORT(element))
15010 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15011 element == EL_SP_GRAVITY_PORT_RIGHT ||
15012 element == EL_SP_GRAVITY_PORT_UP ||
15013 element == EL_SP_GRAVITY_PORT_DOWN)
15014 #if USE_PLAYER_GRAVITY
15015 player->gravity = !player->gravity;
15017 game.gravity = !game.gravity;
15019 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15020 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15021 element == EL_SP_GRAVITY_ON_PORT_UP ||
15022 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15023 #if USE_PLAYER_GRAVITY
15024 player->gravity = TRUE;
15026 game.gravity = TRUE;
15028 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15029 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15030 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15031 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15032 #if USE_PLAYER_GRAVITY
15033 player->gravity = FALSE;
15035 game.gravity = FALSE;
15039 /* automatically move to the next field with double speed */
15040 player->programmed_action = move_direction;
15042 if (player->move_delay_reset_counter == 0)
15044 player->move_delay_reset_counter = 2; /* two double speed steps */
15046 DOUBLE_PLAYER_SPEED(player);
15049 PlayLevelSoundAction(x, y, ACTION_PASSING);
15051 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15055 if (mode != DF_SNAP)
15057 GfxElement[x][y] = GFX_ELEMENT(element);
15058 player->is_digging = TRUE;
15061 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15063 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15064 player->index_bit, dig_side);
15066 if (mode == DF_SNAP)
15068 #if USE_NEW_SNAP_DELAY
15069 if (level.block_snap_field)
15070 setFieldForSnapping(x, y, element, move_direction);
15072 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15074 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15077 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15078 player->index_bit, dig_side);
15081 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15085 if (is_player && mode != DF_SNAP)
15087 GfxElement[x][y] = element;
15088 player->is_collecting = TRUE;
15091 if (element == EL_SPEED_PILL)
15093 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15095 else if (element == EL_EXTRA_TIME && level.time > 0)
15097 TimeLeft += level.extra_time;
15100 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15102 DisplayGameControlValues();
15104 DrawGameValue_Time(TimeLeft);
15107 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15109 player->shield_normal_time_left += level.shield_normal_time;
15110 if (element == EL_SHIELD_DEADLY)
15111 player->shield_deadly_time_left += level.shield_deadly_time;
15113 else if (element == EL_DYNAMITE ||
15114 element == EL_EM_DYNAMITE ||
15115 element == EL_SP_DISK_RED)
15117 if (player->inventory_size < MAX_INVENTORY_SIZE)
15118 player->inventory_element[player->inventory_size++] = element;
15120 DrawGameDoorValues();
15122 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15124 player->dynabomb_count++;
15125 player->dynabombs_left++;
15127 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15129 player->dynabomb_size++;
15131 else if (element == EL_DYNABOMB_INCREASE_POWER)
15133 player->dynabomb_xl = TRUE;
15135 else if (IS_KEY(element))
15137 player->key[KEY_NR(element)] = TRUE;
15139 DrawGameDoorValues();
15141 else if (element == EL_DC_KEY_WHITE)
15143 player->num_white_keys++;
15145 /* display white keys? */
15146 /* DrawGameDoorValues(); */
15148 else if (IS_ENVELOPE(element))
15150 player->show_envelope = element;
15152 else if (element == EL_EMC_LENSES)
15154 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15156 RedrawAllInvisibleElementsForLenses();
15158 else if (element == EL_EMC_MAGNIFIER)
15160 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15162 RedrawAllInvisibleElementsForMagnifier();
15164 else if (IS_DROPPABLE(element) ||
15165 IS_THROWABLE(element)) /* can be collected and dropped */
15169 if (collect_count == 0)
15170 player->inventory_infinite_element = element;
15172 for (i = 0; i < collect_count; i++)
15173 if (player->inventory_size < MAX_INVENTORY_SIZE)
15174 player->inventory_element[player->inventory_size++] = element;
15176 DrawGameDoorValues();
15178 else if (collect_count > 0)
15180 local_player->gems_still_needed -= collect_count;
15181 if (local_player->gems_still_needed < 0)
15182 local_player->gems_still_needed = 0;
15185 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15187 DisplayGameControlValues();
15189 DrawGameValue_Emeralds(local_player->gems_still_needed);
15193 RaiseScoreElement(element);
15194 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15197 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15198 player->index_bit, dig_side);
15200 if (mode == DF_SNAP)
15202 #if USE_NEW_SNAP_DELAY
15203 if (level.block_snap_field)
15204 setFieldForSnapping(x, y, element, move_direction);
15206 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15208 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15211 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15212 player->index_bit, dig_side);
15215 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15217 if (mode == DF_SNAP && element != EL_BD_ROCK)
15218 return MP_NO_ACTION;
15220 if (CAN_FALL(element) && dy)
15221 return MP_NO_ACTION;
15223 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15224 !(element == EL_SPRING && level.use_spring_bug))
15225 return MP_NO_ACTION;
15227 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15228 ((move_direction & MV_VERTICAL &&
15229 ((element_info[element].move_pattern & MV_LEFT &&
15230 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15231 (element_info[element].move_pattern & MV_RIGHT &&
15232 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15233 (move_direction & MV_HORIZONTAL &&
15234 ((element_info[element].move_pattern & MV_UP &&
15235 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15236 (element_info[element].move_pattern & MV_DOWN &&
15237 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15238 return MP_NO_ACTION;
15240 /* do not push elements already moving away faster than player */
15241 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15242 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15243 return MP_NO_ACTION;
15245 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15247 if (player->push_delay_value == -1 || !player_was_pushing)
15248 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15250 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15252 if (player->push_delay_value == -1)
15253 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15255 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15257 if (!player->is_pushing)
15258 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15261 player->is_pushing = TRUE;
15262 player->is_active = TRUE;
15264 if (!(IN_LEV_FIELD(nextx, nexty) &&
15265 (IS_FREE(nextx, nexty) ||
15266 (IS_SB_ELEMENT(element) &&
15267 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15268 (IS_CUSTOM_ELEMENT(element) &&
15269 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15270 return MP_NO_ACTION;
15272 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15273 return MP_NO_ACTION;
15275 if (player->push_delay == -1) /* new pushing; restart delay */
15276 player->push_delay = 0;
15278 if (player->push_delay < player->push_delay_value &&
15279 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15280 element != EL_SPRING && element != EL_BALLOON)
15282 /* make sure that there is no move delay before next try to push */
15283 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15284 player->move_delay = 0;
15286 return MP_NO_ACTION;
15289 if (IS_CUSTOM_ELEMENT(element) &&
15290 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15292 if (!DigFieldByCE(nextx, nexty, element))
15293 return MP_NO_ACTION;
15296 if (IS_SB_ELEMENT(element))
15298 if (element == EL_SOKOBAN_FIELD_FULL)
15300 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15301 local_player->sokobanfields_still_needed++;
15304 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15306 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15307 local_player->sokobanfields_still_needed--;
15310 Feld[x][y] = EL_SOKOBAN_OBJECT;
15312 if (Back[x][y] == Back[nextx][nexty])
15313 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15314 else if (Back[x][y] != 0)
15315 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15318 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15322 if (local_player->sokobanfields_still_needed == 0 &&
15323 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15325 if (local_player->sokobanfields_still_needed == 0 &&
15326 game.emulation == EMU_SOKOBAN)
15329 PlayerWins(player);
15331 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15335 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15337 InitMovingField(x, y, move_direction);
15338 GfxAction[x][y] = ACTION_PUSHING;
15340 if (mode == DF_SNAP)
15341 ContinueMoving(x, y);
15343 MovPos[x][y] = (dx != 0 ? dx : dy);
15345 Pushed[x][y] = TRUE;
15346 Pushed[nextx][nexty] = TRUE;
15348 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15349 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15351 player->push_delay_value = -1; /* get new value later */
15353 /* check for element change _after_ element has been pushed */
15354 if (game.use_change_when_pushing_bug)
15356 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15357 player->index_bit, dig_side);
15358 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15359 player->index_bit, dig_side);
15362 else if (IS_SWITCHABLE(element))
15364 if (PLAYER_SWITCHING(player, x, y))
15366 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367 player->index_bit, dig_side);
15372 player->is_switching = TRUE;
15373 player->switch_x = x;
15374 player->switch_y = y;
15376 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15378 if (element == EL_ROBOT_WHEEL)
15380 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15384 game.robot_wheel_active = TRUE;
15386 TEST_DrawLevelField(x, y);
15388 else if (element == EL_SP_TERMINAL)
15392 SCAN_PLAYFIELD(xx, yy)
15394 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15396 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15397 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15400 else if (IS_BELT_SWITCH(element))
15402 ToggleBeltSwitch(x, y);
15404 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15405 element == EL_SWITCHGATE_SWITCH_DOWN ||
15406 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15407 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15409 ToggleSwitchgateSwitch(x, y);
15411 else if (element == EL_LIGHT_SWITCH ||
15412 element == EL_LIGHT_SWITCH_ACTIVE)
15414 ToggleLightSwitch(x, y);
15416 else if (element == EL_TIMEGATE_SWITCH ||
15417 element == EL_DC_TIMEGATE_SWITCH)
15419 ActivateTimegateSwitch(x, y);
15421 else if (element == EL_BALLOON_SWITCH_LEFT ||
15422 element == EL_BALLOON_SWITCH_RIGHT ||
15423 element == EL_BALLOON_SWITCH_UP ||
15424 element == EL_BALLOON_SWITCH_DOWN ||
15425 element == EL_BALLOON_SWITCH_NONE ||
15426 element == EL_BALLOON_SWITCH_ANY)
15428 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15429 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15430 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15431 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15432 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15435 else if (element == EL_LAMP)
15437 Feld[x][y] = EL_LAMP_ACTIVE;
15438 local_player->lights_still_needed--;
15440 ResetGfxAnimation(x, y);
15441 TEST_DrawLevelField(x, y);
15443 else if (element == EL_TIME_ORB_FULL)
15445 Feld[x][y] = EL_TIME_ORB_EMPTY;
15447 if (level.time > 0 || level.use_time_orb_bug)
15449 TimeLeft += level.time_orb_time;
15450 game.no_time_limit = FALSE;
15453 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15455 DisplayGameControlValues();
15457 DrawGameValue_Time(TimeLeft);
15461 ResetGfxAnimation(x, y);
15462 TEST_DrawLevelField(x, y);
15464 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15465 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15469 game.ball_state = !game.ball_state;
15471 SCAN_PLAYFIELD(xx, yy)
15473 int e = Feld[xx][yy];
15475 if (game.ball_state)
15477 if (e == EL_EMC_MAGIC_BALL)
15478 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15479 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15480 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15484 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15485 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15486 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15487 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15492 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15493 player->index_bit, dig_side);
15495 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15496 player->index_bit, dig_side);
15498 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15499 player->index_bit, dig_side);
15505 if (!PLAYER_SWITCHING(player, x, y))
15507 player->is_switching = TRUE;
15508 player->switch_x = x;
15509 player->switch_y = y;
15511 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15512 player->index_bit, dig_side);
15513 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15514 player->index_bit, dig_side);
15516 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15517 player->index_bit, dig_side);
15518 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15519 player->index_bit, dig_side);
15522 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15523 player->index_bit, dig_side);
15524 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15525 player->index_bit, dig_side);
15527 return MP_NO_ACTION;
15530 player->push_delay = -1;
15532 if (is_player) /* function can also be called by EL_PENGUIN */
15534 if (Feld[x][y] != element) /* really digged/collected something */
15536 player->is_collecting = !player->is_digging;
15537 player->is_active = TRUE;
15544 static boolean DigFieldByCE(int x, int y, int digging_element)
15546 int element = Feld[x][y];
15548 if (!IS_FREE(x, y))
15550 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15551 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15554 /* no element can dig solid indestructible elements */
15555 if (IS_INDESTRUCTIBLE(element) &&
15556 !IS_DIGGABLE(element) &&
15557 !IS_COLLECTIBLE(element))
15560 if (AmoebaNr[x][y] &&
15561 (element == EL_AMOEBA_FULL ||
15562 element == EL_BD_AMOEBA ||
15563 element == EL_AMOEBA_GROWING))
15565 AmoebaCnt[AmoebaNr[x][y]]--;
15566 AmoebaCnt2[AmoebaNr[x][y]]--;
15569 if (IS_MOVING(x, y))
15570 RemoveMovingField(x, y);
15574 TEST_DrawLevelField(x, y);
15577 /* if digged element was about to explode, prevent the explosion */
15578 ExplodeField[x][y] = EX_TYPE_NONE;
15580 PlayLevelSoundAction(x, y, action);
15583 Store[x][y] = EL_EMPTY;
15586 /* this makes it possible to leave the removed element again */
15587 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15588 Store[x][y] = element;
15590 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15592 int move_leave_element = element_info[digging_element].move_leave_element;
15594 /* this makes it possible to leave the removed element again */
15595 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15596 element : move_leave_element);
15603 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15605 int jx = player->jx, jy = player->jy;
15606 int x = jx + dx, y = jy + dy;
15607 int snap_direction = (dx == -1 ? MV_LEFT :
15608 dx == +1 ? MV_RIGHT :
15610 dy == +1 ? MV_DOWN : MV_NONE);
15611 boolean can_continue_snapping = (level.continuous_snapping &&
15612 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15614 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15617 if (!player->active || !IN_LEV_FIELD(x, y))
15625 if (player->MovPos == 0)
15626 player->is_pushing = FALSE;
15628 player->is_snapping = FALSE;
15630 if (player->MovPos == 0)
15632 player->is_moving = FALSE;
15633 player->is_digging = FALSE;
15634 player->is_collecting = FALSE;
15640 #if USE_NEW_CONTINUOUS_SNAPPING
15641 /* prevent snapping with already pressed snap key when not allowed */
15642 if (player->is_snapping && !can_continue_snapping)
15645 if (player->is_snapping)
15649 player->MovDir = snap_direction;
15651 if (player->MovPos == 0)
15653 player->is_moving = FALSE;
15654 player->is_digging = FALSE;
15655 player->is_collecting = FALSE;
15658 player->is_dropping = FALSE;
15659 player->is_dropping_pressed = FALSE;
15660 player->drop_pressed_delay = 0;
15662 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15665 player->is_snapping = TRUE;
15666 player->is_active = TRUE;
15668 if (player->MovPos == 0)
15670 player->is_moving = FALSE;
15671 player->is_digging = FALSE;
15672 player->is_collecting = FALSE;
15675 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15676 TEST_DrawLevelField(player->last_jx, player->last_jy);
15678 TEST_DrawLevelField(x, y);
15683 static boolean DropElement(struct PlayerInfo *player)
15685 int old_element, new_element;
15686 int dropx = player->jx, dropy = player->jy;
15687 int drop_direction = player->MovDir;
15688 int drop_side = drop_direction;
15690 int drop_element = get_next_dropped_element(player);
15692 int drop_element = (player->inventory_size > 0 ?
15693 player->inventory_element[player->inventory_size - 1] :
15694 player->inventory_infinite_element != EL_UNDEFINED ?
15695 player->inventory_infinite_element :
15696 player->dynabombs_left > 0 ?
15697 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15701 player->is_dropping_pressed = TRUE;
15703 /* do not drop an element on top of another element; when holding drop key
15704 pressed without moving, dropped element must move away before the next
15705 element can be dropped (this is especially important if the next element
15706 is dynamite, which can be placed on background for historical reasons) */
15707 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15710 if (IS_THROWABLE(drop_element))
15712 dropx += GET_DX_FROM_DIR(drop_direction);
15713 dropy += GET_DY_FROM_DIR(drop_direction);
15715 if (!IN_LEV_FIELD(dropx, dropy))
15719 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15720 new_element = drop_element; /* default: no change when dropping */
15722 /* check if player is active, not moving and ready to drop */
15723 if (!player->active || player->MovPos || player->drop_delay > 0)
15726 /* check if player has anything that can be dropped */
15727 if (new_element == EL_UNDEFINED)
15730 /* check if drop key was pressed long enough for EM style dynamite */
15731 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15734 /* check if anything can be dropped at the current position */
15735 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15738 /* collected custom elements can only be dropped on empty fields */
15739 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15742 if (old_element != EL_EMPTY)
15743 Back[dropx][dropy] = old_element; /* store old element on this field */
15745 ResetGfxAnimation(dropx, dropy);
15746 ResetRandomAnimationValue(dropx, dropy);
15748 if (player->inventory_size > 0 ||
15749 player->inventory_infinite_element != EL_UNDEFINED)
15751 if (player->inventory_size > 0)
15753 player->inventory_size--;
15755 DrawGameDoorValues();
15757 if (new_element == EL_DYNAMITE)
15758 new_element = EL_DYNAMITE_ACTIVE;
15759 else if (new_element == EL_EM_DYNAMITE)
15760 new_element = EL_EM_DYNAMITE_ACTIVE;
15761 else if (new_element == EL_SP_DISK_RED)
15762 new_element = EL_SP_DISK_RED_ACTIVE;
15765 Feld[dropx][dropy] = new_element;
15767 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15768 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15769 el2img(Feld[dropx][dropy]), 0);
15771 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15773 /* needed if previous element just changed to "empty" in the last frame */
15774 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15776 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15777 player->index_bit, drop_side);
15778 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15780 player->index_bit, drop_side);
15782 TestIfElementTouchesCustomElement(dropx, dropy);
15784 else /* player is dropping a dyna bomb */
15786 player->dynabombs_left--;
15788 Feld[dropx][dropy] = new_element;
15790 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15791 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15792 el2img(Feld[dropx][dropy]), 0);
15794 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15797 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15798 InitField_WithBug1(dropx, dropy, FALSE);
15800 new_element = Feld[dropx][dropy]; /* element might have changed */
15802 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15803 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15806 int move_direction;
15810 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15811 MovDir[dropx][dropy] = drop_direction;
15814 move_direction = MovDir[dropx][dropy];
15815 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15816 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15819 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15821 #if USE_FIX_IMPACT_COLLISION
15822 /* do not cause impact style collision by dropping elements that can fall */
15823 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15825 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15829 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15830 player->is_dropping = TRUE;
15832 player->drop_pressed_delay = 0;
15833 player->is_dropping_pressed = FALSE;
15835 player->drop_x = dropx;
15836 player->drop_y = dropy;
15841 /* ------------------------------------------------------------------------- */
15842 /* game sound playing functions */
15843 /* ------------------------------------------------------------------------- */
15845 static int *loop_sound_frame = NULL;
15846 static int *loop_sound_volume = NULL;
15848 void InitPlayLevelSound()
15850 int num_sounds = getSoundListSize();
15852 checked_free(loop_sound_frame);
15853 checked_free(loop_sound_volume);
15855 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15856 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15859 static void PlayLevelSound(int x, int y, int nr)
15861 int sx = SCREENX(x), sy = SCREENY(y);
15862 int volume, stereo_position;
15863 int max_distance = 8;
15864 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15866 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15867 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15870 if (!IN_LEV_FIELD(x, y) ||
15871 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15872 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15875 volume = SOUND_MAX_VOLUME;
15877 if (!IN_SCR_FIELD(sx, sy))
15879 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15880 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15882 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15885 stereo_position = (SOUND_MAX_LEFT +
15886 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15887 (SCR_FIELDX + 2 * max_distance));
15889 if (IS_LOOP_SOUND(nr))
15891 /* This assures that quieter loop sounds do not overwrite louder ones,
15892 while restarting sound volume comparison with each new game frame. */
15894 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15897 loop_sound_volume[nr] = volume;
15898 loop_sound_frame[nr] = FrameCounter;
15901 PlaySoundExt(nr, volume, stereo_position, type);
15904 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15906 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15907 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15908 y < LEVELY(BY1) ? LEVELY(BY1) :
15909 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15913 static void PlayLevelSoundAction(int x, int y, int action)
15915 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15918 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15920 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15922 if (sound_effect != SND_UNDEFINED)
15923 PlayLevelSound(x, y, sound_effect);
15926 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15929 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15931 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15932 PlayLevelSound(x, y, sound_effect);
15935 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15937 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15939 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15940 PlayLevelSound(x, y, sound_effect);
15943 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15945 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15947 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15948 StopSound(sound_effect);
15951 static void PlayLevelMusic()
15953 if (levelset.music[level_nr] != MUS_UNDEFINED)
15954 PlayMusic(levelset.music[level_nr]); /* from config file */
15956 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15959 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15961 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15962 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15963 int x = xx - 1 - offset;
15964 int y = yy - 1 - offset;
15969 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15973 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15977 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15981 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15985 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15989 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15993 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15996 case SAMPLE_android_clone:
15997 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16000 case SAMPLE_android_move:
16001 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16004 case SAMPLE_spring:
16005 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16009 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16013 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16016 case SAMPLE_eater_eat:
16017 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16021 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16024 case SAMPLE_collect:
16025 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16028 case SAMPLE_diamond:
16029 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16032 case SAMPLE_squash:
16033 /* !!! CHECK THIS !!! */
16035 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16037 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16041 case SAMPLE_wonderfall:
16042 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16046 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16050 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16054 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16058 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16062 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16066 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16069 case SAMPLE_wonder:
16070 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16074 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16077 case SAMPLE_exit_open:
16078 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16081 case SAMPLE_exit_leave:
16082 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16085 case SAMPLE_dynamite:
16086 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16090 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16094 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16098 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16102 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16106 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16110 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16114 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16119 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16121 int element = map_element_SP_to_RND(element_sp);
16122 int action = map_action_SP_to_RND(action_sp);
16123 int offset = (setup.sp_show_border_elements ? 0 : 1);
16124 int x = xx - offset;
16125 int y = yy - offset;
16128 printf("::: %d -> %d\n", element_sp, action_sp);
16131 PlayLevelSoundElementAction(x, y, element, action);
16134 void RaiseScore(int value)
16136 local_player->score += value;
16139 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16141 DisplayGameControlValues();
16143 DrawGameValue_Score(local_player->score);
16147 void RaiseScoreElement(int element)
16152 case EL_BD_DIAMOND:
16153 case EL_EMERALD_YELLOW:
16154 case EL_EMERALD_RED:
16155 case EL_EMERALD_PURPLE:
16156 case EL_SP_INFOTRON:
16157 RaiseScore(level.score[SC_EMERALD]);
16160 RaiseScore(level.score[SC_DIAMOND]);
16163 RaiseScore(level.score[SC_CRYSTAL]);
16166 RaiseScore(level.score[SC_PEARL]);
16169 case EL_BD_BUTTERFLY:
16170 case EL_SP_ELECTRON:
16171 RaiseScore(level.score[SC_BUG]);
16174 case EL_BD_FIREFLY:
16175 case EL_SP_SNIKSNAK:
16176 RaiseScore(level.score[SC_SPACESHIP]);
16179 case EL_DARK_YAMYAM:
16180 RaiseScore(level.score[SC_YAMYAM]);
16183 RaiseScore(level.score[SC_ROBOT]);
16186 RaiseScore(level.score[SC_PACMAN]);
16189 RaiseScore(level.score[SC_NUT]);
16192 case EL_EM_DYNAMITE:
16193 case EL_SP_DISK_RED:
16194 case EL_DYNABOMB_INCREASE_NUMBER:
16195 case EL_DYNABOMB_INCREASE_SIZE:
16196 case EL_DYNABOMB_INCREASE_POWER:
16197 RaiseScore(level.score[SC_DYNAMITE]);
16199 case EL_SHIELD_NORMAL:
16200 case EL_SHIELD_DEADLY:
16201 RaiseScore(level.score[SC_SHIELD]);
16203 case EL_EXTRA_TIME:
16204 RaiseScore(level.extra_time_score);
16218 case EL_DC_KEY_WHITE:
16219 RaiseScore(level.score[SC_KEY]);
16222 RaiseScore(element_info[element].collect_score);
16227 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16229 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16231 #if defined(NETWORK_AVALIABLE)
16232 if (options.network)
16233 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16242 FadeSkipNextFadeIn();
16244 fading = fading_none;
16248 OpenDoor(DOOR_CLOSE_1);
16251 game_status = GAME_MODE_MAIN;
16254 DrawAndFadeInMainMenu(REDRAW_FIELD);
16262 FadeOut(REDRAW_FIELD);
16265 game_status = GAME_MODE_MAIN;
16267 DrawAndFadeInMainMenu(REDRAW_FIELD);
16271 else /* continue playing the game */
16273 if (tape.playing && tape.deactivate_display)
16274 TapeDeactivateDisplayOff(TRUE);
16276 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16278 if (tape.playing && tape.deactivate_display)
16279 TapeDeactivateDisplayOn();
16283 void RequestQuitGame(boolean ask_if_really_quit)
16285 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16286 boolean skip_request = AllPlayersGone || quick_quit;
16288 RequestQuitGameExt(skip_request, quick_quit,
16289 "Do you really want to quit the game ?");
16293 /* ------------------------------------------------------------------------- */
16294 /* random generator functions */
16295 /* ------------------------------------------------------------------------- */
16297 unsigned int InitEngineRandom_RND(int seed)
16299 game.num_random_calls = 0;
16302 unsigned int rnd_seed = InitEngineRandom(seed);
16304 printf("::: START RND: %d\n", rnd_seed);
16309 return InitEngineRandom(seed);
16315 unsigned int RND(int max)
16319 game.num_random_calls++;
16321 return GetEngineRandom(max);
16328 /* ------------------------------------------------------------------------- */
16329 /* game engine snapshot handling functions */
16330 /* ------------------------------------------------------------------------- */
16332 struct EngineSnapshotInfo
16334 /* runtime values for custom element collect score */
16335 int collect_score[NUM_CUSTOM_ELEMENTS];
16337 /* runtime values for group element choice position */
16338 int choice_pos[NUM_GROUP_ELEMENTS];
16340 /* runtime values for belt position animations */
16341 int belt_graphic[4][NUM_BELT_PARTS];
16342 int belt_anim_mode[4][NUM_BELT_PARTS];
16345 static struct EngineSnapshotInfo engine_snapshot_rnd;
16346 static char *snapshot_level_identifier = NULL;
16347 static int snapshot_level_nr = -1;
16349 static void SaveEngineSnapshotValues_RND()
16351 static int belt_base_active_element[4] =
16353 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16354 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16355 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16356 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16360 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16362 int element = EL_CUSTOM_START + i;
16364 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16367 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16369 int element = EL_GROUP_START + i;
16371 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16374 for (i = 0; i < 4; i++)
16376 for (j = 0; j < NUM_BELT_PARTS; j++)
16378 int element = belt_base_active_element[i] + j;
16379 int graphic = el2img(element);
16380 int anim_mode = graphic_info[graphic].anim_mode;
16382 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16383 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16388 static void LoadEngineSnapshotValues_RND()
16390 unsigned int num_random_calls = game.num_random_calls;
16393 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16395 int element = EL_CUSTOM_START + i;
16397 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16400 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16402 int element = EL_GROUP_START + i;
16404 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16407 for (i = 0; i < 4; i++)
16409 for (j = 0; j < NUM_BELT_PARTS; j++)
16411 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16412 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16414 graphic_info[graphic].anim_mode = anim_mode;
16418 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16420 InitRND(tape.random_seed);
16421 for (i = 0; i < num_random_calls; i++)
16425 if (game.num_random_calls != num_random_calls)
16427 Error(ERR_INFO, "number of random calls out of sync");
16428 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16429 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16430 Error(ERR_EXIT, "this should not happen -- please debug");
16434 void SaveEngineSnapshot()
16436 /* do not save snapshots from editor */
16437 if (level_editor_test_game)
16440 /* free previous snapshot buffers, if needed */
16441 FreeEngineSnapshotBuffers();
16443 /* copy some special values to a structure better suited for the snapshot */
16445 SaveEngineSnapshotValues_RND();
16446 SaveEngineSnapshotValues_EM();
16447 SaveEngineSnapshotValues_SP();
16449 /* save values stored in special snapshot structure */
16451 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16452 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16453 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16455 /* save further RND engine values */
16457 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16458 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16459 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16461 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16462 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16463 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16464 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16466 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16467 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16468 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16469 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16470 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16472 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16473 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16474 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16476 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16478 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16480 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16481 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16483 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16484 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16485 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16486 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16487 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16488 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16489 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16490 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16491 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16492 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16493 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16494 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16495 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16496 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16497 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16498 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16499 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16500 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16502 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16503 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16505 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16506 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16507 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16509 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16513 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16514 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16516 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16518 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16519 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16521 /* save level identification information */
16523 setString(&snapshot_level_identifier, leveldir_current->identifier);
16524 snapshot_level_nr = level_nr;
16527 ListNode *node = engine_snapshot_list_rnd;
16530 while (node != NULL)
16532 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16537 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16541 void LoadEngineSnapshot()
16543 /* restore generically stored snapshot buffers */
16545 LoadEngineSnapshotBuffers();
16547 /* restore special values from snapshot structure */
16549 LoadEngineSnapshotValues_RND();
16550 LoadEngineSnapshotValues_EM();
16551 LoadEngineSnapshotValues_SP();
16554 boolean CheckEngineSnapshot()
16556 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16557 snapshot_level_nr == level_nr);
16561 /* ---------- new game button stuff ---------------------------------------- */
16569 } gamebutton_info[NUM_GAME_BUTTONS] =
16572 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16573 GAME_CTRL_ID_STOP, "stop game"
16576 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16577 GAME_CTRL_ID_PAUSE, "pause game"
16580 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16581 GAME_CTRL_ID_PLAY, "play game"
16584 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16585 SOUND_CTRL_ID_MUSIC, "background music on/off"
16588 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16589 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16592 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16593 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16597 void CreateGameButtons()
16601 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16603 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16604 struct Rect *pos = gamebutton_info[i].pos;
16605 struct GadgetInfo *gi;
16608 unsigned int event_mask;
16609 int gd_x = gfx->src_x;
16610 int gd_y = gfx->src_y;
16611 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16612 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16613 int gd_xa = gfx->src_x + gfx->active_xoffset;
16614 int gd_ya = gfx->src_y + gfx->active_yoffset;
16615 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16616 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16619 if (id == GAME_CTRL_ID_STOP ||
16620 id == GAME_CTRL_ID_PAUSE ||
16621 id == GAME_CTRL_ID_PLAY)
16623 button_type = GD_TYPE_NORMAL_BUTTON;
16625 event_mask = GD_EVENT_RELEASED;
16629 button_type = GD_TYPE_CHECK_BUTTON;
16631 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16632 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16633 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16634 event_mask = GD_EVENT_PRESSED;
16637 gi = CreateGadget(GDI_CUSTOM_ID, id,
16638 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16639 GDI_X, DX + pos->x,
16640 GDI_Y, DY + pos->y,
16641 GDI_WIDTH, gfx->width,
16642 GDI_HEIGHT, gfx->height,
16643 GDI_TYPE, button_type,
16644 GDI_STATE, GD_BUTTON_UNPRESSED,
16645 GDI_CHECKED, checked,
16646 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16647 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16648 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16649 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16650 GDI_DIRECT_DRAW, FALSE,
16651 GDI_EVENT_MASK, event_mask,
16652 GDI_CALLBACK_ACTION, HandleGameButtons,
16656 Error(ERR_EXIT, "cannot create gadget");
16658 game_gadget[id] = gi;
16662 void FreeGameButtons()
16666 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16667 FreeGadget(game_gadget[i]);
16670 static void MapGameButtons()
16674 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16675 MapGadget(game_gadget[i]);
16678 void UnmapGameButtons()
16682 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16683 UnmapGadget(game_gadget[i]);
16686 void RedrawGameButtons()
16690 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16691 RedrawGadget(game_gadget[i]);
16694 static void HandleGameButtonsExt(int id)
16696 if (game_status != GAME_MODE_PLAYING)
16701 case GAME_CTRL_ID_STOP:
16705 RequestQuitGame(TRUE);
16708 case GAME_CTRL_ID_PAUSE:
16709 if (options.network)
16711 #if defined(NETWORK_AVALIABLE)
16713 SendToServer_ContinuePlaying();
16715 SendToServer_PausePlaying();
16719 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16722 case GAME_CTRL_ID_PLAY:
16725 #if defined(NETWORK_AVALIABLE)
16726 if (options.network)
16727 SendToServer_ContinuePlaying();
16731 tape.pausing = FALSE;
16732 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16737 case SOUND_CTRL_ID_MUSIC:
16738 if (setup.sound_music)
16740 setup.sound_music = FALSE;
16744 else if (audio.music_available)
16746 setup.sound = setup.sound_music = TRUE;
16748 SetAudioMode(setup.sound);
16754 case SOUND_CTRL_ID_LOOPS:
16755 if (setup.sound_loops)
16756 setup.sound_loops = FALSE;
16757 else if (audio.loops_available)
16759 setup.sound = setup.sound_loops = TRUE;
16761 SetAudioMode(setup.sound);
16765 case SOUND_CTRL_ID_SIMPLE:
16766 if (setup.sound_simple)
16767 setup.sound_simple = FALSE;
16768 else if (audio.sound_available)
16770 setup.sound = setup.sound_simple = TRUE;
16772 SetAudioMode(setup.sound);
16781 static void HandleGameButtons(struct GadgetInfo *gi)
16783 HandleGameButtonsExt(gi->custom_id);
16786 void HandleSoundButtonKeys(Key key)
16789 if (key == setup.shortcut.sound_simple)
16790 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16791 else if (key == setup.shortcut.sound_loops)
16792 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16793 else if (key == setup.shortcut.sound_music)
16794 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16796 if (key == setup.shortcut.sound_simple)
16797 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16798 else if (key == setup.shortcut.sound_loops)
16799 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16800 else if (key == setup.shortcut.sound_music)
16801 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);