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"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_STUFF ( 1)
35 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
36 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
37 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
38 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
39 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
40 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
41 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
42 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
43 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
44 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
45 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
46 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
47 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
48 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
49 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
50 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
51 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
52 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
53 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
55 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
57 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
59 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
61 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
64 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
65 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
66 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
67 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
69 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
71 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
73 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
75 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
77 #if USE_DELAYED_GFX_REDRAW
78 #define TEST_DrawLevelField(x, y) \
79 GfxRedraw[x][y] |= GFX_REDRAW_TILE
80 #define TEST_DrawLevelFieldCrumbled(x, y) \
81 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
82 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
83 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
84 #define TEST_DrawTwinkleOnField(x, y) \
85 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
87 #define TEST_DrawLevelField(x, y) \
89 #define TEST_DrawLevelFieldCrumbled(x, y) \
90 DrawLevelFieldCrumbled(x, y)
91 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
92 DrawLevelFieldCrumbledNeighbours(x, y)
93 #define TEST_DrawTwinkleOnField(x, y) \
94 DrawTwinkleOnField(x, y)
103 /* for MovePlayer() */
104 #define MP_NO_ACTION 0
107 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
109 /* for ScrollPlayer() */
110 #define SCROLL_INIT 0
111 #define SCROLL_GO_ON 1
113 /* for Bang()/Explode() */
114 #define EX_PHASE_START 0
115 #define EX_TYPE_NONE 0
116 #define EX_TYPE_NORMAL (1 << 0)
117 #define EX_TYPE_CENTER (1 << 1)
118 #define EX_TYPE_BORDER (1 << 2)
119 #define EX_TYPE_CROSS (1 << 3)
120 #define EX_TYPE_DYNA (1 << 4)
121 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
123 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
124 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
125 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
126 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
128 /* game panel display and control definitions */
129 #define GAME_PANEL_LEVEL_NUMBER 0
130 #define GAME_PANEL_GEMS 1
131 #define GAME_PANEL_INVENTORY_COUNT 2
132 #define GAME_PANEL_INVENTORY_FIRST_1 3
133 #define GAME_PANEL_INVENTORY_FIRST_2 4
134 #define GAME_PANEL_INVENTORY_FIRST_3 5
135 #define GAME_PANEL_INVENTORY_FIRST_4 6
136 #define GAME_PANEL_INVENTORY_FIRST_5 7
137 #define GAME_PANEL_INVENTORY_FIRST_6 8
138 #define GAME_PANEL_INVENTORY_FIRST_7 9
139 #define GAME_PANEL_INVENTORY_FIRST_8 10
140 #define GAME_PANEL_INVENTORY_LAST_1 11
141 #define GAME_PANEL_INVENTORY_LAST_2 12
142 #define GAME_PANEL_INVENTORY_LAST_3 13
143 #define GAME_PANEL_INVENTORY_LAST_4 14
144 #define GAME_PANEL_INVENTORY_LAST_5 15
145 #define GAME_PANEL_INVENTORY_LAST_6 16
146 #define GAME_PANEL_INVENTORY_LAST_7 17
147 #define GAME_PANEL_INVENTORY_LAST_8 18
148 #define GAME_PANEL_KEY_1 19
149 #define GAME_PANEL_KEY_2 20
150 #define GAME_PANEL_KEY_3 21
151 #define GAME_PANEL_KEY_4 22
152 #define GAME_PANEL_KEY_5 23
153 #define GAME_PANEL_KEY_6 24
154 #define GAME_PANEL_KEY_7 25
155 #define GAME_PANEL_KEY_8 26
156 #define GAME_PANEL_KEY_WHITE 27
157 #define GAME_PANEL_KEY_WHITE_COUNT 28
158 #define GAME_PANEL_SCORE 29
159 #define GAME_PANEL_HIGHSCORE 30
160 #define GAME_PANEL_TIME 31
161 #define GAME_PANEL_TIME_HH 32
162 #define GAME_PANEL_TIME_MM 33
163 #define GAME_PANEL_TIME_SS 34
164 #define GAME_PANEL_FRAME 35
165 #define GAME_PANEL_SHIELD_NORMAL 36
166 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
167 #define GAME_PANEL_SHIELD_DEADLY 38
168 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
169 #define GAME_PANEL_EXIT 40
170 #define GAME_PANEL_EMC_MAGIC_BALL 41
171 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
172 #define GAME_PANEL_LIGHT_SWITCH 43
173 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
174 #define GAME_PANEL_TIMEGATE_SWITCH 45
175 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
176 #define GAME_PANEL_SWITCHGATE_SWITCH 47
177 #define GAME_PANEL_EMC_LENSES 48
178 #define GAME_PANEL_EMC_LENSES_TIME 49
179 #define GAME_PANEL_EMC_MAGNIFIER 50
180 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
181 #define GAME_PANEL_BALLOON_SWITCH 52
182 #define GAME_PANEL_DYNABOMB_NUMBER 53
183 #define GAME_PANEL_DYNABOMB_SIZE 54
184 #define GAME_PANEL_DYNABOMB_POWER 55
185 #define GAME_PANEL_PENGUINS 56
186 #define GAME_PANEL_SOKOBAN_OBJECTS 57
187 #define GAME_PANEL_SOKOBAN_FIELDS 58
188 #define GAME_PANEL_ROBOT_WHEEL 59
189 #define GAME_PANEL_CONVEYOR_BELT_1 60
190 #define GAME_PANEL_CONVEYOR_BELT_2 61
191 #define GAME_PANEL_CONVEYOR_BELT_3 62
192 #define GAME_PANEL_CONVEYOR_BELT_4 63
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
194 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
195 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
196 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
197 #define GAME_PANEL_MAGIC_WALL 68
198 #define GAME_PANEL_MAGIC_WALL_TIME 69
199 #define GAME_PANEL_GRAVITY_STATE 70
200 #define GAME_PANEL_GRAPHIC_1 71
201 #define GAME_PANEL_GRAPHIC_2 72
202 #define GAME_PANEL_GRAPHIC_3 73
203 #define GAME_PANEL_GRAPHIC_4 74
204 #define GAME_PANEL_GRAPHIC_5 75
205 #define GAME_PANEL_GRAPHIC_6 76
206 #define GAME_PANEL_GRAPHIC_7 77
207 #define GAME_PANEL_GRAPHIC_8 78
208 #define GAME_PANEL_ELEMENT_1 79
209 #define GAME_PANEL_ELEMENT_2 80
210 #define GAME_PANEL_ELEMENT_3 81
211 #define GAME_PANEL_ELEMENT_4 82
212 #define GAME_PANEL_ELEMENT_5 83
213 #define GAME_PANEL_ELEMENT_6 84
214 #define GAME_PANEL_ELEMENT_7 85
215 #define GAME_PANEL_ELEMENT_8 86
216 #define GAME_PANEL_ELEMENT_COUNT_1 87
217 #define GAME_PANEL_ELEMENT_COUNT_2 88
218 #define GAME_PANEL_ELEMENT_COUNT_3 89
219 #define GAME_PANEL_ELEMENT_COUNT_4 90
220 #define GAME_PANEL_ELEMENT_COUNT_5 91
221 #define GAME_PANEL_ELEMENT_COUNT_6 92
222 #define GAME_PANEL_ELEMENT_COUNT_7 93
223 #define GAME_PANEL_ELEMENT_COUNT_8 94
224 #define GAME_PANEL_CE_SCORE_1 95
225 #define GAME_PANEL_CE_SCORE_2 96
226 #define GAME_PANEL_CE_SCORE_3 97
227 #define GAME_PANEL_CE_SCORE_4 98
228 #define GAME_PANEL_CE_SCORE_5 99
229 #define GAME_PANEL_CE_SCORE_6 100
230 #define GAME_PANEL_CE_SCORE_7 101
231 #define GAME_PANEL_CE_SCORE_8 102
232 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
233 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
234 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
235 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
236 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
237 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
238 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
239 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
240 #define GAME_PANEL_PLAYER_NAME 111
241 #define GAME_PANEL_LEVEL_NAME 112
242 #define GAME_PANEL_LEVEL_AUTHOR 113
244 #define NUM_GAME_PANEL_CONTROLS 114
246 struct GamePanelOrderInfo
252 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
254 struct GamePanelControlInfo
258 struct TextPosInfo *pos;
261 int value, last_value;
262 int frame, last_frame;
267 static struct GamePanelControlInfo game_panel_controls[] =
270 GAME_PANEL_LEVEL_NUMBER,
271 &game.panel.level_number,
280 GAME_PANEL_INVENTORY_COUNT,
281 &game.panel.inventory_count,
285 GAME_PANEL_INVENTORY_FIRST_1,
286 &game.panel.inventory_first[0],
290 GAME_PANEL_INVENTORY_FIRST_2,
291 &game.panel.inventory_first[1],
295 GAME_PANEL_INVENTORY_FIRST_3,
296 &game.panel.inventory_first[2],
300 GAME_PANEL_INVENTORY_FIRST_4,
301 &game.panel.inventory_first[3],
305 GAME_PANEL_INVENTORY_FIRST_5,
306 &game.panel.inventory_first[4],
310 GAME_PANEL_INVENTORY_FIRST_6,
311 &game.panel.inventory_first[5],
315 GAME_PANEL_INVENTORY_FIRST_7,
316 &game.panel.inventory_first[6],
320 GAME_PANEL_INVENTORY_FIRST_8,
321 &game.panel.inventory_first[7],
325 GAME_PANEL_INVENTORY_LAST_1,
326 &game.panel.inventory_last[0],
330 GAME_PANEL_INVENTORY_LAST_2,
331 &game.panel.inventory_last[1],
335 GAME_PANEL_INVENTORY_LAST_3,
336 &game.panel.inventory_last[2],
340 GAME_PANEL_INVENTORY_LAST_4,
341 &game.panel.inventory_last[3],
345 GAME_PANEL_INVENTORY_LAST_5,
346 &game.panel.inventory_last[4],
350 GAME_PANEL_INVENTORY_LAST_6,
351 &game.panel.inventory_last[5],
355 GAME_PANEL_INVENTORY_LAST_7,
356 &game.panel.inventory_last[6],
360 GAME_PANEL_INVENTORY_LAST_8,
361 &game.panel.inventory_last[7],
405 GAME_PANEL_KEY_WHITE,
406 &game.panel.key_white,
410 GAME_PANEL_KEY_WHITE_COUNT,
411 &game.panel.key_white_count,
420 GAME_PANEL_HIGHSCORE,
421 &game.panel.highscore,
450 GAME_PANEL_SHIELD_NORMAL,
451 &game.panel.shield_normal,
455 GAME_PANEL_SHIELD_NORMAL_TIME,
456 &game.panel.shield_normal_time,
460 GAME_PANEL_SHIELD_DEADLY,
461 &game.panel.shield_deadly,
465 GAME_PANEL_SHIELD_DEADLY_TIME,
466 &game.panel.shield_deadly_time,
475 GAME_PANEL_EMC_MAGIC_BALL,
476 &game.panel.emc_magic_ball,
480 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
481 &game.panel.emc_magic_ball_switch,
485 GAME_PANEL_LIGHT_SWITCH,
486 &game.panel.light_switch,
490 GAME_PANEL_LIGHT_SWITCH_TIME,
491 &game.panel.light_switch_time,
495 GAME_PANEL_TIMEGATE_SWITCH,
496 &game.panel.timegate_switch,
500 GAME_PANEL_TIMEGATE_SWITCH_TIME,
501 &game.panel.timegate_switch_time,
505 GAME_PANEL_SWITCHGATE_SWITCH,
506 &game.panel.switchgate_switch,
510 GAME_PANEL_EMC_LENSES,
511 &game.panel.emc_lenses,
515 GAME_PANEL_EMC_LENSES_TIME,
516 &game.panel.emc_lenses_time,
520 GAME_PANEL_EMC_MAGNIFIER,
521 &game.panel.emc_magnifier,
525 GAME_PANEL_EMC_MAGNIFIER_TIME,
526 &game.panel.emc_magnifier_time,
530 GAME_PANEL_BALLOON_SWITCH,
531 &game.panel.balloon_switch,
535 GAME_PANEL_DYNABOMB_NUMBER,
536 &game.panel.dynabomb_number,
540 GAME_PANEL_DYNABOMB_SIZE,
541 &game.panel.dynabomb_size,
545 GAME_PANEL_DYNABOMB_POWER,
546 &game.panel.dynabomb_power,
551 &game.panel.penguins,
555 GAME_PANEL_SOKOBAN_OBJECTS,
556 &game.panel.sokoban_objects,
560 GAME_PANEL_SOKOBAN_FIELDS,
561 &game.panel.sokoban_fields,
565 GAME_PANEL_ROBOT_WHEEL,
566 &game.panel.robot_wheel,
570 GAME_PANEL_CONVEYOR_BELT_1,
571 &game.panel.conveyor_belt[0],
575 GAME_PANEL_CONVEYOR_BELT_2,
576 &game.panel.conveyor_belt[1],
580 GAME_PANEL_CONVEYOR_BELT_3,
581 &game.panel.conveyor_belt[2],
585 GAME_PANEL_CONVEYOR_BELT_4,
586 &game.panel.conveyor_belt[3],
590 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
591 &game.panel.conveyor_belt_switch[0],
595 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
596 &game.panel.conveyor_belt_switch[1],
600 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
601 &game.panel.conveyor_belt_switch[2],
605 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
606 &game.panel.conveyor_belt_switch[3],
610 GAME_PANEL_MAGIC_WALL,
611 &game.panel.magic_wall,
615 GAME_PANEL_MAGIC_WALL_TIME,
616 &game.panel.magic_wall_time,
620 GAME_PANEL_GRAVITY_STATE,
621 &game.panel.gravity_state,
625 GAME_PANEL_GRAPHIC_1,
626 &game.panel.graphic[0],
630 GAME_PANEL_GRAPHIC_2,
631 &game.panel.graphic[1],
635 GAME_PANEL_GRAPHIC_3,
636 &game.panel.graphic[2],
640 GAME_PANEL_GRAPHIC_4,
641 &game.panel.graphic[3],
645 GAME_PANEL_GRAPHIC_5,
646 &game.panel.graphic[4],
650 GAME_PANEL_GRAPHIC_6,
651 &game.panel.graphic[5],
655 GAME_PANEL_GRAPHIC_7,
656 &game.panel.graphic[6],
660 GAME_PANEL_GRAPHIC_8,
661 &game.panel.graphic[7],
665 GAME_PANEL_ELEMENT_1,
666 &game.panel.element[0],
670 GAME_PANEL_ELEMENT_2,
671 &game.panel.element[1],
675 GAME_PANEL_ELEMENT_3,
676 &game.panel.element[2],
680 GAME_PANEL_ELEMENT_4,
681 &game.panel.element[3],
685 GAME_PANEL_ELEMENT_5,
686 &game.panel.element[4],
690 GAME_PANEL_ELEMENT_6,
691 &game.panel.element[5],
695 GAME_PANEL_ELEMENT_7,
696 &game.panel.element[6],
700 GAME_PANEL_ELEMENT_8,
701 &game.panel.element[7],
705 GAME_PANEL_ELEMENT_COUNT_1,
706 &game.panel.element_count[0],
710 GAME_PANEL_ELEMENT_COUNT_2,
711 &game.panel.element_count[1],
715 GAME_PANEL_ELEMENT_COUNT_3,
716 &game.panel.element_count[2],
720 GAME_PANEL_ELEMENT_COUNT_4,
721 &game.panel.element_count[3],
725 GAME_PANEL_ELEMENT_COUNT_5,
726 &game.panel.element_count[4],
730 GAME_PANEL_ELEMENT_COUNT_6,
731 &game.panel.element_count[5],
735 GAME_PANEL_ELEMENT_COUNT_7,
736 &game.panel.element_count[6],
740 GAME_PANEL_ELEMENT_COUNT_8,
741 &game.panel.element_count[7],
745 GAME_PANEL_CE_SCORE_1,
746 &game.panel.ce_score[0],
750 GAME_PANEL_CE_SCORE_2,
751 &game.panel.ce_score[1],
755 GAME_PANEL_CE_SCORE_3,
756 &game.panel.ce_score[2],
760 GAME_PANEL_CE_SCORE_4,
761 &game.panel.ce_score[3],
765 GAME_PANEL_CE_SCORE_5,
766 &game.panel.ce_score[4],
770 GAME_PANEL_CE_SCORE_6,
771 &game.panel.ce_score[5],
775 GAME_PANEL_CE_SCORE_7,
776 &game.panel.ce_score[6],
780 GAME_PANEL_CE_SCORE_8,
781 &game.panel.ce_score[7],
785 GAME_PANEL_CE_SCORE_1_ELEMENT,
786 &game.panel.ce_score_element[0],
790 GAME_PANEL_CE_SCORE_2_ELEMENT,
791 &game.panel.ce_score_element[1],
795 GAME_PANEL_CE_SCORE_3_ELEMENT,
796 &game.panel.ce_score_element[2],
800 GAME_PANEL_CE_SCORE_4_ELEMENT,
801 &game.panel.ce_score_element[3],
805 GAME_PANEL_CE_SCORE_5_ELEMENT,
806 &game.panel.ce_score_element[4],
810 GAME_PANEL_CE_SCORE_6_ELEMENT,
811 &game.panel.ce_score_element[5],
815 GAME_PANEL_CE_SCORE_7_ELEMENT,
816 &game.panel.ce_score_element[6],
820 GAME_PANEL_CE_SCORE_8_ELEMENT,
821 &game.panel.ce_score_element[7],
825 GAME_PANEL_PLAYER_NAME,
826 &game.panel.player_name,
830 GAME_PANEL_LEVEL_NAME,
831 &game.panel.level_name,
835 GAME_PANEL_LEVEL_AUTHOR,
836 &game.panel.level_author,
847 /* values for delayed check of falling and moving elements and for collision */
848 #define CHECK_DELAY_MOVING 3
849 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
850 #define CHECK_DELAY_COLLISION 2
851 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
853 /* values for initial player move delay (initial delay counter value) */
854 #define INITIAL_MOVE_DELAY_OFF -1
855 #define INITIAL_MOVE_DELAY_ON 0
857 /* values for player movement speed (which is in fact a delay value) */
858 #define MOVE_DELAY_MIN_SPEED 32
859 #define MOVE_DELAY_NORMAL_SPEED 8
860 #define MOVE_DELAY_HIGH_SPEED 4
861 #define MOVE_DELAY_MAX_SPEED 1
863 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
864 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
866 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
867 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
869 /* values for other actions */
870 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
871 #define MOVE_STEPSIZE_MIN (1)
872 #define MOVE_STEPSIZE_MAX (TILEX)
874 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
875 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
877 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
879 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
880 RND(element_info[e].push_delay_random))
881 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
882 RND(element_info[e].drop_delay_random))
883 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
884 RND(element_info[e].move_delay_random))
885 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
886 (element_info[e].move_delay_random))
887 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
888 RND(element_info[e].ce_value_random_initial))
889 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
890 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
891 RND((c)->delay_random * (c)->delay_frames))
892 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
893 RND((c)->delay_random))
896 #define GET_VALID_RUNTIME_ELEMENT(e) \
897 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
899 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
900 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
901 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
902 (be) + (e) - EL_SELF)
904 #define GET_PLAYER_FROM_BITS(p) \
905 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
907 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
908 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
909 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
910 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
911 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
912 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
913 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
914 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
915 RESOLVED_REFERENCE_ELEMENT(be, e) : \
918 #define CAN_GROW_INTO(e) \
919 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
921 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
922 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
925 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
926 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
927 (CAN_MOVE_INTO_ACID(e) && \
928 Feld[x][y] == EL_ACID) || \
931 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
932 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
933 (CAN_MOVE_INTO_ACID(e) && \
934 Feld[x][y] == EL_ACID) || \
937 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
938 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
940 (CAN_MOVE_INTO_ACID(e) && \
941 Feld[x][y] == EL_ACID) || \
942 (DONT_COLLIDE_WITH(e) && \
944 !PLAYER_ENEMY_PROTECTED(x, y))))
946 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
947 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
949 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
952 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
955 #define ANDROID_CAN_CLONE_FIELD(x, y) \
956 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
957 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
959 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
960 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
962 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
963 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
965 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
966 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
968 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
971 #define PIG_CAN_ENTER_FIELD(e, x, y) \
972 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
974 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
975 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
976 Feld[x][y] == EL_EM_EXIT_OPEN || \
977 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
978 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
979 IS_FOOD_PENGUIN(Feld[x][y])))
980 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
981 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
984 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
986 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
987 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
990 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
991 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
993 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
995 #define CE_ENTER_FIELD_COND(e, x, y) \
996 (!IS_PLAYER(x, y) && \
997 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
999 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1000 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1002 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1003 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1005 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1006 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1007 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1008 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1010 /* game button identifiers */
1011 #define GAME_CTRL_ID_STOP 0
1012 #define GAME_CTRL_ID_PAUSE 1
1013 #define GAME_CTRL_ID_PLAY 2
1014 #define SOUND_CTRL_ID_MUSIC 3
1015 #define SOUND_CTRL_ID_LOOPS 4
1016 #define SOUND_CTRL_ID_SIMPLE 5
1017 #define GAME_CTRL_ID_SAVE 6
1018 #define GAME_CTRL_ID_LOAD 7
1020 #define NUM_GAME_BUTTONS 8
1023 /* forward declaration for internal use */
1025 static void CreateField(int, int, int);
1027 static void ResetGfxAnimation(int, int);
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1053 static void TestIfElementSmashesCustomElement(int, int, int);
1056 static void HandleElementChange(int, int, int);
1057 static void ExecuteCustomElementAction(int, int, int, int);
1058 static boolean ChangeElement(int, int, int, int);
1060 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1061 #define CheckTriggeredElementChange(x, y, e, ev) \
1062 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1063 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1064 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1065 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1066 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1067 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1068 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1070 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1071 #define CheckElementChange(x, y, e, te, ev) \
1072 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1073 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1074 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1075 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1076 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1078 static void PlayLevelSound(int, int, int);
1079 static void PlayLevelSoundNearest(int, int, int);
1080 static void PlayLevelSoundAction(int, int, int);
1081 static void PlayLevelSoundElementAction(int, int, int, int);
1082 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1083 static void PlayLevelSoundActionIfLoop(int, int, int);
1084 static void StopLevelSoundActionIfLoop(int, int, int);
1085 static void PlayLevelMusic();
1087 static void HandleGameButtons(struct GadgetInfo *);
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1106 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1108 void KillPlayer(struct PlayerInfo *);
1109 void BuryPlayer(struct PlayerInfo *);
1110 void RemovePlayer(struct PlayerInfo *);
1112 static int getInvisibleActiveFromInvisibleElement(int);
1113 static int getInvisibleFromInvisibleActiveElement(int);
1115 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1117 /* for detection of endless loops, caused by custom element programming */
1118 /* (using maximal playfield width x 10 is just a rough approximation) */
1119 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1121 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1123 if (recursion_loop_detected) \
1126 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1128 recursion_loop_detected = TRUE; \
1129 recursion_loop_element = (e); \
1132 recursion_loop_depth++; \
1135 #define RECURSION_LOOP_DETECTION_END() \
1137 recursion_loop_depth--; \
1140 static int recursion_loop_depth;
1141 static boolean recursion_loop_detected;
1142 static boolean recursion_loop_element;
1144 static int map_player_action[MAX_PLAYERS];
1147 /* ------------------------------------------------------------------------- */
1148 /* definition of elements that automatically change to other elements after */
1149 /* a specified time, eventually calling a function when changing */
1150 /* ------------------------------------------------------------------------- */
1152 /* forward declaration for changer functions */
1153 static void InitBuggyBase(int, int);
1154 static void WarnBuggyBase(int, int);
1156 static void InitTrap(int, int);
1157 static void ActivateTrap(int, int);
1158 static void ChangeActiveTrap(int, int);
1160 static void InitRobotWheel(int, int);
1161 static void RunRobotWheel(int, int);
1162 static void StopRobotWheel(int, int);
1164 static void InitTimegateWheel(int, int);
1165 static void RunTimegateWheel(int, int);
1167 static void InitMagicBallDelay(int, int);
1168 static void ActivateMagicBall(int, int);
1170 struct ChangingElementInfo
1175 void (*pre_change_function)(int x, int y);
1176 void (*change_function)(int x, int y);
1177 void (*post_change_function)(int x, int y);
1180 static struct ChangingElementInfo change_delay_list[] =
1215 EL_STEEL_EXIT_OPENING,
1223 EL_STEEL_EXIT_CLOSING,
1224 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1263 EL_EM_STEEL_EXIT_CLOSED,
1287 EL_SWITCHGATE_OPENING,
1295 EL_SWITCHGATE_CLOSING,
1296 EL_SWITCHGATE_CLOSED,
1303 EL_TIMEGATE_OPENING,
1311 EL_TIMEGATE_CLOSING,
1320 EL_ACID_SPLASH_LEFT,
1328 EL_ACID_SPLASH_RIGHT,
1337 EL_SP_BUGGY_BASE_ACTIVATING,
1344 EL_SP_BUGGY_BASE_ACTIVATING,
1345 EL_SP_BUGGY_BASE_ACTIVE,
1352 EL_SP_BUGGY_BASE_ACTIVE,
1376 EL_ROBOT_WHEEL_ACTIVE,
1384 EL_TIMEGATE_SWITCH_ACTIVE,
1392 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1393 EL_DC_TIMEGATE_SWITCH,
1400 EL_EMC_MAGIC_BALL_ACTIVE,
1401 EL_EMC_MAGIC_BALL_ACTIVE,
1408 EL_EMC_SPRING_BUMPER_ACTIVE,
1409 EL_EMC_SPRING_BUMPER,
1416 EL_DIAGONAL_SHRINKING,
1424 EL_DIAGONAL_GROWING,
1445 int push_delay_fixed, push_delay_random;
1449 { EL_SPRING, 0, 0 },
1450 { EL_BALLOON, 0, 0 },
1452 { EL_SOKOBAN_OBJECT, 2, 0 },
1453 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1454 { EL_SATELLITE, 2, 0 },
1455 { EL_SP_DISK_YELLOW, 2, 0 },
1457 { EL_UNDEFINED, 0, 0 },
1465 move_stepsize_list[] =
1467 { EL_AMOEBA_DROP, 2 },
1468 { EL_AMOEBA_DROPPING, 2 },
1469 { EL_QUICKSAND_FILLING, 1 },
1470 { EL_QUICKSAND_EMPTYING, 1 },
1471 { EL_QUICKSAND_FAST_FILLING, 2 },
1472 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1473 { EL_MAGIC_WALL_FILLING, 2 },
1474 { EL_MAGIC_WALL_EMPTYING, 2 },
1475 { EL_BD_MAGIC_WALL_FILLING, 2 },
1476 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1477 { EL_DC_MAGIC_WALL_FILLING, 2 },
1478 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1480 { EL_UNDEFINED, 0 },
1488 collect_count_list[] =
1491 { EL_BD_DIAMOND, 1 },
1492 { EL_EMERALD_YELLOW, 1 },
1493 { EL_EMERALD_RED, 1 },
1494 { EL_EMERALD_PURPLE, 1 },
1496 { EL_SP_INFOTRON, 1 },
1500 { EL_UNDEFINED, 0 },
1508 access_direction_list[] =
1510 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1512 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1513 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1514 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1515 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1516 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1517 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1518 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1519 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1520 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1522 { EL_SP_PORT_LEFT, MV_RIGHT },
1523 { EL_SP_PORT_RIGHT, MV_LEFT },
1524 { EL_SP_PORT_UP, MV_DOWN },
1525 { EL_SP_PORT_DOWN, MV_UP },
1526 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1527 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1528 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1529 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1537 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1538 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1539 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1540 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1542 { EL_UNDEFINED, MV_NONE }
1545 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1548 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1549 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1550 IS_JUST_CHANGING(x, y))
1552 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1554 /* static variables for playfield scan mode (scanning forward or backward) */
1555 static int playfield_scan_start_x = 0;
1556 static int playfield_scan_start_y = 0;
1557 static int playfield_scan_delta_x = 1;
1558 static int playfield_scan_delta_y = 1;
1560 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1561 (y) >= 0 && (y) <= lev_fieldy - 1; \
1562 (y) += playfield_scan_delta_y) \
1563 for ((x) = playfield_scan_start_x; \
1564 (x) >= 0 && (x) <= lev_fieldx - 1; \
1565 (x) += playfield_scan_delta_x)
1568 void DEBUG_SetMaximumDynamite()
1572 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1573 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1574 local_player->inventory_element[local_player->inventory_size++] =
1579 static void InitPlayfieldScanModeVars()
1581 if (game.use_reverse_scan_direction)
1583 playfield_scan_start_x = lev_fieldx - 1;
1584 playfield_scan_start_y = lev_fieldy - 1;
1586 playfield_scan_delta_x = -1;
1587 playfield_scan_delta_y = -1;
1591 playfield_scan_start_x = 0;
1592 playfield_scan_start_y = 0;
1594 playfield_scan_delta_x = 1;
1595 playfield_scan_delta_y = 1;
1599 static void InitPlayfieldScanMode(int mode)
1601 game.use_reverse_scan_direction =
1602 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604 InitPlayfieldScanModeVars();
1607 static int get_move_delay_from_stepsize(int move_stepsize)
1610 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612 /* make sure that stepsize value is always a power of 2 */
1613 move_stepsize = (1 << log_2(move_stepsize));
1615 return TILEX / move_stepsize;
1618 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1621 int player_nr = player->index_nr;
1622 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1623 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625 /* do no immediately change move delay -- the player might just be moving */
1626 player->move_delay_value_next = move_delay;
1628 /* information if player can move must be set separately */
1629 player->cannot_move = cannot_move;
1633 player->move_delay = game.initial_move_delay[player_nr];
1634 player->move_delay_value = game.initial_move_delay_value[player_nr];
1636 player->move_delay_value_next = -1;
1638 player->move_delay_reset_counter = 0;
1642 void GetPlayerConfig()
1644 GameFrameDelay = setup.game_frame_delay;
1646 if (!audio.sound_available)
1647 setup.sound_simple = FALSE;
1649 if (!audio.loops_available)
1650 setup.sound_loops = FALSE;
1652 if (!audio.music_available)
1653 setup.sound_music = FALSE;
1655 if (!video.fullscreen_available)
1656 setup.fullscreen = FALSE;
1658 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660 SetAudioMode(setup.sound);
1664 int GetElementFromGroupElement(int element)
1666 if (IS_GROUP_ELEMENT(element))
1668 struct ElementGroupInfo *group = element_info[element].group;
1669 int last_anim_random_frame = gfx.anim_random_frame;
1672 if (group->choice_mode == ANIM_RANDOM)
1673 gfx.anim_random_frame = RND(group->num_elements_resolved);
1675 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676 group->choice_mode, 0,
1679 if (group->choice_mode == ANIM_RANDOM)
1680 gfx.anim_random_frame = last_anim_random_frame;
1682 group->choice_pos++;
1684 element = group->element_resolved[element_pos];
1690 static void InitPlayerField(int x, int y, int element, boolean init_game)
1692 if (element == EL_SP_MURPHY)
1696 if (stored_player[0].present)
1698 Feld[x][y] = EL_SP_MURPHY_CLONE;
1704 stored_player[0].initial_element = element;
1705 stored_player[0].use_murphy = TRUE;
1707 if (!level.use_artwork_element[0])
1708 stored_player[0].artwork_element = EL_SP_MURPHY;
1711 Feld[x][y] = EL_PLAYER_1;
1717 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1718 int jx = player->jx, jy = player->jy;
1720 player->present = TRUE;
1722 player->block_last_field = (element == EL_SP_MURPHY ?
1723 level.sp_block_last_field :
1724 level.block_last_field);
1726 /* ---------- initialize player's last field block delay --------------- */
1728 /* always start with reliable default value (no adjustment needed) */
1729 player->block_delay_adjustment = 0;
1731 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1732 if (player->block_last_field && element == EL_SP_MURPHY)
1733 player->block_delay_adjustment = 1;
1735 /* special case 2: in game engines before 3.1.1, blocking was different */
1736 if (game.use_block_last_field_bug)
1737 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1739 if (!options.network || player->connected)
1741 player->active = TRUE;
1743 /* remove potentially duplicate players */
1744 if (StorePlayer[jx][jy] == Feld[x][y])
1745 StorePlayer[jx][jy] = 0;
1747 StorePlayer[x][y] = Feld[x][y];
1749 #if DEBUG_INIT_PLAYER
1752 printf("- player element %d activated", player->element_nr);
1753 printf(" (local player is %d and currently %s)\n",
1754 local_player->element_nr,
1755 local_player->active ? "active" : "not active");
1760 Feld[x][y] = EL_EMPTY;
1762 player->jx = player->last_jx = x;
1763 player->jy = player->last_jy = y;
1766 #if USE_PLAYER_REANIMATION
1769 int player_nr = GET_PLAYER_NR(element);
1770 struct PlayerInfo *player = &stored_player[player_nr];
1772 if (player->active && player->killed)
1773 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1778 static void InitField(int x, int y, boolean init_game)
1780 int element = Feld[x][y];
1789 InitPlayerField(x, y, element, init_game);
1792 case EL_SOKOBAN_FIELD_PLAYER:
1793 element = Feld[x][y] = EL_PLAYER_1;
1794 InitField(x, y, init_game);
1796 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1797 InitField(x, y, init_game);
1800 case EL_SOKOBAN_FIELD_EMPTY:
1801 local_player->sokobanfields_still_needed++;
1805 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1806 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1807 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1808 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1809 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1810 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1811 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1812 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1813 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1814 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1823 case EL_SPACESHIP_RIGHT:
1824 case EL_SPACESHIP_UP:
1825 case EL_SPACESHIP_LEFT:
1826 case EL_SPACESHIP_DOWN:
1827 case EL_BD_BUTTERFLY:
1828 case EL_BD_BUTTERFLY_RIGHT:
1829 case EL_BD_BUTTERFLY_UP:
1830 case EL_BD_BUTTERFLY_LEFT:
1831 case EL_BD_BUTTERFLY_DOWN:
1833 case EL_BD_FIREFLY_RIGHT:
1834 case EL_BD_FIREFLY_UP:
1835 case EL_BD_FIREFLY_LEFT:
1836 case EL_BD_FIREFLY_DOWN:
1837 case EL_PACMAN_RIGHT:
1839 case EL_PACMAN_LEFT:
1840 case EL_PACMAN_DOWN:
1842 case EL_YAMYAM_LEFT:
1843 case EL_YAMYAM_RIGHT:
1845 case EL_YAMYAM_DOWN:
1846 case EL_DARK_YAMYAM:
1849 case EL_SP_SNIKSNAK:
1850 case EL_SP_ELECTRON:
1859 case EL_AMOEBA_FULL:
1864 case EL_AMOEBA_DROP:
1865 if (y == lev_fieldy - 1)
1867 Feld[x][y] = EL_AMOEBA_GROWING;
1868 Store[x][y] = EL_AMOEBA_WET;
1872 case EL_DYNAMITE_ACTIVE:
1873 case EL_SP_DISK_RED_ACTIVE:
1874 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1875 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1876 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1877 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1878 MovDelay[x][y] = 96;
1881 case EL_EM_DYNAMITE_ACTIVE:
1882 MovDelay[x][y] = 32;
1886 local_player->lights_still_needed++;
1890 local_player->friends_still_needed++;
1895 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1898 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1899 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1900 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1901 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1902 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1903 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1904 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1905 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1906 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1907 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1908 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1909 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1912 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1913 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1914 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1916 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1918 game.belt_dir[belt_nr] = belt_dir;
1919 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1921 else /* more than one switch -- set it like the first switch */
1923 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1928 #if !USE_BOTH_SWITCHGATE_SWITCHES
1929 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1931 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1934 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1936 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1940 case EL_LIGHT_SWITCH_ACTIVE:
1942 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1945 case EL_INVISIBLE_STEELWALL:
1946 case EL_INVISIBLE_WALL:
1947 case EL_INVISIBLE_SAND:
1948 if (game.light_time_left > 0 ||
1949 game.lenses_time_left > 0)
1950 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1953 case EL_EMC_MAGIC_BALL:
1954 if (game.ball_state)
1955 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1958 case EL_EMC_MAGIC_BALL_SWITCH:
1959 if (game.ball_state)
1960 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1963 case EL_TRIGGER_PLAYER:
1964 case EL_TRIGGER_ELEMENT:
1965 case EL_TRIGGER_CE_VALUE:
1966 case EL_TRIGGER_CE_SCORE:
1968 case EL_ANY_ELEMENT:
1969 case EL_CURRENT_CE_VALUE:
1970 case EL_CURRENT_CE_SCORE:
1987 /* reference elements should not be used on the playfield */
1988 Feld[x][y] = EL_EMPTY;
1992 if (IS_CUSTOM_ELEMENT(element))
1994 if (CAN_MOVE(element))
1997 #if USE_NEW_CUSTOM_VALUE
1998 if (!element_info[element].use_last_ce_value || init_game)
1999 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2002 else if (IS_GROUP_ELEMENT(element))
2004 Feld[x][y] = GetElementFromGroupElement(element);
2006 InitField(x, y, init_game);
2013 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2016 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2018 InitField(x, y, init_game);
2020 /* not needed to call InitMovDir() -- already done by InitField()! */
2021 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2022 CAN_MOVE(Feld[x][y]))
2026 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2028 int old_element = Feld[x][y];
2030 InitField(x, y, init_game);
2032 /* not needed to call InitMovDir() -- already done by InitField()! */
2033 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034 CAN_MOVE(old_element) &&
2035 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2038 /* this case is in fact a combination of not less than three bugs:
2039 first, it calls InitMovDir() for elements that can move, although this is
2040 already done by InitField(); then, it checks the element that was at this
2041 field _before_ the call to InitField() (which can change it); lastly, it
2042 was not called for "mole with direction" elements, which were treated as
2043 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2047 static int get_key_element_from_nr(int key_nr)
2049 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2050 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2051 EL_EM_KEY_1 : EL_KEY_1);
2053 return key_base_element + key_nr;
2056 static int get_next_dropped_element(struct PlayerInfo *player)
2058 return (player->inventory_size > 0 ?
2059 player->inventory_element[player->inventory_size - 1] :
2060 player->inventory_infinite_element != EL_UNDEFINED ?
2061 player->inventory_infinite_element :
2062 player->dynabombs_left > 0 ?
2063 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2067 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2069 /* pos >= 0: get element from bottom of the stack;
2070 pos < 0: get element from top of the stack */
2074 int min_inventory_size = -pos;
2075 int inventory_pos = player->inventory_size - min_inventory_size;
2076 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2078 return (player->inventory_size >= min_inventory_size ?
2079 player->inventory_element[inventory_pos] :
2080 player->inventory_infinite_element != EL_UNDEFINED ?
2081 player->inventory_infinite_element :
2082 player->dynabombs_left >= min_dynabombs_left ?
2083 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 int min_dynabombs_left = pos + 1;
2089 int min_inventory_size = pos + 1 - player->dynabombs_left;
2090 int inventory_pos = pos - player->dynabombs_left;
2092 return (player->inventory_infinite_element != EL_UNDEFINED ?
2093 player->inventory_infinite_element :
2094 player->dynabombs_left >= min_dynabombs_left ?
2095 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096 player->inventory_size >= min_inventory_size ?
2097 player->inventory_element[inventory_pos] :
2102 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2104 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2105 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2108 if (gpo1->sort_priority != gpo2->sort_priority)
2109 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2111 compare_result = gpo1->nr - gpo2->nr;
2113 return compare_result;
2116 void InitGameControlValues()
2120 for (i = 0; game_panel_controls[i].nr != -1; i++)
2122 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2123 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2124 struct TextPosInfo *pos = gpc->pos;
2126 int type = gpc->type;
2130 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2131 Error(ERR_EXIT, "this should not happen -- please debug");
2134 /* force update of game controls after initialization */
2135 gpc->value = gpc->last_value = -1;
2136 gpc->frame = gpc->last_frame = -1;
2137 gpc->gfx_frame = -1;
2139 /* determine panel value width for later calculation of alignment */
2140 if (type == TYPE_INTEGER || type == TYPE_STRING)
2142 pos->width = pos->size * getFontWidth(pos->font);
2143 pos->height = getFontHeight(pos->font);
2145 else if (type == TYPE_ELEMENT)
2147 pos->width = pos->size;
2148 pos->height = pos->size;
2151 /* fill structure for game panel draw order */
2153 gpo->sort_priority = pos->sort_priority;
2156 /* sort game panel controls according to sort_priority and control number */
2157 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2158 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2161 void UpdatePlayfieldElementCount()
2163 boolean use_element_count = FALSE;
2166 /* first check if it is needed at all to calculate playfield element count */
2167 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2168 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2169 use_element_count = TRUE;
2171 if (!use_element_count)
2174 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2175 element_info[i].element_count = 0;
2177 SCAN_PLAYFIELD(x, y)
2179 element_info[Feld[x][y]].element_count++;
2182 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2183 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2184 if (IS_IN_GROUP(j, i))
2185 element_info[EL_GROUP_START + i].element_count +=
2186 element_info[j].element_count;
2189 void UpdateGameControlValues()
2192 int time = (local_player->LevelSolved ?
2193 local_player->LevelSolved_CountingTime :
2194 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2195 level.native_em_level->lev->time :
2196 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2197 level.native_sp_level->game_sp->time_played :
2198 game.no_time_limit ? TimePlayed : TimeLeft);
2199 int score = (local_player->LevelSolved ?
2200 local_player->LevelSolved_CountingScore :
2201 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202 level.native_em_level->lev->score :
2203 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2204 level.native_sp_level->game_sp->score :
2205 local_player->score);
2206 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed :
2210 local_player->gems_still_needed);
2211 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212 level.native_em_level->lev->required > 0 :
2213 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215 local_player->gems_still_needed > 0 ||
2216 local_player->sokobanfields_still_needed > 0 ||
2217 local_player->lights_still_needed > 0);
2219 UpdatePlayfieldElementCount();
2221 /* update game panel control values */
2223 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2224 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2226 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2227 for (i = 0; i < MAX_NUM_KEYS; i++)
2228 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2229 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2230 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2232 if (game.centered_player_nr == -1)
2234 for (i = 0; i < MAX_PLAYERS; i++)
2236 /* only one player in Supaplex game engine */
2237 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2240 for (k = 0; k < MAX_NUM_KEYS; k++)
2242 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2244 if (level.native_em_level->ply[i]->keys & (1 << k))
2245 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2246 get_key_element_from_nr(k);
2248 else if (stored_player[i].key[k])
2249 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250 get_key_element_from_nr(k);
2253 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255 level.native_em_level->ply[i]->dynamite;
2256 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2257 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258 level.native_sp_level->game_sp->red_disk_count;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2261 stored_player[i].inventory_size;
2263 if (stored_player[i].num_white_keys > 0)
2264 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2267 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2268 stored_player[i].num_white_keys;
2273 int player_nr = game.centered_player_nr;
2275 for (k = 0; k < MAX_NUM_KEYS; k++)
2277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2283 else if (stored_player[player_nr].key[k])
2284 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2285 get_key_element_from_nr(k);
2288 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290 level.native_em_level->ply[player_nr]->dynamite;
2291 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2292 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2293 level.native_sp_level->game_sp->red_disk_count;
2295 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2296 stored_player[player_nr].inventory_size;
2298 if (stored_player[player_nr].num_white_keys > 0)
2299 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[player_nr].num_white_keys;
2305 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2307 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2308 get_inventory_element_from_pos(local_player, i);
2309 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2310 get_inventory_element_from_pos(local_player, -i - 1);
2313 game_panel_controls[GAME_PANEL_SCORE].value = score;
2314 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2316 game_panel_controls[GAME_PANEL_TIME].value = time;
2318 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2319 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2320 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2322 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2325 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2327 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2328 local_player->shield_normal_time_left;
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2330 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2332 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2333 local_player->shield_deadly_time_left;
2335 game_panel_controls[GAME_PANEL_EXIT].value =
2336 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2338 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2339 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2340 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2341 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2342 EL_EMC_MAGIC_BALL_SWITCH);
2344 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2345 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2346 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2347 game.light_time_left;
2349 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2350 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2351 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2352 game.timegate_time_left;
2354 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2355 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2357 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2358 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2359 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2360 game.lenses_time_left;
2362 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2363 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2364 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2365 game.magnify_time_left;
2367 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2368 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2369 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2370 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2371 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2372 EL_BALLOON_SWITCH_NONE);
2374 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2375 local_player->dynabomb_count;
2376 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2377 local_player->dynabomb_size;
2378 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2379 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2381 game_panel_controls[GAME_PANEL_PENGUINS].value =
2382 local_player->friends_still_needed;
2384 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2385 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2387 local_player->sokobanfields_still_needed;
2389 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2390 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2392 for (i = 0; i < NUM_BELTS; i++)
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2395 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2396 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2397 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2398 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2401 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2402 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2403 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2404 game.magic_wall_time_left;
2406 #if USE_PLAYER_GRAVITY
2407 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2408 local_player->gravity;
2410 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2413 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2416 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419 game.panel.element[i].id : EL_UNDEFINED);
2421 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424 element_info[game.panel.element_count[i].id].element_count : 0);
2426 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429 element_info[game.panel.ce_score[i].id].collect_score : 0);
2431 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434 element_info[game.panel.ce_score_element[i].id].collect_score :
2437 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2441 /* update game panel control frames */
2443 for (i = 0; game_panel_controls[i].nr != -1; i++)
2445 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2447 if (gpc->type == TYPE_ELEMENT)
2449 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2451 int last_anim_random_frame = gfx.anim_random_frame;
2452 int element = gpc->value;
2453 int graphic = el2panelimg(element);
2455 if (gpc->value != gpc->last_value)
2458 gpc->gfx_random = INIT_GFX_RANDOM();
2464 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466 gpc->gfx_random = INIT_GFX_RANDOM();
2469 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470 gfx.anim_random_frame = gpc->gfx_random;
2472 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473 gpc->gfx_frame = element_info[element].collect_score;
2475 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2478 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479 gfx.anim_random_frame = last_anim_random_frame;
2485 void DisplayGameControlValues()
2487 boolean redraw_panel = FALSE;
2490 for (i = 0; game_panel_controls[i].nr != -1; i++)
2492 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2494 if (PANEL_DEACTIVATED(gpc->pos))
2497 if (gpc->value == gpc->last_value &&
2498 gpc->frame == gpc->last_frame)
2501 redraw_panel = TRUE;
2507 /* copy default game door content to main double buffer */
2509 /* !!! CHECK AGAIN !!! */
2510 SetPanelBackground();
2511 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2512 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2514 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518 /* redraw game control buttons */
2520 RedrawGameButtons();
2526 game_status = GAME_MODE_PSEUDO_PANEL;
2529 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 for (i = 0; game_panel_controls[i].nr != -1; i++)
2535 int nr = game_panel_order[i].nr;
2536 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541 struct TextPosInfo *pos = gpc->pos;
2542 int type = gpc->type;
2543 int value = gpc->value;
2544 int frame = gpc->frame;
2546 int last_value = gpc->last_value;
2547 int last_frame = gpc->last_frame;
2549 int size = pos->size;
2550 int font = pos->font;
2551 boolean draw_masked = pos->draw_masked;
2552 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554 if (PANEL_DEACTIVATED(pos))
2558 if (value == last_value && frame == last_frame)
2562 gpc->last_value = value;
2563 gpc->last_frame = frame;
2566 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2569 if (type == TYPE_INTEGER)
2571 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2572 nr == GAME_PANEL_TIME)
2574 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576 if (use_dynamic_size) /* use dynamic number of digits */
2578 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2579 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2580 int size2 = size1 + 1;
2581 int font1 = pos->font;
2582 int font2 = pos->font_alt;
2584 size = (value < value_change ? size1 : size2);
2585 font = (value < value_change ? font1 : font2);
2588 /* clear background if value just changed its size (dynamic digits) */
2589 if ((last_value < value_change) != (value < value_change))
2591 int width1 = size1 * getFontWidth(font1);
2592 int width2 = size2 * getFontWidth(font2);
2593 int max_width = MAX(width1, width2);
2594 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596 pos->width = max_width;
2598 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2599 max_width, max_height);
2606 /* correct text size if "digits" is zero or less */
2608 size = strlen(int2str(value, size));
2610 /* dynamically correct text alignment */
2611 pos->width = size * getFontWidth(font);
2614 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2615 int2str(value, size), font, mask_mode);
2617 else if (type == TYPE_ELEMENT)
2619 int element, graphic;
2623 int dst_x = PANEL_XPOS(pos);
2624 int dst_y = PANEL_YPOS(pos);
2627 if (value != EL_UNDEFINED && value != EL_EMPTY)
2630 graphic = el2panelimg(value);
2632 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2635 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642 width = graphic_info[graphic].width * size / TILESIZE;
2643 height = graphic_info[graphic].height * size / TILESIZE;
2647 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2648 dst_x - src_x, dst_y - src_y);
2649 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2654 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2659 if (value == EL_UNDEFINED || value == EL_EMPTY)
2661 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2662 graphic = el2panelimg(element);
2664 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2665 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2666 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2671 graphic = el2panelimg(value);
2673 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2676 width = graphic_info[graphic].width * size / TILESIZE;
2677 height = graphic_info[graphic].height * size / TILESIZE;
2679 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2682 else if (type == TYPE_STRING)
2684 boolean active = (value != 0);
2685 char *state_normal = "off";
2686 char *state_active = "on";
2687 char *state = (active ? state_active : state_normal);
2688 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2689 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2690 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2691 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2693 if (nr == GAME_PANEL_GRAVITY_STATE)
2695 int font1 = pos->font; /* (used for normal state) */
2696 int font2 = pos->font_alt; /* (used for active state) */
2698 int size1 = strlen(state_normal);
2699 int size2 = strlen(state_active);
2700 int width1 = size1 * getFontWidth(font1);
2701 int width2 = size2 * getFontWidth(font2);
2702 int max_width = MAX(width1, width2);
2703 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705 pos->width = max_width;
2707 /* clear background for values that may have changed its size */
2708 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709 max_width, max_height);
2712 font = (active ? font2 : font1);
2722 /* don't truncate output if "chars" is zero or less */
2725 /* dynamically correct text alignment */
2726 pos->width = size * getFontWidth(font);
2730 s_cut = getStringCopyN(s, size);
2732 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2733 s_cut, font, mask_mode);
2739 redraw_mask |= REDRAW_DOOR_1;
2742 game_status = GAME_MODE_PLAYING;
2745 void UpdateAndDisplayGameControlValues()
2747 if (tape.warp_forward)
2750 UpdateGameControlValues();
2751 DisplayGameControlValues();
2754 void DrawGameValue_Emeralds(int value)
2756 struct TextPosInfo *pos = &game.panel.gems;
2757 int font_nr = pos->font;
2758 int font_width = getFontWidth(font_nr);
2759 int chars = pos->size;
2762 return; /* !!! USE NEW STUFF !!! */
2765 if (PANEL_DEACTIVATED(pos))
2768 pos->width = chars * font_width;
2770 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2773 void DrawGameValue_Dynamite(int value)
2775 struct TextPosInfo *pos = &game.panel.inventory_count;
2776 int font_nr = pos->font;
2777 int font_width = getFontWidth(font_nr);
2778 int chars = pos->size;
2781 return; /* !!! USE NEW STUFF !!! */
2784 if (PANEL_DEACTIVATED(pos))
2787 pos->width = chars * font_width;
2789 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2792 void DrawGameValue_Score(int value)
2794 struct TextPosInfo *pos = &game.panel.score;
2795 int font_nr = pos->font;
2796 int font_width = getFontWidth(font_nr);
2797 int chars = pos->size;
2800 return; /* !!! USE NEW STUFF !!! */
2803 if (PANEL_DEACTIVATED(pos))
2806 pos->width = chars * font_width;
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Time(int value)
2813 struct TextPosInfo *pos = &game.panel.time;
2814 static int last_value = -1;
2817 int chars = pos->size;
2818 int font1_nr = pos->font;
2819 int font2_nr = pos->font_alt;
2820 int font_nr = font1_nr;
2821 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2824 return; /* !!! USE NEW STUFF !!! */
2827 if (PANEL_DEACTIVATED(pos))
2830 if (use_dynamic_chars) /* use dynamic number of chars */
2832 chars = (value < 1000 ? chars1 : chars2);
2833 font_nr = (value < 1000 ? font1_nr : font2_nr);
2836 /* clear background if value just changed its size (dynamic chars only) */
2837 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2839 int width1 = chars1 * getFontWidth(font1_nr);
2840 int width2 = chars2 * getFontWidth(font2_nr);
2841 int max_width = MAX(width1, width2);
2842 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2844 pos->width = max_width;
2846 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847 max_width, max_height);
2850 pos->width = chars * getFontWidth(font_nr);
2852 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Level(int value)
2859 struct TextPosInfo *pos = &game.panel.level_number;
2862 int chars = pos->size;
2863 int font1_nr = pos->font;
2864 int font2_nr = pos->font_alt;
2865 int font_nr = font1_nr;
2866 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2869 return; /* !!! USE NEW STUFF !!! */
2872 if (PANEL_DEACTIVATED(pos))
2875 if (use_dynamic_chars) /* use dynamic number of chars */
2877 chars = (level_nr < 100 ? chars1 : chars2);
2878 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2881 pos->width = chars * getFontWidth(font_nr);
2883 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2886 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2891 return; /* !!! USE NEW STUFF !!! */
2894 for (i = 0; i < MAX_NUM_KEYS; i++)
2896 struct TextPosInfo *pos = &game.panel.key[i];
2897 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2898 int src_y = DOOR_GFX_PAGEY1 + 123;
2899 int dst_x = PANEL_XPOS(pos);
2900 int dst_y = PANEL_YPOS(pos);
2902 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2903 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2905 int graphic = el2edimg(element);
2907 if (PANEL_DEACTIVATED(pos))
2911 /* masked blit with tiles from half-size scaled bitmap does not work yet
2912 (no mask bitmap created for these sizes after loading and scaling) --
2913 solution: load without creating mask, scale, then create final mask */
2915 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2916 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2923 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2925 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2926 dst_x - src_x, dst_y - src_y);
2927 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2932 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2934 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2935 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2940 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2943 int key[MAX_NUM_KEYS];
2946 /* prevent EM engine from updating time/score values parallel to GameWon() */
2947 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2948 local_player->LevelSolved)
2951 for (i = 0; i < MAX_NUM_KEYS; i++)
2952 key[i] = key_bits & (1 << i);
2954 DrawGameValue_Level(level_nr);
2956 DrawGameValue_Emeralds(emeralds);
2957 DrawGameValue_Dynamite(dynamite);
2958 DrawGameValue_Score(score);
2959 DrawGameValue_Time(time);
2961 DrawGameValue_Keys(key);
2964 void UpdateGameDoorValues()
2966 UpdateGameControlValues();
2969 void DrawGameDoorValues()
2971 DisplayGameControlValues();
2974 void DrawGameDoorValues_OLD()
2976 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2977 int dynamite_value = 0;
2978 int score_value = (local_player->LevelSolved ? local_player->score_final :
2979 local_player->score);
2980 int gems_value = local_player->gems_still_needed;
2984 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2986 DrawGameDoorValues_EM();
2991 if (game.centered_player_nr == -1)
2993 for (i = 0; i < MAX_PLAYERS; i++)
2995 for (j = 0; j < MAX_NUM_KEYS; j++)
2996 if (stored_player[i].key[j])
2997 key_bits |= (1 << j);
2999 dynamite_value += stored_player[i].inventory_size;
3004 int player_nr = game.centered_player_nr;
3006 for (i = 0; i < MAX_NUM_KEYS; i++)
3007 if (stored_player[player_nr].key[i])
3008 key_bits |= (1 << i);
3010 dynamite_value = stored_player[player_nr].inventory_size;
3013 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3019 =============================================================================
3021 -----------------------------------------------------------------------------
3022 initialize game engine due to level / tape version number
3023 =============================================================================
3026 static void InitGameEngine()
3028 int i, j, k, l, x, y;
3030 /* set game engine from tape file when re-playing, else from level file */
3031 game.engine_version = (tape.playing ? tape.engine_version :
3032 level.game_version);
3034 /* set single or multi-player game mode (needed for re-playing tapes) */
3035 game.team_mode = setup.team_mode;
3039 int num_players = 0;
3041 for (i = 0; i < MAX_PLAYERS; i++)
3042 if (tape.player_participates[i])
3045 /* multi-player tapes contain input data for more than one player */
3046 game.team_mode = (num_players > 1);
3049 /* ---------------------------------------------------------------------- */
3050 /* set flags for bugs and changes according to active game engine version */
3051 /* ---------------------------------------------------------------------- */
3054 Summary of bugfix/change:
3055 Fixed handling for custom elements that change when pushed by the player.
3057 Fixed/changed in version:
3061 Before 3.1.0, custom elements that "change when pushing" changed directly
3062 after the player started pushing them (until then handled in "DigField()").
3063 Since 3.1.0, these custom elements are not changed until the "pushing"
3064 move of the element is finished (now handled in "ContinueMoving()").
3066 Affected levels/tapes:
3067 The first condition is generally needed for all levels/tapes before version
3068 3.1.0, which might use the old behaviour before it was changed; known tapes
3069 that are affected are some tapes from the level set "Walpurgis Gardens" by
3071 The second condition is an exception from the above case and is needed for
3072 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073 above (including some development versions of 3.1.0), but before it was
3074 known that this change would break tapes like the above and was fixed in
3075 3.1.1, so that the changed behaviour was active although the engine version
3076 while recording maybe was before 3.1.0. There is at least one tape that is
3077 affected by this exception, which is the tape for the one-level set "Bug
3078 Machine" by Juergen Bonhagen.
3081 game.use_change_when_pushing_bug =
3082 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3084 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085 tape.game_version < VERSION_IDENT(3,1,1,0)));
3088 Summary of bugfix/change:
3089 Fixed handling for blocking the field the player leaves when moving.
3091 Fixed/changed in version:
3095 Before 3.1.1, when "block last field when moving" was enabled, the field
3096 the player is leaving when moving was blocked for the time of the move,
3097 and was directly unblocked afterwards. This resulted in the last field
3098 being blocked for exactly one less than the number of frames of one player
3099 move. Additionally, even when blocking was disabled, the last field was
3100 blocked for exactly one frame.
3101 Since 3.1.1, due to changes in player movement handling, the last field
3102 is not blocked at all when blocking is disabled. When blocking is enabled,
3103 the last field is blocked for exactly the number of frames of one player
3104 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105 last field is blocked for exactly one more than the number of frames of
3108 Affected levels/tapes:
3109 (!!! yet to be determined -- probably many !!!)
3112 game.use_block_last_field_bug =
3113 (game.engine_version < VERSION_IDENT(3,1,1,0));
3116 Summary of bugfix/change:
3117 Changed behaviour of CE changes with multiple changes per single frame.
3119 Fixed/changed in version:
3123 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124 This resulted in race conditions where CEs seem to behave strange in some
3125 situations (where triggered CE changes were just skipped because there was
3126 already a CE change on that tile in the playfield in that engine frame).
3127 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128 (The number of changes per frame must be limited in any case, because else
3129 it is easily possible to define CE changes that would result in an infinite
3130 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131 should be set large enough so that it would only be reached in cases where
3132 the corresponding CE change conditions run into a loop. Therefore, it seems
3133 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134 maximal number of change pages for custom elements.)
3136 Affected levels/tapes:
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141 game.max_num_changes_per_frame = 1;
3143 game.max_num_changes_per_frame =
3144 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3147 /* ---------------------------------------------------------------------- */
3149 /* default scan direction: scan playfield from top/left to bottom/right */
3150 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3152 /* dynamically adjust element properties according to game engine version */
3153 InitElementPropertiesEngine(game.engine_version);
3156 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157 printf(" tape version == %06d [%s] [file: %06d]\n",
3158 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3160 printf(" => game.engine_version == %06d\n", game.engine_version);
3163 /* ---------- initialize player's initial move delay --------------------- */
3165 /* dynamically adjust player properties according to level information */
3166 for (i = 0; i < MAX_PLAYERS; i++)
3167 game.initial_move_delay_value[i] =
3168 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3170 /* dynamically adjust player properties according to game engine version */
3171 for (i = 0; i < MAX_PLAYERS; i++)
3172 game.initial_move_delay[i] =
3173 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174 game.initial_move_delay_value[i] : 0);
3176 /* ---------- initialize player's initial push delay --------------------- */
3178 /* dynamically adjust player properties according to game engine version */
3179 game.initial_push_delay_value =
3180 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3182 /* ---------- initialize changing elements ------------------------------- */
3184 /* initialize changing elements information */
3185 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3187 struct ElementInfo *ei = &element_info[i];
3189 /* this pointer might have been changed in the level editor */
3190 ei->change = &ei->change_page[0];
3192 if (!IS_CUSTOM_ELEMENT(i))
3194 ei->change->target_element = EL_EMPTY_SPACE;
3195 ei->change->delay_fixed = 0;
3196 ei->change->delay_random = 0;
3197 ei->change->delay_frames = 1;
3200 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3202 ei->has_change_event[j] = FALSE;
3204 ei->event_page_nr[j] = 0;
3205 ei->event_page[j] = &ei->change_page[0];
3209 /* add changing elements from pre-defined list */
3210 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3212 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213 struct ElementInfo *ei = &element_info[ch_delay->element];
3215 ei->change->target_element = ch_delay->target_element;
3216 ei->change->delay_fixed = ch_delay->change_delay;
3218 ei->change->pre_change_function = ch_delay->pre_change_function;
3219 ei->change->change_function = ch_delay->change_function;
3220 ei->change->post_change_function = ch_delay->post_change_function;
3222 ei->change->can_change = TRUE;
3223 ei->change->can_change_or_has_action = TRUE;
3225 ei->has_change_event[CE_DELAY] = TRUE;
3227 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3231 /* ---------- initialize internal run-time variables --------------------- */
3233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3237 for (j = 0; j < ei->num_change_pages; j++)
3239 ei->change_page[j].can_change_or_has_action =
3240 (ei->change_page[j].can_change |
3241 ei->change_page[j].has_action);
3245 /* add change events from custom element configuration */
3246 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3248 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3250 for (j = 0; j < ei->num_change_pages; j++)
3252 if (!ei->change_page[j].can_change_or_has_action)
3255 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3257 /* only add event page for the first page found with this event */
3258 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3260 ei->has_change_event[k] = TRUE;
3262 ei->event_page_nr[k] = j;
3263 ei->event_page[k] = &ei->change_page[j];
3270 /* ---------- initialize reference elements in change conditions --------- */
3272 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3274 int element = EL_CUSTOM_START + i;
3275 struct ElementInfo *ei = &element_info[element];
3277 for (j = 0; j < ei->num_change_pages; j++)
3279 int trigger_element = ei->change_page[j].initial_trigger_element;
3281 if (trigger_element >= EL_PREV_CE_8 &&
3282 trigger_element <= EL_NEXT_CE_8)
3283 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3285 ei->change_page[j].trigger_element = trigger_element;
3290 /* ---------- initialize run-time trigger player and element ------------- */
3292 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3294 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3296 for (j = 0; j < ei->num_change_pages; j++)
3298 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3299 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3300 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3301 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3302 ei->change_page[j].actual_trigger_ce_value = 0;
3303 ei->change_page[j].actual_trigger_ce_score = 0;
3307 /* ---------- initialize trigger events ---------------------------------- */
3309 /* initialize trigger events information */
3310 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3312 trigger_events[i][j] = FALSE;
3314 /* add trigger events from element change event properties */
3315 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317 struct ElementInfo *ei = &element_info[i];
3319 for (j = 0; j < ei->num_change_pages; j++)
3321 if (!ei->change_page[j].can_change_or_has_action)
3324 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3326 int trigger_element = ei->change_page[j].trigger_element;
3328 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3330 if (ei->change_page[j].has_event[k])
3332 if (IS_GROUP_ELEMENT(trigger_element))
3334 struct ElementGroupInfo *group =
3335 element_info[trigger_element].group;
3337 for (l = 0; l < group->num_elements_resolved; l++)
3338 trigger_events[group->element_resolved[l]][k] = TRUE;
3340 else if (trigger_element == EL_ANY_ELEMENT)
3341 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3342 trigger_events[l][k] = TRUE;
3344 trigger_events[trigger_element][k] = TRUE;
3351 /* ---------- initialize push delay -------------------------------------- */
3353 /* initialize push delay values to default */
3354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356 if (!IS_CUSTOM_ELEMENT(i))
3358 /* set default push delay values (corrected since version 3.0.7-1) */
3359 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3361 element_info[i].push_delay_fixed = 2;
3362 element_info[i].push_delay_random = 8;
3366 element_info[i].push_delay_fixed = 8;
3367 element_info[i].push_delay_random = 8;
3372 /* set push delay value for certain elements from pre-defined list */
3373 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3375 int e = push_delay_list[i].element;
3377 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3378 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3381 /* set push delay value for Supaplex elements for newer engine versions */
3382 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386 if (IS_SP_ELEMENT(i))
3388 /* set SP push delay to just enough to push under a falling zonk */
3389 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3391 element_info[i].push_delay_fixed = delay;
3392 element_info[i].push_delay_random = 0;
3397 /* ---------- initialize move stepsize ----------------------------------- */
3399 /* initialize move stepsize values to default */
3400 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401 if (!IS_CUSTOM_ELEMENT(i))
3402 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3404 /* set move stepsize value for certain elements from pre-defined list */
3405 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3407 int e = move_stepsize_list[i].element;
3409 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3412 /* ---------- initialize collect score ----------------------------------- */
3414 /* initialize collect score values for custom elements from initial value */
3415 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416 if (IS_CUSTOM_ELEMENT(i))
3417 element_info[i].collect_score = element_info[i].collect_score_initial;
3419 /* ---------- initialize collect count ----------------------------------- */
3421 /* initialize collect count values for non-custom elements */
3422 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423 if (!IS_CUSTOM_ELEMENT(i))
3424 element_info[i].collect_count_initial = 0;
3426 /* add collect count values for all elements from pre-defined list */
3427 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3428 element_info[collect_count_list[i].element].collect_count_initial =
3429 collect_count_list[i].count;
3431 /* ---------- initialize access direction -------------------------------- */
3433 /* initialize access direction values to default (access from every side) */
3434 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435 if (!IS_CUSTOM_ELEMENT(i))
3436 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3438 /* set access direction value for certain elements from pre-defined list */
3439 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3440 element_info[access_direction_list[i].element].access_direction =
3441 access_direction_list[i].direction;
3443 /* ---------- initialize explosion content ------------------------------- */
3444 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3446 if (IS_CUSTOM_ELEMENT(i))
3449 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3451 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3453 element_info[i].content.e[x][y] =
3454 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3455 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3456 i == EL_PLAYER_3 ? EL_EMERALD :
3457 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3458 i == EL_MOLE ? EL_EMERALD_RED :
3459 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3460 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3461 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3462 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3463 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3464 i == EL_WALL_EMERALD ? EL_EMERALD :
3465 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3466 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3467 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3468 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3469 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3470 i == EL_WALL_PEARL ? EL_PEARL :
3471 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3476 /* ---------- initialize recursion detection ------------------------------ */
3477 recursion_loop_depth = 0;
3478 recursion_loop_detected = FALSE;
3479 recursion_loop_element = EL_UNDEFINED;
3481 /* ---------- initialize graphics engine ---------------------------------- */
3482 game.scroll_delay_value =
3483 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3484 setup.scroll_delay ? setup.scroll_delay_value : 0);
3485 game.scroll_delay_value =
3486 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3489 int get_num_special_action(int element, int action_first, int action_last)
3491 int num_special_action = 0;
3494 for (i = action_first; i <= action_last; i++)
3496 boolean found = FALSE;
3498 for (j = 0; j < NUM_DIRECTIONS; j++)
3499 if (el_act_dir2img(element, i, j) !=
3500 el_act_dir2img(element, ACTION_DEFAULT, j))
3504 num_special_action++;
3509 return num_special_action;
3514 =============================================================================
3516 -----------------------------------------------------------------------------
3517 initialize and start new game
3518 =============================================================================
3523 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3524 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3525 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3527 boolean do_fading = (game_status == GAME_MODE_MAIN);
3530 int initial_move_dir = MV_DOWN;
3532 int initial_move_dir = MV_NONE;
3536 game_status = GAME_MODE_PLAYING;
3539 /* needed if different viewport properties defined for playing */
3540 ChangeViewportPropertiesIfNeeded();
3544 DrawCompleteVideoDisplay();
3548 InitGameControlValues();
3550 /* don't play tapes over network */
3551 network_playing = (options.network && !tape.playing);
3553 for (i = 0; i < MAX_PLAYERS; i++)
3555 struct PlayerInfo *player = &stored_player[i];
3557 player->index_nr = i;
3558 player->index_bit = (1 << i);
3559 player->element_nr = EL_PLAYER_1 + i;
3561 player->present = FALSE;
3562 player->active = FALSE;
3563 player->mapped = FALSE;
3565 player->killed = FALSE;
3566 player->reanimated = FALSE;
3569 player->effective_action = 0;
3570 player->programmed_action = 0;
3573 player->score_final = 0;
3575 player->gems_still_needed = level.gems_needed;
3576 player->sokobanfields_still_needed = 0;
3577 player->lights_still_needed = 0;
3578 player->friends_still_needed = 0;
3580 for (j = 0; j < MAX_NUM_KEYS; j++)
3581 player->key[j] = FALSE;
3583 player->num_white_keys = 0;
3585 player->dynabomb_count = 0;
3586 player->dynabomb_size = 1;
3587 player->dynabombs_left = 0;
3588 player->dynabomb_xl = FALSE;
3590 player->MovDir = initial_move_dir;
3593 player->GfxDir = initial_move_dir;
3594 player->GfxAction = ACTION_DEFAULT;
3596 player->StepFrame = 0;
3598 player->initial_element = player->element_nr;
3599 player->artwork_element =
3600 (level.use_artwork_element[i] ? level.artwork_element[i] :
3601 player->element_nr);
3602 player->use_murphy = FALSE;
3604 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3605 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3607 player->gravity = level.initial_player_gravity[i];
3609 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3611 player->actual_frame_counter = 0;
3613 player->step_counter = 0;
3615 player->last_move_dir = initial_move_dir;
3617 player->is_active = FALSE;
3619 player->is_waiting = FALSE;
3620 player->is_moving = FALSE;
3621 player->is_auto_moving = FALSE;
3622 player->is_digging = FALSE;
3623 player->is_snapping = FALSE;
3624 player->is_collecting = FALSE;
3625 player->is_pushing = FALSE;
3626 player->is_switching = FALSE;
3627 player->is_dropping = FALSE;
3628 player->is_dropping_pressed = FALSE;
3630 player->is_bored = FALSE;
3631 player->is_sleeping = FALSE;
3633 player->frame_counter_bored = -1;
3634 player->frame_counter_sleeping = -1;
3636 player->anim_delay_counter = 0;
3637 player->post_delay_counter = 0;
3639 player->dir_waiting = initial_move_dir;
3640 player->action_waiting = ACTION_DEFAULT;
3641 player->last_action_waiting = ACTION_DEFAULT;
3642 player->special_action_bored = ACTION_DEFAULT;
3643 player->special_action_sleeping = ACTION_DEFAULT;
3645 player->switch_x = -1;
3646 player->switch_y = -1;
3648 player->drop_x = -1;
3649 player->drop_y = -1;
3651 player->show_envelope = 0;
3653 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3655 player->push_delay = -1; /* initialized when pushing starts */
3656 player->push_delay_value = game.initial_push_delay_value;
3658 player->drop_delay = 0;
3659 player->drop_pressed_delay = 0;
3661 player->last_jx = -1;
3662 player->last_jy = -1;
3666 player->shield_normal_time_left = 0;
3667 player->shield_deadly_time_left = 0;
3669 player->inventory_infinite_element = EL_UNDEFINED;
3670 player->inventory_size = 0;
3672 if (level.use_initial_inventory[i])
3674 for (j = 0; j < level.initial_inventory_size[i]; j++)
3676 int element = level.initial_inventory_content[i][j];
3677 int collect_count = element_info[element].collect_count_initial;
3680 if (!IS_CUSTOM_ELEMENT(element))
3683 if (collect_count == 0)
3684 player->inventory_infinite_element = element;
3686 for (k = 0; k < collect_count; k++)
3687 if (player->inventory_size < MAX_INVENTORY_SIZE)
3688 player->inventory_element[player->inventory_size++] = element;
3692 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3693 SnapField(player, 0, 0);
3695 player->LevelSolved = FALSE;
3696 player->GameOver = FALSE;
3698 player->LevelSolved_GameWon = FALSE;
3699 player->LevelSolved_GameEnd = FALSE;
3700 player->LevelSolved_PanelOff = FALSE;
3701 player->LevelSolved_SaveTape = FALSE;
3702 player->LevelSolved_SaveScore = FALSE;
3703 player->LevelSolved_CountingTime = 0;
3704 player->LevelSolved_CountingScore = 0;
3706 map_player_action[i] = i;
3709 network_player_action_received = FALSE;
3711 #if defined(NETWORK_AVALIABLE)
3712 /* initial null action */
3713 if (network_playing)
3714 SendToServer_MovePlayer(MV_NONE);
3723 TimeLeft = level.time;
3726 ScreenMovDir = MV_NONE;
3730 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3732 AllPlayersGone = FALSE;
3734 game.no_time_limit = (level.time == 0);
3736 game.yamyam_content_nr = 0;
3737 game.robot_wheel_active = FALSE;
3738 game.magic_wall_active = FALSE;
3739 game.magic_wall_time_left = 0;
3740 game.light_time_left = 0;
3741 game.timegate_time_left = 0;
3742 game.switchgate_pos = 0;
3743 game.wind_direction = level.wind_direction_initial;
3745 #if !USE_PLAYER_GRAVITY
3746 game.gravity = FALSE;
3747 game.explosions_delayed = TRUE;
3750 game.lenses_time_left = 0;
3751 game.magnify_time_left = 0;
3753 game.ball_state = level.ball_state_initial;
3754 game.ball_content_nr = 0;
3756 game.envelope_active = FALSE;
3758 /* set focus to local player for network games, else to all players */
3759 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3760 game.centered_player_nr_next = game.centered_player_nr;
3761 game.set_centered_player = FALSE;
3763 if (network_playing && tape.recording)
3765 /* store client dependent player focus when recording network games */
3766 tape.centered_player_nr_next = game.centered_player_nr_next;
3767 tape.set_centered_player = TRUE;
3770 for (i = 0; i < NUM_BELTS; i++)
3772 game.belt_dir[i] = MV_NONE;
3773 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3776 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3777 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3779 #if DEBUG_INIT_PLAYER
3782 printf("Player status at level initialization:\n");
3786 SCAN_PLAYFIELD(x, y)
3788 Feld[x][y] = level.field[x][y];
3789 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3790 ChangeDelay[x][y] = 0;
3791 ChangePage[x][y] = -1;
3792 #if USE_NEW_CUSTOM_VALUE
3793 CustomValue[x][y] = 0; /* initialized in InitField() */
3795 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3797 WasJustMoving[x][y] = 0;
3798 WasJustFalling[x][y] = 0;
3799 CheckCollision[x][y] = 0;
3800 CheckImpact[x][y] = 0;
3802 Pushed[x][y] = FALSE;
3804 ChangeCount[x][y] = 0;
3805 ChangeEvent[x][y] = -1;
3807 ExplodePhase[x][y] = 0;
3808 ExplodeDelay[x][y] = 0;
3809 ExplodeField[x][y] = EX_TYPE_NONE;
3811 RunnerVisit[x][y] = 0;
3812 PlayerVisit[x][y] = 0;
3815 GfxRandom[x][y] = INIT_GFX_RANDOM();
3816 GfxElement[x][y] = EL_UNDEFINED;
3817 GfxAction[x][y] = ACTION_DEFAULT;
3818 GfxDir[x][y] = MV_NONE;
3819 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3822 SCAN_PLAYFIELD(x, y)
3824 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3826 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3828 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3831 InitField(x, y, TRUE);
3833 ResetGfxAnimation(x, y);
3838 for (i = 0; i < MAX_PLAYERS; i++)
3840 struct PlayerInfo *player = &stored_player[i];
3842 /* set number of special actions for bored and sleeping animation */
3843 player->num_special_action_bored =
3844 get_num_special_action(player->artwork_element,
3845 ACTION_BORING_1, ACTION_BORING_LAST);
3846 player->num_special_action_sleeping =
3847 get_num_special_action(player->artwork_element,
3848 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3851 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3852 emulate_sb ? EMU_SOKOBAN :
3853 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3855 #if USE_NEW_ALL_SLIPPERY
3856 /* initialize type of slippery elements */
3857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3859 if (!IS_CUSTOM_ELEMENT(i))
3861 /* default: elements slip down either to the left or right randomly */
3862 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3864 /* SP style elements prefer to slip down on the left side */
3865 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3866 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3868 /* BD style elements prefer to slip down on the left side */
3869 if (game.emulation == EMU_BOULDERDASH)
3870 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3875 /* initialize explosion and ignition delay */
3876 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3878 if (!IS_CUSTOM_ELEMENT(i))
3881 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3882 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3883 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3884 int last_phase = (num_phase + 1) * delay;
3885 int half_phase = (num_phase / 2) * delay;
3887 element_info[i].explosion_delay = last_phase - 1;
3888 element_info[i].ignition_delay = half_phase;
3890 if (i == EL_BLACK_ORB)
3891 element_info[i].ignition_delay = 1;
3895 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3896 element_info[i].explosion_delay = 1;
3898 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3899 element_info[i].ignition_delay = 1;
3903 /* correct non-moving belts to start moving left */
3904 for (i = 0; i < NUM_BELTS; i++)
3905 if (game.belt_dir[i] == MV_NONE)
3906 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3908 #if USE_NEW_PLAYER_ASSIGNMENTS
3909 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3910 /* choose default local player */
3911 local_player = &stored_player[0];
3913 for (i = 0; i < MAX_PLAYERS; i++)
3914 stored_player[i].connected = FALSE;
3916 local_player->connected = TRUE;
3917 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3920 printf("::: TEAM MODE: %d\n", game.team_mode);
3926 for (i = 0; i < MAX_PLAYERS; i++)
3927 stored_player[i].connected = tape.player_participates[i];
3929 /* try to guess locally connected team mode players (needed for correct
3930 assignment of player figures from level to locally playing players) */
3932 for (i = 0; i < MAX_PLAYERS; i++)
3933 if (tape.player_participates[i])
3934 stored_player[i].connected = TRUE;
3937 else if (game.team_mode && !options.network)
3939 /* try to guess locally connected team mode players (needed for correct
3940 assignment of player figures from level to locally playing players) */
3942 for (i = 0; i < MAX_PLAYERS; i++)
3943 if (setup.input[i].use_joystick ||
3944 setup.input[i].key.left != KSYM_UNDEFINED)
3945 stored_player[i].connected = TRUE;
3948 #if DEBUG_INIT_PLAYER
3951 printf("Player status after level initialization:\n");
3953 for (i = 0; i < MAX_PLAYERS; i++)
3955 struct PlayerInfo *player = &stored_player[i];
3957 printf("- player %d: present == %d, connected == %d, active == %d",
3963 if (local_player == player)
3964 printf(" (local player)");
3971 #if DEBUG_INIT_PLAYER
3973 printf("Reassigning players ...\n");
3976 /* check if any connected player was not found in playfield */
3977 for (i = 0; i < MAX_PLAYERS; i++)
3979 struct PlayerInfo *player = &stored_player[i];
3981 if (player->connected && !player->present)
3983 struct PlayerInfo *field_player = NULL;
3985 #if DEBUG_INIT_PLAYER
3987 printf("- looking for field player for player %d ...\n", i + 1);
3990 /* assign first free player found that is present in the playfield */
3993 /* first try: look for unmapped playfield player that is not connected */
3994 for (j = 0; j < MAX_PLAYERS; j++)
3995 if (field_player == NULL &&
3996 stored_player[j].present &&
3997 !stored_player[j].mapped &&
3998 !stored_player[j].connected)
3999 field_player = &stored_player[j];
4001 /* second try: look for *any* unmapped playfield player */
4002 for (j = 0; j < MAX_PLAYERS; j++)
4003 if (field_player == NULL &&
4004 stored_player[j].present &&
4005 !stored_player[j].mapped)
4006 field_player = &stored_player[j];
4008 /* first try: look for unmapped playfield player that is not connected */
4009 if (field_player == NULL)
4010 for (j = 0; j < MAX_PLAYERS; j++)
4011 if (stored_player[j].present &&
4012 !stored_player[j].mapped &&
4013 !stored_player[j].connected)
4014 field_player = &stored_player[j];
4016 /* second try: look for *any* unmapped playfield player */
4017 if (field_player == NULL)
4018 for (j = 0; j < MAX_PLAYERS; j++)
4019 if (stored_player[j].present &&
4020 !stored_player[j].mapped)
4021 field_player = &stored_player[j];
4024 if (field_player != NULL)
4026 int jx = field_player->jx, jy = field_player->jy;
4028 #if DEBUG_INIT_PLAYER
4030 printf("- found player %d\n", field_player->index_nr + 1);
4033 player->present = FALSE;
4034 player->active = FALSE;
4036 field_player->present = TRUE;
4037 field_player->active = TRUE;
4040 player->initial_element = field_player->initial_element;
4041 player->artwork_element = field_player->artwork_element;
4043 player->block_last_field = field_player->block_last_field;
4044 player->block_delay_adjustment = field_player->block_delay_adjustment;
4047 StorePlayer[jx][jy] = field_player->element_nr;
4049 field_player->jx = field_player->last_jx = jx;
4050 field_player->jy = field_player->last_jy = jy;
4052 if (local_player == player)
4053 local_player = field_player;
4055 map_player_action[field_player->index_nr] = i;
4057 field_player->mapped = TRUE;
4059 #if DEBUG_INIT_PLAYER
4061 printf("- map_player_action[%d] == %d\n",
4062 field_player->index_nr + 1, i + 1);
4067 if (player->connected && player->present)
4068 player->mapped = TRUE;
4071 #if DEBUG_INIT_PLAYER
4074 printf("Player status after player assignment (first stage):\n");
4076 for (i = 0; i < MAX_PLAYERS; i++)
4078 struct PlayerInfo *player = &stored_player[i];
4080 printf("- player %d: present == %d, connected == %d, active == %d",
4086 if (local_player == player)
4087 printf(" (local player)");
4096 /* check if any connected player was not found in playfield */
4097 for (i = 0; i < MAX_PLAYERS; i++)
4099 struct PlayerInfo *player = &stored_player[i];
4101 if (player->connected && !player->present)
4103 for (j = 0; j < MAX_PLAYERS; j++)
4105 struct PlayerInfo *field_player = &stored_player[j];
4106 int jx = field_player->jx, jy = field_player->jy;
4108 /* assign first free player found that is present in the playfield */
4109 if (field_player->present && !field_player->connected)
4111 player->present = TRUE;
4112 player->active = TRUE;
4114 field_player->present = FALSE;
4115 field_player->active = FALSE;
4117 player->initial_element = field_player->initial_element;
4118 player->artwork_element = field_player->artwork_element;
4120 player->block_last_field = field_player->block_last_field;
4121 player->block_delay_adjustment = field_player->block_delay_adjustment;
4123 StorePlayer[jx][jy] = player->element_nr;
4125 player->jx = player->last_jx = jx;
4126 player->jy = player->last_jy = jy;
4136 printf("::: local_player->present == %d\n", local_player->present);
4141 /* when playing a tape, eliminate all players who do not participate */
4143 #if USE_NEW_PLAYER_ASSIGNMENTS
4146 if (!game.team_mode)
4149 for (i = 0; i < MAX_PLAYERS; i++)
4151 if (stored_player[i].active &&
4152 !tape.player_participates[map_player_action[i]])
4154 struct PlayerInfo *player = &stored_player[i];
4155 int jx = player->jx, jy = player->jy;
4157 #if DEBUG_INIT_PLAYER
4159 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4162 player->active = FALSE;
4163 StorePlayer[jx][jy] = 0;
4164 Feld[jx][jy] = EL_EMPTY;
4170 for (i = 0; i < MAX_PLAYERS; i++)
4172 if (stored_player[i].active &&
4173 !tape.player_participates[i])
4175 struct PlayerInfo *player = &stored_player[i];
4176 int jx = player->jx, jy = player->jy;
4178 player->active = FALSE;
4179 StorePlayer[jx][jy] = 0;
4180 Feld[jx][jy] = EL_EMPTY;
4185 else if (!options.network && !game.team_mode) /* && !tape.playing */
4187 /* when in single player mode, eliminate all but the first active player */
4189 for (i = 0; i < MAX_PLAYERS; i++)
4191 if (stored_player[i].active)
4193 for (j = i + 1; j < MAX_PLAYERS; j++)
4195 if (stored_player[j].active)
4197 struct PlayerInfo *player = &stored_player[j];
4198 int jx = player->jx, jy = player->jy;
4200 player->active = FALSE;
4201 player->present = FALSE;
4203 StorePlayer[jx][jy] = 0;
4204 Feld[jx][jy] = EL_EMPTY;
4211 /* when recording the game, store which players take part in the game */
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215 for (i = 0; i < MAX_PLAYERS; i++)
4216 if (stored_player[i].connected)
4217 tape.player_participates[i] = TRUE;
4219 for (i = 0; i < MAX_PLAYERS; i++)
4220 if (stored_player[i].active)
4221 tape.player_participates[i] = TRUE;
4225 #if DEBUG_INIT_PLAYER
4228 printf("Player status after player assignment (final stage):\n");
4230 for (i = 0; i < MAX_PLAYERS; i++)
4232 struct PlayerInfo *player = &stored_player[i];
4234 printf("- player %d: present == %d, connected == %d, active == %d",
4240 if (local_player == player)
4241 printf(" (local player)");
4248 if (BorderElement == EL_EMPTY)
4251 SBX_Right = lev_fieldx - SCR_FIELDX;
4253 SBY_Lower = lev_fieldy - SCR_FIELDY;
4258 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4260 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4266 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4268 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4269 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271 if (EVEN(SCR_FIELDX))
4273 if (EVEN(SCR_FIELDY))
4278 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4279 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4281 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4282 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4285 /* if local player not found, look for custom element that might create
4286 the player (make some assumptions about the right custom element) */
4287 if (!local_player->present)
4289 int start_x = 0, start_y = 0;
4290 int found_rating = 0;
4291 int found_element = EL_UNDEFINED;
4292 int player_nr = local_player->index_nr;
4294 SCAN_PLAYFIELD(x, y)
4296 int element = Feld[x][y];
4301 if (level.use_start_element[player_nr] &&
4302 level.start_element[player_nr] == element &&
4309 found_element = element;
4312 if (!IS_CUSTOM_ELEMENT(element))
4315 if (CAN_CHANGE(element))
4317 for (i = 0; i < element_info[element].num_change_pages; i++)
4319 /* check for player created from custom element as single target */
4320 content = element_info[element].change_page[i].target_element;
4321 is_player = ELEM_IS_PLAYER(content);
4323 if (is_player && (found_rating < 3 ||
4324 (found_rating == 3 && element < found_element)))
4330 found_element = element;
4335 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4337 /* check for player created from custom element as explosion content */
4338 content = element_info[element].content.e[xx][yy];
4339 is_player = ELEM_IS_PLAYER(content);
4341 if (is_player && (found_rating < 2 ||
4342 (found_rating == 2 && element < found_element)))
4344 start_x = x + xx - 1;
4345 start_y = y + yy - 1;
4348 found_element = element;
4351 if (!CAN_CHANGE(element))
4354 for (i = 0; i < element_info[element].num_change_pages; i++)
4356 /* check for player created from custom element as extended target */
4358 element_info[element].change_page[i].target_content.e[xx][yy];
4360 is_player = ELEM_IS_PLAYER(content);
4362 if (is_player && (found_rating < 1 ||
4363 (found_rating == 1 && element < found_element)))
4365 start_x = x + xx - 1;
4366 start_y = y + yy - 1;
4369 found_element = element;
4375 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4376 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4379 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4380 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4385 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4386 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4387 local_player->jx - MIDPOSX);
4389 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4390 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4391 local_player->jy - MIDPOSY);
4395 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4399 /* do not use PLAYING mask for fading out from main screen */
4400 game_status = GAME_MODE_MAIN;
4405 if (!game.restart_level)
4406 CloseDoor(DOOR_CLOSE_1);
4409 if (level_editor_test_game)
4410 FadeSkipNextFadeIn();
4412 FadeSetEnterScreen();
4414 if (level_editor_test_game)
4415 fading = fading_none;
4417 fading = menu.destination;
4421 FadeOut(REDRAW_FIELD);
4424 FadeOut(REDRAW_FIELD);
4428 game_status = GAME_MODE_PLAYING;
4431 /* !!! FIX THIS (START) !!! */
4432 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4434 InitGameEngine_EM();
4436 /* blit playfield from scroll buffer to normal back buffer for fading in */
4437 BlitScreenToBitmap_EM(backbuffer);
4439 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4441 InitGameEngine_SP();
4443 /* blit playfield from scroll buffer to normal back buffer for fading in */
4444 BlitScreenToBitmap_SP(backbuffer);
4451 /* after drawing the level, correct some elements */
4452 if (game.timegate_time_left == 0)
4453 CloseAllOpenTimegates();
4456 BlitScreenToBitmap(backbuffer);
4458 /* blit playfield from scroll buffer to normal back buffer for fading in */
4459 if (setup.soft_scrolling)
4460 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4463 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4465 /* !!! FIX THIS (END) !!! */
4468 FadeIn(REDRAW_FIELD);
4471 FadeIn(REDRAW_FIELD);
4476 if (!game.restart_level)
4478 /* copy default game door content to main double buffer */
4481 /* !!! CHECK AGAIN !!! */
4482 SetPanelBackground();
4483 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4484 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4486 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4488 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4489 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4490 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4491 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4494 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4495 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4499 SetPanelBackground();
4500 SetDrawBackgroundMask(REDRAW_DOOR_1);
4503 UpdateAndDisplayGameControlValues();
4505 UpdateGameDoorValues();
4506 DrawGameDoorValues();
4509 if (!game.restart_level)
4513 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4514 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4515 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4519 /* copy actual game door content to door double buffer for OpenDoor() */
4521 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4523 BlitBitmap(drawto, bitmap_db_door,
4524 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4527 OpenDoor(DOOR_OPEN_ALL);
4529 PlaySound(SND_GAME_STARTING);
4531 if (setup.sound_music)
4534 KeyboardAutoRepeatOffUnlessAutoplay();
4536 #if DEBUG_INIT_PLAYER
4539 printf("Player status (final):\n");
4541 for (i = 0; i < MAX_PLAYERS; i++)
4543 struct PlayerInfo *player = &stored_player[i];
4545 printf("- player %d: present == %d, connected == %d, active == %d",
4551 if (local_player == player)
4552 printf(" (local player)");
4567 if (!game.restart_level && !tape.playing)
4569 LevelStats_incPlayed(level_nr);
4571 SaveLevelSetup_SeriesInfo();
4574 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4578 game.restart_level = FALSE;
4581 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4583 /* this is used for non-R'n'D game engines to update certain engine values */
4585 /* needed to determine if sounds are played within the visible screen area */
4586 scroll_x = actual_scroll_x;
4587 scroll_y = actual_scroll_y;
4590 void InitMovDir(int x, int y)
4592 int i, element = Feld[x][y];
4593 static int xy[4][2] =
4600 static int direction[3][4] =
4602 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4603 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4604 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4613 Feld[x][y] = EL_BUG;
4614 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4617 case EL_SPACESHIP_RIGHT:
4618 case EL_SPACESHIP_UP:
4619 case EL_SPACESHIP_LEFT:
4620 case EL_SPACESHIP_DOWN:
4621 Feld[x][y] = EL_SPACESHIP;
4622 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4625 case EL_BD_BUTTERFLY_RIGHT:
4626 case EL_BD_BUTTERFLY_UP:
4627 case EL_BD_BUTTERFLY_LEFT:
4628 case EL_BD_BUTTERFLY_DOWN:
4629 Feld[x][y] = EL_BD_BUTTERFLY;
4630 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4633 case EL_BD_FIREFLY_RIGHT:
4634 case EL_BD_FIREFLY_UP:
4635 case EL_BD_FIREFLY_LEFT:
4636 case EL_BD_FIREFLY_DOWN:
4637 Feld[x][y] = EL_BD_FIREFLY;
4638 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4641 case EL_PACMAN_RIGHT:
4643 case EL_PACMAN_LEFT:
4644 case EL_PACMAN_DOWN:
4645 Feld[x][y] = EL_PACMAN;
4646 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4649 case EL_YAMYAM_LEFT:
4650 case EL_YAMYAM_RIGHT:
4652 case EL_YAMYAM_DOWN:
4653 Feld[x][y] = EL_YAMYAM;
4654 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4657 case EL_SP_SNIKSNAK:
4658 MovDir[x][y] = MV_UP;
4661 case EL_SP_ELECTRON:
4662 MovDir[x][y] = MV_LEFT;
4669 Feld[x][y] = EL_MOLE;
4670 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4674 if (IS_CUSTOM_ELEMENT(element))
4676 struct ElementInfo *ei = &element_info[element];
4677 int move_direction_initial = ei->move_direction_initial;
4678 int move_pattern = ei->move_pattern;
4680 if (move_direction_initial == MV_START_PREVIOUS)
4682 if (MovDir[x][y] != MV_NONE)
4685 move_direction_initial = MV_START_AUTOMATIC;
4688 if (move_direction_initial == MV_START_RANDOM)
4689 MovDir[x][y] = 1 << RND(4);
4690 else if (move_direction_initial & MV_ANY_DIRECTION)
4691 MovDir[x][y] = move_direction_initial;
4692 else if (move_pattern == MV_ALL_DIRECTIONS ||
4693 move_pattern == MV_TURNING_LEFT ||
4694 move_pattern == MV_TURNING_RIGHT ||
4695 move_pattern == MV_TURNING_LEFT_RIGHT ||
4696 move_pattern == MV_TURNING_RIGHT_LEFT ||
4697 move_pattern == MV_TURNING_RANDOM)
4698 MovDir[x][y] = 1 << RND(4);
4699 else if (move_pattern == MV_HORIZONTAL)
4700 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4701 else if (move_pattern == MV_VERTICAL)
4702 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4703 else if (move_pattern & MV_ANY_DIRECTION)
4704 MovDir[x][y] = element_info[element].move_pattern;
4705 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4706 move_pattern == MV_ALONG_RIGHT_SIDE)
4708 /* use random direction as default start direction */
4709 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4710 MovDir[x][y] = 1 << RND(4);
4712 for (i = 0; i < NUM_DIRECTIONS; i++)
4714 int x1 = x + xy[i][0];
4715 int y1 = y + xy[i][1];
4717 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4719 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4720 MovDir[x][y] = direction[0][i];
4722 MovDir[x][y] = direction[1][i];
4731 MovDir[x][y] = 1 << RND(4);
4733 if (element != EL_BUG &&
4734 element != EL_SPACESHIP &&
4735 element != EL_BD_BUTTERFLY &&
4736 element != EL_BD_FIREFLY)
4739 for (i = 0; i < NUM_DIRECTIONS; i++)
4741 int x1 = x + xy[i][0];
4742 int y1 = y + xy[i][1];
4744 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4746 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4748 MovDir[x][y] = direction[0][i];
4751 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4752 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4754 MovDir[x][y] = direction[1][i];
4763 GfxDir[x][y] = MovDir[x][y];
4766 void InitAmoebaNr(int x, int y)
4769 int group_nr = AmoebeNachbarNr(x, y);
4773 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4775 if (AmoebaCnt[i] == 0)
4783 AmoebaNr[x][y] = group_nr;
4784 AmoebaCnt[group_nr]++;
4785 AmoebaCnt2[group_nr]++;
4788 static void PlayerWins(struct PlayerInfo *player)
4790 player->LevelSolved = TRUE;
4791 player->GameOver = TRUE;
4793 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4794 level.native_em_level->lev->score : player->score);
4796 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4798 player->LevelSolved_CountingScore = player->score_final;
4803 static int time, time_final;
4804 static int score, score_final;
4805 static int game_over_delay_1 = 0;
4806 static int game_over_delay_2 = 0;
4807 int game_over_delay_value_1 = 50;
4808 int game_over_delay_value_2 = 50;
4810 if (!local_player->LevelSolved_GameWon)
4814 /* do not start end game actions before the player stops moving (to exit) */
4815 if (local_player->MovPos)
4818 local_player->LevelSolved_GameWon = TRUE;
4819 local_player->LevelSolved_SaveTape = tape.recording;
4820 local_player->LevelSolved_SaveScore = !tape.playing;
4824 LevelStats_incSolved(level_nr);
4826 SaveLevelSetup_SeriesInfo();
4829 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4833 if (tape.auto_play) /* tape might already be stopped here */
4834 tape.auto_play_level_solved = TRUE;
4840 game_over_delay_1 = game_over_delay_value_1;
4841 game_over_delay_2 = game_over_delay_value_2;
4843 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4844 score = score_final = local_player->score_final;
4849 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4851 else if (game.no_time_limit && TimePlayed < 999)
4854 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4857 local_player->score_final = score_final;
4859 if (level_editor_test_game)
4862 score = score_final;
4865 local_player->LevelSolved_CountingTime = time;
4866 local_player->LevelSolved_CountingScore = score;
4868 game_panel_controls[GAME_PANEL_TIME].value = time;
4869 game_panel_controls[GAME_PANEL_SCORE].value = score;
4871 DisplayGameControlValues();
4873 DrawGameValue_Time(time);
4874 DrawGameValue_Score(score);
4878 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4880 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4882 /* close exit door after last player */
4883 if ((AllPlayersGone &&
4884 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4885 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4886 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4887 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4888 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4890 int element = Feld[ExitX][ExitY];
4893 if (element == EL_EM_EXIT_OPEN ||
4894 element == EL_EM_STEEL_EXIT_OPEN)
4901 Feld[ExitX][ExitY] =
4902 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4903 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4904 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4905 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4906 EL_EM_STEEL_EXIT_CLOSING);
4908 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4912 /* player disappears */
4913 DrawLevelField(ExitX, ExitY);
4916 for (i = 0; i < MAX_PLAYERS; i++)
4918 struct PlayerInfo *player = &stored_player[i];
4920 if (player->present)
4922 RemovePlayer(player);
4924 /* player disappears */
4925 DrawLevelField(player->jx, player->jy);
4930 PlaySound(SND_GAME_WINNING);
4933 if (game_over_delay_1 > 0)
4935 game_over_delay_1--;
4940 if (time != time_final)
4942 int time_to_go = ABS(time_final - time);
4943 int time_count_dir = (time < time_final ? +1 : -1);
4944 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4946 time += time_count_steps * time_count_dir;
4947 score += time_count_steps * level.score[SC_TIME_BONUS];
4950 local_player->LevelSolved_CountingTime = time;
4951 local_player->LevelSolved_CountingScore = score;
4953 game_panel_controls[GAME_PANEL_TIME].value = time;
4954 game_panel_controls[GAME_PANEL_SCORE].value = score;
4956 DisplayGameControlValues();
4958 DrawGameValue_Time(time);
4959 DrawGameValue_Score(score);
4962 if (time == time_final)
4963 StopSound(SND_GAME_LEVELTIME_BONUS);
4964 else if (setup.sound_loops)
4965 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4967 PlaySound(SND_GAME_LEVELTIME_BONUS);
4972 local_player->LevelSolved_PanelOff = TRUE;
4974 if (game_over_delay_2 > 0)
4976 game_over_delay_2--;
4989 boolean raise_level = FALSE;
4991 local_player->LevelSolved_GameEnd = TRUE;
4993 CloseDoor(DOOR_CLOSE_1);
4995 if (local_player->LevelSolved_SaveTape)
5002 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5004 SaveTape(tape.level_nr); /* ask to save tape */
5008 if (level_editor_test_game)
5010 game_status = GAME_MODE_MAIN;
5013 DrawAndFadeInMainMenu(REDRAW_FIELD);
5021 if (!local_player->LevelSolved_SaveScore)
5024 FadeOut(REDRAW_FIELD);
5027 game_status = GAME_MODE_MAIN;
5029 DrawAndFadeInMainMenu(REDRAW_FIELD);
5034 if (level_nr == leveldir_current->handicap_level)
5036 leveldir_current->handicap_level++;
5038 SaveLevelSetup_SeriesInfo();
5041 if (level_nr < leveldir_current->last_level)
5042 raise_level = TRUE; /* advance to next level */
5044 if ((hi_pos = NewHiScore()) >= 0)
5046 game_status = GAME_MODE_SCORES;
5048 DrawHallOfFame(hi_pos);
5059 FadeOut(REDRAW_FIELD);
5062 game_status = GAME_MODE_MAIN;
5070 DrawAndFadeInMainMenu(REDRAW_FIELD);
5079 LoadScore(level_nr);
5081 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5082 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5085 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5087 if (local_player->score_final > highscore[k].Score)
5089 /* player has made it to the hall of fame */
5091 if (k < MAX_SCORE_ENTRIES - 1)
5093 int m = MAX_SCORE_ENTRIES - 1;
5096 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5097 if (strEqual(setup.player_name, highscore[l].Name))
5099 if (m == k) /* player's new highscore overwrites his old one */
5103 for (l = m; l > k; l--)
5105 strcpy(highscore[l].Name, highscore[l - 1].Name);
5106 highscore[l].Score = highscore[l - 1].Score;
5113 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5114 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5115 highscore[k].Score = local_player->score_final;
5121 else if (!strncmp(setup.player_name, highscore[k].Name,
5122 MAX_PLAYER_NAME_LEN))
5123 break; /* player already there with a higher score */
5129 SaveScore(level_nr);
5134 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5136 int element = Feld[x][y];
5137 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5138 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5139 int horiz_move = (dx != 0);
5140 int sign = (horiz_move ? dx : dy);
5141 int step = sign * element_info[element].move_stepsize;
5143 /* special values for move stepsize for spring and things on conveyor belt */
5146 if (CAN_FALL(element) &&
5147 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5148 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5149 else if (element == EL_SPRING)
5150 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5156 inline static int getElementMoveStepsize(int x, int y)
5158 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5161 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5163 if (player->GfxAction != action || player->GfxDir != dir)
5166 printf("Player frame reset! (%d => %d, %d => %d)\n",
5167 player->GfxAction, action, player->GfxDir, dir);
5170 player->GfxAction = action;
5171 player->GfxDir = dir;
5173 player->StepFrame = 0;
5177 #if USE_GFX_RESET_GFX_ANIMATION
5178 static void ResetGfxFrame(int x, int y, boolean redraw)
5180 int element = Feld[x][y];
5181 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5182 int last_gfx_frame = GfxFrame[x][y];
5184 if (graphic_info[graphic].anim_global_sync)
5185 GfxFrame[x][y] = FrameCounter;
5186 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5187 GfxFrame[x][y] = CustomValue[x][y];
5188 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5189 GfxFrame[x][y] = element_info[element].collect_score;
5190 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5191 GfxFrame[x][y] = ChangeDelay[x][y];
5193 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5194 DrawLevelGraphicAnimation(x, y, graphic);
5198 static void ResetGfxAnimation(int x, int y)
5200 GfxAction[x][y] = ACTION_DEFAULT;
5201 GfxDir[x][y] = MovDir[x][y];
5204 #if USE_GFX_RESET_GFX_ANIMATION
5205 ResetGfxFrame(x, y, FALSE);
5209 static void ResetRandomAnimationValue(int x, int y)
5211 GfxRandom[x][y] = INIT_GFX_RANDOM();
5214 void InitMovingField(int x, int y, int direction)
5216 int element = Feld[x][y];
5217 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5218 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5221 boolean is_moving_before, is_moving_after;
5223 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5226 /* check if element was/is moving or being moved before/after mode change */
5229 is_moving_before = (WasJustMoving[x][y] != 0);
5231 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5232 is_moving_before = WasJustMoving[x][y];
5235 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5237 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5239 /* reset animation only for moving elements which change direction of moving
5240 or which just started or stopped moving
5241 (else CEs with property "can move" / "not moving" are reset each frame) */
5242 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5244 if (is_moving_before != is_moving_after ||
5245 direction != MovDir[x][y])
5246 ResetGfxAnimation(x, y);
5248 if ((is_moving_before || is_moving_after) && !continues_moving)
5249 ResetGfxAnimation(x, y);
5252 if (!continues_moving)
5253 ResetGfxAnimation(x, y);
5256 MovDir[x][y] = direction;
5257 GfxDir[x][y] = direction;
5259 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5260 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5261 direction == MV_DOWN && CAN_FALL(element) ?
5262 ACTION_FALLING : ACTION_MOVING);
5264 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5265 ACTION_FALLING : ACTION_MOVING);
5268 /* this is needed for CEs with property "can move" / "not moving" */
5270 if (is_moving_after)
5272 if (Feld[newx][newy] == EL_EMPTY)
5273 Feld[newx][newy] = EL_BLOCKED;
5275 MovDir[newx][newy] = MovDir[x][y];
5277 #if USE_NEW_CUSTOM_VALUE
5278 CustomValue[newx][newy] = CustomValue[x][y];
5281 GfxFrame[newx][newy] = GfxFrame[x][y];
5282 GfxRandom[newx][newy] = GfxRandom[x][y];
5283 GfxAction[newx][newy] = GfxAction[x][y];
5284 GfxDir[newx][newy] = GfxDir[x][y];
5288 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5290 int direction = MovDir[x][y];
5291 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5292 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5298 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5300 int oldx = x, oldy = y;
5301 int direction = MovDir[x][y];
5303 if (direction == MV_LEFT)
5305 else if (direction == MV_RIGHT)
5307 else if (direction == MV_UP)
5309 else if (direction == MV_DOWN)
5312 *comes_from_x = oldx;
5313 *comes_from_y = oldy;
5316 int MovingOrBlocked2Element(int x, int y)
5318 int element = Feld[x][y];
5320 if (element == EL_BLOCKED)
5324 Blocked2Moving(x, y, &oldx, &oldy);
5325 return Feld[oldx][oldy];
5331 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5333 /* like MovingOrBlocked2Element(), but if element is moving
5334 and (x,y) is the field the moving element is just leaving,
5335 return EL_BLOCKED instead of the element value */
5336 int element = Feld[x][y];
5338 if (IS_MOVING(x, y))
5340 if (element == EL_BLOCKED)
5344 Blocked2Moving(x, y, &oldx, &oldy);
5345 return Feld[oldx][oldy];
5354 static void RemoveField(int x, int y)
5356 Feld[x][y] = EL_EMPTY;
5362 #if USE_NEW_CUSTOM_VALUE
5363 CustomValue[x][y] = 0;
5367 ChangeDelay[x][y] = 0;
5368 ChangePage[x][y] = -1;
5369 Pushed[x][y] = FALSE;
5372 ExplodeField[x][y] = EX_TYPE_NONE;
5375 GfxElement[x][y] = EL_UNDEFINED;
5376 GfxAction[x][y] = ACTION_DEFAULT;
5377 GfxDir[x][y] = MV_NONE;
5379 /* !!! this would prevent the removed tile from being redrawn !!! */
5380 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5384 void RemoveMovingField(int x, int y)
5386 int oldx = x, oldy = y, newx = x, newy = y;
5387 int element = Feld[x][y];
5388 int next_element = EL_UNDEFINED;
5390 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5393 if (IS_MOVING(x, y))
5395 Moving2Blocked(x, y, &newx, &newy);
5397 if (Feld[newx][newy] != EL_BLOCKED)
5399 /* element is moving, but target field is not free (blocked), but
5400 already occupied by something different (example: acid pool);
5401 in this case, only remove the moving field, but not the target */
5403 RemoveField(oldx, oldy);
5405 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5407 TEST_DrawLevelField(oldx, oldy);
5412 else if (element == EL_BLOCKED)
5414 Blocked2Moving(x, y, &oldx, &oldy);
5415 if (!IS_MOVING(oldx, oldy))
5419 if (element == EL_BLOCKED &&
5420 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5421 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5422 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5423 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5424 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5425 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5426 next_element = get_next_element(Feld[oldx][oldy]);
5428 RemoveField(oldx, oldy);
5429 RemoveField(newx, newy);
5431 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5433 if (next_element != EL_UNDEFINED)
5434 Feld[oldx][oldy] = next_element;
5436 TEST_DrawLevelField(oldx, oldy);
5437 TEST_DrawLevelField(newx, newy);
5440 void DrawDynamite(int x, int y)
5442 int sx = SCREENX(x), sy = SCREENY(y);
5443 int graphic = el2img(Feld[x][y]);
5446 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5449 if (IS_WALKABLE_INSIDE(Back[x][y]))
5453 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5454 else if (Store[x][y])
5455 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5457 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5459 if (Back[x][y] || Store[x][y])
5460 DrawGraphicThruMask(sx, sy, graphic, frame);
5462 DrawGraphic(sx, sy, graphic, frame);
5465 void CheckDynamite(int x, int y)
5467 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5471 if (MovDelay[x][y] != 0)
5474 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5480 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5485 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5487 boolean num_checked_players = 0;
5490 for (i = 0; i < MAX_PLAYERS; i++)
5492 if (stored_player[i].active)
5494 int sx = stored_player[i].jx;
5495 int sy = stored_player[i].jy;
5497 if (num_checked_players == 0)
5504 *sx1 = MIN(*sx1, sx);
5505 *sy1 = MIN(*sy1, sy);
5506 *sx2 = MAX(*sx2, sx);
5507 *sy2 = MAX(*sy2, sy);
5510 num_checked_players++;
5515 static boolean checkIfAllPlayersFitToScreen_RND()
5517 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5519 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5521 return (sx2 - sx1 < SCR_FIELDX &&
5522 sy2 - sy1 < SCR_FIELDY);
5525 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5527 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5529 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5531 *sx = (sx1 + sx2) / 2;
5532 *sy = (sy1 + sy2) / 2;
5535 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5536 boolean center_screen, boolean quick_relocation)
5538 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5539 boolean no_delay = (tape.warp_forward);
5540 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5541 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5543 if (quick_relocation)
5545 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5547 if (!level.shifted_relocation || center_screen)
5549 /* quick relocation (without scrolling), with centering of screen */
5551 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5552 x > SBX_Right + MIDPOSX ? SBX_Right :
5555 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5556 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5561 /* quick relocation (without scrolling), but do not center screen */
5563 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5564 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5567 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5568 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5571 int offset_x = x + (scroll_x - center_scroll_x);
5572 int offset_y = y + (scroll_y - center_scroll_y);
5574 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5575 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5576 offset_x - MIDPOSX);
5578 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5579 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5580 offset_y - MIDPOSY);
5586 if (!level.shifted_relocation || center_screen)
5588 /* quick relocation (without scrolling), with centering of screen */
5590 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5591 x > SBX_Right + MIDPOSX ? SBX_Right :
5594 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5595 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5600 /* quick relocation (without scrolling), but do not center screen */
5602 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5603 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5606 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5607 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5610 int offset_x = x + (scroll_x - center_scroll_x);
5611 int offset_y = y + (scroll_y - center_scroll_y);
5613 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5614 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5615 offset_x - MIDPOSX);
5617 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5618 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5619 offset_y - MIDPOSY);
5622 /* quick relocation (without scrolling), inside visible screen area */
5624 int offset = game.scroll_delay_value;
5626 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5627 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5628 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5630 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5631 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5632 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5634 /* don't scroll over playfield boundaries */
5635 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5636 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5638 /* don't scroll over playfield boundaries */
5639 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5640 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5644 RedrawPlayfield(TRUE, 0,0,0,0);
5649 int scroll_xx, scroll_yy;
5651 if (!level.shifted_relocation || center_screen)
5653 /* visible relocation (with scrolling), with centering of screen */
5655 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5656 x > SBX_Right + MIDPOSX ? SBX_Right :
5659 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5660 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5665 /* visible relocation (with scrolling), but do not center screen */
5667 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5668 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5671 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5672 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5675 int offset_x = x + (scroll_x - center_scroll_x);
5676 int offset_y = y + (scroll_y - center_scroll_y);
5678 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5679 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5680 offset_x - MIDPOSX);
5682 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5683 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5684 offset_y - MIDPOSY);
5689 /* visible relocation (with scrolling), with centering of screen */
5691 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5692 x > SBX_Right + MIDPOSX ? SBX_Right :
5695 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5696 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5700 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5702 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5705 int fx = FX, fy = FY;
5707 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5708 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5710 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5716 fx += dx * TILEX / 2;
5717 fy += dy * TILEY / 2;
5719 ScrollLevel(dx, dy);
5722 /* scroll in two steps of half tile size to make things smoother */
5723 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5725 Delay(wait_delay_value);
5727 /* scroll second step to align at full tile size */
5729 Delay(wait_delay_value);
5734 Delay(wait_delay_value);
5738 void RelocatePlayer(int jx, int jy, int el_player_raw)
5740 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5741 int player_nr = GET_PLAYER_NR(el_player);
5742 struct PlayerInfo *player = &stored_player[player_nr];
5743 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5744 boolean no_delay = (tape.warp_forward);
5745 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5746 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5747 int old_jx = player->jx;
5748 int old_jy = player->jy;
5749 int old_element = Feld[old_jx][old_jy];
5750 int element = Feld[jx][jy];
5751 boolean player_relocated = (old_jx != jx || old_jy != jy);
5753 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5754 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5755 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5756 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5757 int leave_side_horiz = move_dir_horiz;
5758 int leave_side_vert = move_dir_vert;
5759 int enter_side = enter_side_horiz | enter_side_vert;
5760 int leave_side = leave_side_horiz | leave_side_vert;
5762 if (player->GameOver) /* do not reanimate dead player */
5765 if (!player_relocated) /* no need to relocate the player */
5768 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5770 RemoveField(jx, jy); /* temporarily remove newly placed player */
5771 DrawLevelField(jx, jy);
5774 if (player->present)
5776 while (player->MovPos)
5778 ScrollPlayer(player, SCROLL_GO_ON);
5779 ScrollScreen(NULL, SCROLL_GO_ON);
5781 AdvanceFrameAndPlayerCounters(player->index_nr);
5786 Delay(wait_delay_value);
5789 DrawPlayer(player); /* needed here only to cleanup last field */
5790 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5792 player->is_moving = FALSE;
5795 if (IS_CUSTOM_ELEMENT(old_element))
5796 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5798 player->index_bit, leave_side);
5800 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5802 player->index_bit, leave_side);
5804 Feld[jx][jy] = el_player;
5805 InitPlayerField(jx, jy, el_player, TRUE);
5807 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5808 possible that the relocation target field did not contain a player element,
5809 but a walkable element, to which the new player was relocated -- in this
5810 case, restore that (already initialized!) element on the player field */
5811 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5813 Feld[jx][jy] = element; /* restore previously existing element */
5815 /* !!! do not initialize already initialized element a second time !!! */
5816 /* (this causes at least problems with "element creation" CE trigger for
5817 already existing elements, and existing Sokoban fields counted twice) */
5818 InitField(jx, jy, FALSE);
5822 /* only visually relocate centered player */
5823 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5824 FALSE, level.instant_relocation);
5826 TestIfPlayerTouchesBadThing(jx, jy);
5827 TestIfPlayerTouchesCustomElement(jx, jy);
5829 if (IS_CUSTOM_ELEMENT(element))
5830 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5831 player->index_bit, enter_side);
5833 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5834 player->index_bit, enter_side);
5837 if (player->is_switching)
5839 /* ensure that relocation while still switching an element does not cause
5840 a new element to be treated as also switched directly after relocation
5841 (this is important for teleporter switches that teleport the player to
5842 a place where another teleporter switch is in the same direction, which
5843 would then incorrectly be treated as immediately switched before the
5844 direction key that caused the switch was released) */
5846 player->switch_x += jx - old_jx;
5847 player->switch_y += jy - old_jy;
5852 void Explode(int ex, int ey, int phase, int mode)
5858 /* !!! eliminate this variable !!! */
5859 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5861 if (game.explosions_delayed)
5863 ExplodeField[ex][ey] = mode;
5867 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5869 int center_element = Feld[ex][ey];
5870 int artwork_element, explosion_element; /* set these values later */
5873 /* --- This is only really needed (and now handled) in "Impact()". --- */
5874 /* do not explode moving elements that left the explode field in time */
5875 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5876 center_element == EL_EMPTY &&
5877 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5882 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5883 if (mode == EX_TYPE_NORMAL ||
5884 mode == EX_TYPE_CENTER ||
5885 mode == EX_TYPE_CROSS)
5886 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5889 /* remove things displayed in background while burning dynamite */
5890 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5893 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5895 /* put moving element to center field (and let it explode there) */
5896 center_element = MovingOrBlocked2Element(ex, ey);
5897 RemoveMovingField(ex, ey);
5898 Feld[ex][ey] = center_element;
5901 /* now "center_element" is finally determined -- set related values now */
5902 artwork_element = center_element; /* for custom player artwork */
5903 explosion_element = center_element; /* for custom player artwork */
5905 if (IS_PLAYER(ex, ey))
5907 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5909 artwork_element = stored_player[player_nr].artwork_element;
5911 if (level.use_explosion_element[player_nr])
5913 explosion_element = level.explosion_element[player_nr];
5914 artwork_element = explosion_element;
5919 if (mode == EX_TYPE_NORMAL ||
5920 mode == EX_TYPE_CENTER ||
5921 mode == EX_TYPE_CROSS)
5922 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5925 last_phase = element_info[explosion_element].explosion_delay + 1;
5927 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5929 int xx = x - ex + 1;
5930 int yy = y - ey + 1;
5933 if (!IN_LEV_FIELD(x, y) ||
5934 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5935 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5938 element = Feld[x][y];
5940 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5942 element = MovingOrBlocked2Element(x, y);
5944 if (!IS_EXPLOSION_PROOF(element))
5945 RemoveMovingField(x, y);
5948 /* indestructible elements can only explode in center (but not flames) */
5949 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5950 mode == EX_TYPE_BORDER)) ||
5951 element == EL_FLAMES)
5954 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5955 behaviour, for example when touching a yamyam that explodes to rocks
5956 with active deadly shield, a rock is created under the player !!! */
5957 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5959 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5960 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5961 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5963 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5966 if (IS_ACTIVE_BOMB(element))
5968 /* re-activate things under the bomb like gate or penguin */
5969 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5976 /* save walkable background elements while explosion on same tile */
5977 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5978 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5979 Back[x][y] = element;
5981 /* ignite explodable elements reached by other explosion */
5982 if (element == EL_EXPLOSION)
5983 element = Store2[x][y];
5985 if (AmoebaNr[x][y] &&
5986 (element == EL_AMOEBA_FULL ||
5987 element == EL_BD_AMOEBA ||
5988 element == EL_AMOEBA_GROWING))
5990 AmoebaCnt[AmoebaNr[x][y]]--;
5991 AmoebaCnt2[AmoebaNr[x][y]]--;
5996 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5998 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6000 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6002 if (PLAYERINFO(ex, ey)->use_murphy)
6003 Store[x][y] = EL_EMPTY;
6006 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6007 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6008 else if (ELEM_IS_PLAYER(center_element))
6009 Store[x][y] = EL_EMPTY;
6010 else if (center_element == EL_YAMYAM)
6011 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6012 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6013 Store[x][y] = element_info[center_element].content.e[xx][yy];
6015 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6016 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6017 otherwise) -- FIX THIS !!! */
6018 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6019 Store[x][y] = element_info[element].content.e[1][1];
6021 else if (!CAN_EXPLODE(element))
6022 Store[x][y] = element_info[element].content.e[1][1];
6025 Store[x][y] = EL_EMPTY;
6027 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6028 center_element == EL_AMOEBA_TO_DIAMOND)
6029 Store2[x][y] = element;
6031 Feld[x][y] = EL_EXPLOSION;
6032 GfxElement[x][y] = artwork_element;
6034 ExplodePhase[x][y] = 1;
6035 ExplodeDelay[x][y] = last_phase;
6040 if (center_element == EL_YAMYAM)
6041 game.yamyam_content_nr =
6042 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6054 GfxFrame[x][y] = 0; /* restart explosion animation */
6056 last_phase = ExplodeDelay[x][y];
6058 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6062 /* activate this even in non-DEBUG version until cause for crash in
6063 getGraphicAnimationFrame() (see below) is found and eliminated */
6069 /* this can happen if the player leaves an explosion just in time */
6070 if (GfxElement[x][y] == EL_UNDEFINED)
6071 GfxElement[x][y] = EL_EMPTY;
6073 if (GfxElement[x][y] == EL_UNDEFINED)
6076 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6077 printf("Explode(): This should never happen!\n");
6080 GfxElement[x][y] = EL_EMPTY;
6086 border_element = Store2[x][y];
6087 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6088 border_element = StorePlayer[x][y];
6090 if (phase == element_info[border_element].ignition_delay ||
6091 phase == last_phase)
6093 boolean border_explosion = FALSE;
6095 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6096 !PLAYER_EXPLOSION_PROTECTED(x, y))
6098 KillPlayerUnlessExplosionProtected(x, y);
6099 border_explosion = TRUE;
6101 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6103 Feld[x][y] = Store2[x][y];
6106 border_explosion = TRUE;
6108 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6110 AmoebeUmwandeln(x, y);
6112 border_explosion = TRUE;
6115 /* if an element just explodes due to another explosion (chain-reaction),
6116 do not immediately end the new explosion when it was the last frame of
6117 the explosion (as it would be done in the following "if"-statement!) */
6118 if (border_explosion && phase == last_phase)
6122 if (phase == last_phase)
6126 element = Feld[x][y] = Store[x][y];
6127 Store[x][y] = Store2[x][y] = 0;
6128 GfxElement[x][y] = EL_UNDEFINED;
6130 /* player can escape from explosions and might therefore be still alive */
6131 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6132 element <= EL_PLAYER_IS_EXPLODING_4)
6134 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6135 int explosion_element = EL_PLAYER_1 + player_nr;
6136 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6137 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6139 if (level.use_explosion_element[player_nr])
6140 explosion_element = level.explosion_element[player_nr];
6142 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6143 element_info[explosion_element].content.e[xx][yy]);
6146 /* restore probably existing indestructible background element */
6147 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6148 element = Feld[x][y] = Back[x][y];
6151 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6152 GfxDir[x][y] = MV_NONE;
6153 ChangeDelay[x][y] = 0;
6154 ChangePage[x][y] = -1;
6156 #if USE_NEW_CUSTOM_VALUE
6157 CustomValue[x][y] = 0;
6160 InitField_WithBug2(x, y, FALSE);
6162 TEST_DrawLevelField(x, y);
6164 TestIfElementTouchesCustomElement(x, y);
6166 if (GFX_CRUMBLED(element))
6167 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6169 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6170 StorePlayer[x][y] = 0;
6172 if (ELEM_IS_PLAYER(element))
6173 RelocatePlayer(x, y, element);
6175 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6177 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6178 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6181 TEST_DrawLevelFieldCrumbled(x, y);
6183 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6185 DrawLevelElement(x, y, Back[x][y]);
6186 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6188 else if (IS_WALKABLE_UNDER(Back[x][y]))
6190 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6191 DrawLevelElementThruMask(x, y, Back[x][y]);
6193 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6194 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6198 void DynaExplode(int ex, int ey)
6201 int dynabomb_element = Feld[ex][ey];
6202 int dynabomb_size = 1;
6203 boolean dynabomb_xl = FALSE;
6204 struct PlayerInfo *player;
6205 static int xy[4][2] =
6213 if (IS_ACTIVE_BOMB(dynabomb_element))
6215 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6216 dynabomb_size = player->dynabomb_size;
6217 dynabomb_xl = player->dynabomb_xl;
6218 player->dynabombs_left++;
6221 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6223 for (i = 0; i < NUM_DIRECTIONS; i++)
6225 for (j = 1; j <= dynabomb_size; j++)
6227 int x = ex + j * xy[i][0];
6228 int y = ey + j * xy[i][1];
6231 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6234 element = Feld[x][y];
6236 /* do not restart explosions of fields with active bombs */
6237 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6240 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6242 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6243 !IS_DIGGABLE(element) && !dynabomb_xl)
6249 void Bang(int x, int y)
6251 int element = MovingOrBlocked2Element(x, y);
6252 int explosion_type = EX_TYPE_NORMAL;
6254 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6256 struct PlayerInfo *player = PLAYERINFO(x, y);
6258 #if USE_FIX_CE_ACTION_WITH_PLAYER
6259 element = Feld[x][y] = player->initial_element;
6261 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6262 player->element_nr);
6265 if (level.use_explosion_element[player->index_nr])
6267 int explosion_element = level.explosion_element[player->index_nr];
6269 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6270 explosion_type = EX_TYPE_CROSS;
6271 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6272 explosion_type = EX_TYPE_CENTER;
6280 case EL_BD_BUTTERFLY:
6283 case EL_DARK_YAMYAM:
6287 RaiseScoreElement(element);
6290 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6291 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6292 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6293 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6294 case EL_DYNABOMB_INCREASE_NUMBER:
6295 case EL_DYNABOMB_INCREASE_SIZE:
6296 case EL_DYNABOMB_INCREASE_POWER:
6297 explosion_type = EX_TYPE_DYNA;
6300 case EL_DC_LANDMINE:
6302 case EL_EM_EXIT_OPEN:
6303 case EL_EM_STEEL_EXIT_OPEN:
6305 explosion_type = EX_TYPE_CENTER;
6310 case EL_LAMP_ACTIVE:
6311 case EL_AMOEBA_TO_DIAMOND:
6312 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6313 explosion_type = EX_TYPE_CENTER;
6317 if (element_info[element].explosion_type == EXPLODES_CROSS)
6318 explosion_type = EX_TYPE_CROSS;
6319 else if (element_info[element].explosion_type == EXPLODES_1X1)
6320 explosion_type = EX_TYPE_CENTER;
6324 if (explosion_type == EX_TYPE_DYNA)
6327 Explode(x, y, EX_PHASE_START, explosion_type);
6329 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6332 void SplashAcid(int x, int y)
6334 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6335 (!IN_LEV_FIELD(x - 1, y - 2) ||
6336 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6337 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6339 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6340 (!IN_LEV_FIELD(x + 1, y - 2) ||
6341 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6342 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6344 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6347 static void InitBeltMovement()
6349 static int belt_base_element[4] =
6351 EL_CONVEYOR_BELT_1_LEFT,
6352 EL_CONVEYOR_BELT_2_LEFT,
6353 EL_CONVEYOR_BELT_3_LEFT,
6354 EL_CONVEYOR_BELT_4_LEFT
6356 static int belt_base_active_element[4] =
6358 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6359 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6360 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6361 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6366 /* set frame order for belt animation graphic according to belt direction */
6367 for (i = 0; i < NUM_BELTS; i++)
6371 for (j = 0; j < NUM_BELT_PARTS; j++)
6373 int element = belt_base_active_element[belt_nr] + j;
6374 int graphic_1 = el2img(element);
6375 int graphic_2 = el2panelimg(element);
6377 if (game.belt_dir[i] == MV_LEFT)
6379 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6380 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6384 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6385 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6390 SCAN_PLAYFIELD(x, y)
6392 int element = Feld[x][y];
6394 for (i = 0; i < NUM_BELTS; i++)
6396 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6398 int e_belt_nr = getBeltNrFromBeltElement(element);
6401 if (e_belt_nr == belt_nr)
6403 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6405 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6412 static void ToggleBeltSwitch(int x, int y)
6414 static int belt_base_element[4] =
6416 EL_CONVEYOR_BELT_1_LEFT,
6417 EL_CONVEYOR_BELT_2_LEFT,
6418 EL_CONVEYOR_BELT_3_LEFT,
6419 EL_CONVEYOR_BELT_4_LEFT
6421 static int belt_base_active_element[4] =
6423 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6424 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6425 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6426 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6428 static int belt_base_switch_element[4] =
6430 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6431 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6432 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6433 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6435 static int belt_move_dir[4] =
6443 int element = Feld[x][y];
6444 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6445 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6446 int belt_dir = belt_move_dir[belt_dir_nr];
6449 if (!IS_BELT_SWITCH(element))
6452 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6453 game.belt_dir[belt_nr] = belt_dir;
6455 if (belt_dir_nr == 3)
6458 /* set frame order for belt animation graphic according to belt direction */
6459 for (i = 0; i < NUM_BELT_PARTS; i++)
6461 int element = belt_base_active_element[belt_nr] + i;
6462 int graphic_1 = el2img(element);
6463 int graphic_2 = el2panelimg(element);
6465 if (belt_dir == MV_LEFT)
6467 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6468 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6472 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6473 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6477 SCAN_PLAYFIELD(xx, yy)
6479 int element = Feld[xx][yy];
6481 if (IS_BELT_SWITCH(element))
6483 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6485 if (e_belt_nr == belt_nr)
6487 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6488 TEST_DrawLevelField(xx, yy);
6491 else if (IS_BELT(element) && belt_dir != MV_NONE)
6493 int e_belt_nr = getBeltNrFromBeltElement(element);
6495 if (e_belt_nr == belt_nr)
6497 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6499 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6500 TEST_DrawLevelField(xx, yy);
6503 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6505 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6507 if (e_belt_nr == belt_nr)
6509 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6511 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6512 TEST_DrawLevelField(xx, yy);
6518 static void ToggleSwitchgateSwitch(int x, int y)
6522 game.switchgate_pos = !game.switchgate_pos;
6524 SCAN_PLAYFIELD(xx, yy)
6526 int element = Feld[xx][yy];
6528 #if !USE_BOTH_SWITCHGATE_SWITCHES
6529 if (element == EL_SWITCHGATE_SWITCH_UP ||
6530 element == EL_SWITCHGATE_SWITCH_DOWN)
6532 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6533 TEST_DrawLevelField(xx, yy);
6535 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6536 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6538 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6539 TEST_DrawLevelField(xx, yy);
6542 if (element == EL_SWITCHGATE_SWITCH_UP)
6544 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6545 TEST_DrawLevelField(xx, yy);
6547 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6549 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6550 TEST_DrawLevelField(xx, yy);
6552 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6554 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6555 TEST_DrawLevelField(xx, yy);
6557 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6559 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6560 TEST_DrawLevelField(xx, yy);
6563 else if (element == EL_SWITCHGATE_OPEN ||
6564 element == EL_SWITCHGATE_OPENING)
6566 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6568 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6570 else if (element == EL_SWITCHGATE_CLOSED ||
6571 element == EL_SWITCHGATE_CLOSING)
6573 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6575 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6580 static int getInvisibleActiveFromInvisibleElement(int element)
6582 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6583 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6584 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6588 static int getInvisibleFromInvisibleActiveElement(int element)
6590 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6591 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6592 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6596 static void RedrawAllLightSwitchesAndInvisibleElements()
6600 SCAN_PLAYFIELD(x, y)
6602 int element = Feld[x][y];
6604 if (element == EL_LIGHT_SWITCH &&
6605 game.light_time_left > 0)
6607 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6608 TEST_DrawLevelField(x, y);
6610 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6611 game.light_time_left == 0)
6613 Feld[x][y] = EL_LIGHT_SWITCH;
6614 TEST_DrawLevelField(x, y);
6616 else if (element == EL_EMC_DRIPPER &&
6617 game.light_time_left > 0)
6619 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6620 TEST_DrawLevelField(x, y);
6622 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6623 game.light_time_left == 0)
6625 Feld[x][y] = EL_EMC_DRIPPER;
6626 TEST_DrawLevelField(x, y);
6628 else if (element == EL_INVISIBLE_STEELWALL ||
6629 element == EL_INVISIBLE_WALL ||
6630 element == EL_INVISIBLE_SAND)
6632 if (game.light_time_left > 0)
6633 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6635 TEST_DrawLevelField(x, y);
6637 /* uncrumble neighbour fields, if needed */
6638 if (element == EL_INVISIBLE_SAND)
6639 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6641 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6642 element == EL_INVISIBLE_WALL_ACTIVE ||
6643 element == EL_INVISIBLE_SAND_ACTIVE)
6645 if (game.light_time_left == 0)
6646 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6648 TEST_DrawLevelField(x, y);
6650 /* re-crumble neighbour fields, if needed */
6651 if (element == EL_INVISIBLE_SAND)
6652 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6657 static void RedrawAllInvisibleElementsForLenses()
6661 SCAN_PLAYFIELD(x, y)
6663 int element = Feld[x][y];
6665 if (element == EL_EMC_DRIPPER &&
6666 game.lenses_time_left > 0)
6668 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6669 TEST_DrawLevelField(x, y);
6671 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6672 game.lenses_time_left == 0)
6674 Feld[x][y] = EL_EMC_DRIPPER;
6675 TEST_DrawLevelField(x, y);
6677 else if (element == EL_INVISIBLE_STEELWALL ||
6678 element == EL_INVISIBLE_WALL ||
6679 element == EL_INVISIBLE_SAND)
6681 if (game.lenses_time_left > 0)
6682 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6684 TEST_DrawLevelField(x, y);
6686 /* uncrumble neighbour fields, if needed */
6687 if (element == EL_INVISIBLE_SAND)
6688 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6690 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6691 element == EL_INVISIBLE_WALL_ACTIVE ||
6692 element == EL_INVISIBLE_SAND_ACTIVE)
6694 if (game.lenses_time_left == 0)
6695 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6697 TEST_DrawLevelField(x, y);
6699 /* re-crumble neighbour fields, if needed */
6700 if (element == EL_INVISIBLE_SAND)
6701 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6706 static void RedrawAllInvisibleElementsForMagnifier()
6710 SCAN_PLAYFIELD(x, y)
6712 int element = Feld[x][y];
6714 if (element == EL_EMC_FAKE_GRASS &&
6715 game.magnify_time_left > 0)
6717 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6718 TEST_DrawLevelField(x, y);
6720 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6721 game.magnify_time_left == 0)
6723 Feld[x][y] = EL_EMC_FAKE_GRASS;
6724 TEST_DrawLevelField(x, y);
6726 else if (IS_GATE_GRAY(element) &&
6727 game.magnify_time_left > 0)
6729 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6730 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6731 IS_EM_GATE_GRAY(element) ?
6732 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6733 IS_EMC_GATE_GRAY(element) ?
6734 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6735 IS_DC_GATE_GRAY(element) ?
6736 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6738 TEST_DrawLevelField(x, y);
6740 else if (IS_GATE_GRAY_ACTIVE(element) &&
6741 game.magnify_time_left == 0)
6743 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6744 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6745 IS_EM_GATE_GRAY_ACTIVE(element) ?
6746 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6747 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6748 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6749 IS_DC_GATE_GRAY_ACTIVE(element) ?
6750 EL_DC_GATE_WHITE_GRAY :
6752 TEST_DrawLevelField(x, y);
6757 static void ToggleLightSwitch(int x, int y)
6759 int element = Feld[x][y];
6761 game.light_time_left =
6762 (element == EL_LIGHT_SWITCH ?
6763 level.time_light * FRAMES_PER_SECOND : 0);
6765 RedrawAllLightSwitchesAndInvisibleElements();
6768 static void ActivateTimegateSwitch(int x, int y)
6772 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6774 SCAN_PLAYFIELD(xx, yy)
6776 int element = Feld[xx][yy];
6778 if (element == EL_TIMEGATE_CLOSED ||
6779 element == EL_TIMEGATE_CLOSING)
6781 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6782 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6786 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6788 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6789 TEST_DrawLevelField(xx, yy);
6796 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6797 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6799 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6803 void Impact(int x, int y)
6805 boolean last_line = (y == lev_fieldy - 1);
6806 boolean object_hit = FALSE;
6807 boolean impact = (last_line || object_hit);
6808 int element = Feld[x][y];
6809 int smashed = EL_STEELWALL;
6811 if (!last_line) /* check if element below was hit */
6813 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6816 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6817 MovDir[x][y + 1] != MV_DOWN ||
6818 MovPos[x][y + 1] <= TILEY / 2));
6820 /* do not smash moving elements that left the smashed field in time */
6821 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6822 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6825 #if USE_QUICKSAND_IMPACT_BUGFIX
6826 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6828 RemoveMovingField(x, y + 1);
6829 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6830 Feld[x][y + 2] = EL_ROCK;
6831 TEST_DrawLevelField(x, y + 2);
6836 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6838 RemoveMovingField(x, y + 1);
6839 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6840 Feld[x][y + 2] = EL_ROCK;
6841 TEST_DrawLevelField(x, y + 2);
6848 smashed = MovingOrBlocked2Element(x, y + 1);
6850 impact = (last_line || object_hit);
6853 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6855 SplashAcid(x, y + 1);
6859 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6860 /* only reset graphic animation if graphic really changes after impact */
6862 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6864 ResetGfxAnimation(x, y);
6865 TEST_DrawLevelField(x, y);
6868 if (impact && CAN_EXPLODE_IMPACT(element))
6873 else if (impact && element == EL_PEARL &&
6874 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6876 ResetGfxAnimation(x, y);
6878 Feld[x][y] = EL_PEARL_BREAKING;
6879 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6882 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6884 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6889 if (impact && element == EL_AMOEBA_DROP)
6891 if (object_hit && IS_PLAYER(x, y + 1))
6892 KillPlayerUnlessEnemyProtected(x, y + 1);
6893 else if (object_hit && smashed == EL_PENGUIN)
6897 Feld[x][y] = EL_AMOEBA_GROWING;
6898 Store[x][y] = EL_AMOEBA_WET;
6900 ResetRandomAnimationValue(x, y);
6905 if (object_hit) /* check which object was hit */
6907 if ((CAN_PASS_MAGIC_WALL(element) &&
6908 (smashed == EL_MAGIC_WALL ||
6909 smashed == EL_BD_MAGIC_WALL)) ||
6910 (CAN_PASS_DC_MAGIC_WALL(element) &&
6911 smashed == EL_DC_MAGIC_WALL))
6914 int activated_magic_wall =
6915 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6916 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6917 EL_DC_MAGIC_WALL_ACTIVE);
6919 /* activate magic wall / mill */
6920 SCAN_PLAYFIELD(xx, yy)
6922 if (Feld[xx][yy] == smashed)
6923 Feld[xx][yy] = activated_magic_wall;
6926 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6927 game.magic_wall_active = TRUE;
6929 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6930 SND_MAGIC_WALL_ACTIVATING :
6931 smashed == EL_BD_MAGIC_WALL ?
6932 SND_BD_MAGIC_WALL_ACTIVATING :
6933 SND_DC_MAGIC_WALL_ACTIVATING));
6936 if (IS_PLAYER(x, y + 1))
6938 if (CAN_SMASH_PLAYER(element))
6940 KillPlayerUnlessEnemyProtected(x, y + 1);
6944 else if (smashed == EL_PENGUIN)
6946 if (CAN_SMASH_PLAYER(element))
6952 else if (element == EL_BD_DIAMOND)
6954 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6960 else if (((element == EL_SP_INFOTRON ||
6961 element == EL_SP_ZONK) &&
6962 (smashed == EL_SP_SNIKSNAK ||
6963 smashed == EL_SP_ELECTRON ||
6964 smashed == EL_SP_DISK_ORANGE)) ||
6965 (element == EL_SP_INFOTRON &&
6966 smashed == EL_SP_DISK_YELLOW))
6971 else if (CAN_SMASH_EVERYTHING(element))
6973 if (IS_CLASSIC_ENEMY(smashed) ||
6974 CAN_EXPLODE_SMASHED(smashed))
6979 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6981 if (smashed == EL_LAMP ||
6982 smashed == EL_LAMP_ACTIVE)
6987 else if (smashed == EL_NUT)
6989 Feld[x][y + 1] = EL_NUT_BREAKING;
6990 PlayLevelSound(x, y, SND_NUT_BREAKING);
6991 RaiseScoreElement(EL_NUT);
6994 else if (smashed == EL_PEARL)
6996 ResetGfxAnimation(x, y);
6998 Feld[x][y + 1] = EL_PEARL_BREAKING;
6999 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7002 else if (smashed == EL_DIAMOND)
7004 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7005 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7008 else if (IS_BELT_SWITCH(smashed))
7010 ToggleBeltSwitch(x, y + 1);
7012 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7013 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7014 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7015 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7017 ToggleSwitchgateSwitch(x, y + 1);
7019 else if (smashed == EL_LIGHT_SWITCH ||
7020 smashed == EL_LIGHT_SWITCH_ACTIVE)
7022 ToggleLightSwitch(x, y + 1);
7027 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7030 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7032 CheckElementChangeBySide(x, y + 1, smashed, element,
7033 CE_SWITCHED, CH_SIDE_TOP);
7034 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7040 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7045 /* play sound of magic wall / mill */
7047 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7048 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7049 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7051 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7052 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7053 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7054 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7055 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7056 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7061 /* play sound of object that hits the ground */
7062 if (last_line || object_hit)
7063 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7066 inline static void TurnRoundExt(int x, int y)
7078 { 0, 0 }, { 0, 0 }, { 0, 0 },
7083 int left, right, back;
7087 { MV_DOWN, MV_UP, MV_RIGHT },
7088 { MV_UP, MV_DOWN, MV_LEFT },
7090 { MV_LEFT, MV_RIGHT, MV_DOWN },
7094 { MV_RIGHT, MV_LEFT, MV_UP }
7097 int element = Feld[x][y];
7098 int move_pattern = element_info[element].move_pattern;
7100 int old_move_dir = MovDir[x][y];
7101 int left_dir = turn[old_move_dir].left;
7102 int right_dir = turn[old_move_dir].right;
7103 int back_dir = turn[old_move_dir].back;
7105 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7106 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7107 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7108 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7110 int left_x = x + left_dx, left_y = y + left_dy;
7111 int right_x = x + right_dx, right_y = y + right_dy;
7112 int move_x = x + move_dx, move_y = y + move_dy;
7116 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7118 TestIfBadThingTouchesOtherBadThing(x, y);
7120 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7121 MovDir[x][y] = right_dir;
7122 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7123 MovDir[x][y] = left_dir;
7125 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7127 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7130 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7132 TestIfBadThingTouchesOtherBadThing(x, y);
7134 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7135 MovDir[x][y] = left_dir;
7136 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7137 MovDir[x][y] = right_dir;
7139 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7141 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7144 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7146 TestIfBadThingTouchesOtherBadThing(x, y);
7148 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7149 MovDir[x][y] = left_dir;
7150 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7151 MovDir[x][y] = right_dir;
7153 if (MovDir[x][y] != old_move_dir)
7156 else if (element == EL_YAMYAM)
7158 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7159 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7161 if (can_turn_left && can_turn_right)
7162 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7163 else if (can_turn_left)
7164 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7165 else if (can_turn_right)
7166 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7168 MovDir[x][y] = back_dir;
7170 MovDelay[x][y] = 16 + 16 * RND(3);
7172 else if (element == EL_DARK_YAMYAM)
7174 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7176 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7179 if (can_turn_left && can_turn_right)
7180 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7181 else if (can_turn_left)
7182 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7183 else if (can_turn_right)
7184 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7186 MovDir[x][y] = back_dir;
7188 MovDelay[x][y] = 16 + 16 * RND(3);
7190 else if (element == EL_PACMAN)
7192 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7193 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7195 if (can_turn_left && can_turn_right)
7196 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7197 else if (can_turn_left)
7198 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7199 else if (can_turn_right)
7200 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7202 MovDir[x][y] = back_dir;
7204 MovDelay[x][y] = 6 + RND(40);
7206 else if (element == EL_PIG)
7208 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7209 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7210 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7211 boolean should_turn_left, should_turn_right, should_move_on;
7213 int rnd = RND(rnd_value);
7215 should_turn_left = (can_turn_left &&
7217 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7218 y + back_dy + left_dy)));
7219 should_turn_right = (can_turn_right &&
7221 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7222 y + back_dy + right_dy)));
7223 should_move_on = (can_move_on &&
7226 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7227 y + move_dy + left_dy) ||
7228 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7229 y + move_dy + right_dy)));
7231 if (should_turn_left || should_turn_right || should_move_on)
7233 if (should_turn_left && should_turn_right && should_move_on)
7234 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7235 rnd < 2 * rnd_value / 3 ? right_dir :
7237 else if (should_turn_left && should_turn_right)
7238 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7239 else if (should_turn_left && should_move_on)
7240 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7241 else if (should_turn_right && should_move_on)
7242 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7243 else if (should_turn_left)
7244 MovDir[x][y] = left_dir;
7245 else if (should_turn_right)
7246 MovDir[x][y] = right_dir;
7247 else if (should_move_on)
7248 MovDir[x][y] = old_move_dir;
7250 else if (can_move_on && rnd > rnd_value / 8)
7251 MovDir[x][y] = old_move_dir;
7252 else if (can_turn_left && can_turn_right)
7253 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7254 else if (can_turn_left && rnd > rnd_value / 8)
7255 MovDir[x][y] = left_dir;
7256 else if (can_turn_right && rnd > rnd_value/8)
7257 MovDir[x][y] = right_dir;
7259 MovDir[x][y] = back_dir;
7261 xx = x + move_xy[MovDir[x][y]].dx;
7262 yy = y + move_xy[MovDir[x][y]].dy;
7264 if (!IN_LEV_FIELD(xx, yy) ||
7265 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7266 MovDir[x][y] = old_move_dir;
7270 else if (element == EL_DRAGON)
7272 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7273 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7274 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7276 int rnd = RND(rnd_value);
7278 if (can_move_on && rnd > rnd_value / 8)
7279 MovDir[x][y] = old_move_dir;
7280 else if (can_turn_left && can_turn_right)
7281 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7282 else if (can_turn_left && rnd > rnd_value / 8)
7283 MovDir[x][y] = left_dir;
7284 else if (can_turn_right && rnd > rnd_value / 8)
7285 MovDir[x][y] = right_dir;
7287 MovDir[x][y] = back_dir;
7289 xx = x + move_xy[MovDir[x][y]].dx;
7290 yy = y + move_xy[MovDir[x][y]].dy;
7292 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7293 MovDir[x][y] = old_move_dir;
7297 else if (element == EL_MOLE)
7299 boolean can_move_on =
7300 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7301 IS_AMOEBOID(Feld[move_x][move_y]) ||
7302 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7305 boolean can_turn_left =
7306 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7307 IS_AMOEBOID(Feld[left_x][left_y])));
7309 boolean can_turn_right =
7310 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7311 IS_AMOEBOID(Feld[right_x][right_y])));
7313 if (can_turn_left && can_turn_right)
7314 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7315 else if (can_turn_left)
7316 MovDir[x][y] = left_dir;
7318 MovDir[x][y] = right_dir;
7321 if (MovDir[x][y] != old_move_dir)
7324 else if (element == EL_BALLOON)
7326 MovDir[x][y] = game.wind_direction;
7329 else if (element == EL_SPRING)
7331 #if USE_NEW_SPRING_BUMPER
7332 if (MovDir[x][y] & MV_HORIZONTAL)
7334 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7335 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7337 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7338 ResetGfxAnimation(move_x, move_y);
7339 TEST_DrawLevelField(move_x, move_y);
7341 MovDir[x][y] = back_dir;
7343 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7344 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7345 MovDir[x][y] = MV_NONE;
7348 if (MovDir[x][y] & MV_HORIZONTAL &&
7349 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7350 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7351 MovDir[x][y] = MV_NONE;
7356 else if (element == EL_ROBOT ||
7357 element == EL_SATELLITE ||
7358 element == EL_PENGUIN ||
7359 element == EL_EMC_ANDROID)
7361 int attr_x = -1, attr_y = -1;
7372 for (i = 0; i < MAX_PLAYERS; i++)
7374 struct PlayerInfo *player = &stored_player[i];
7375 int jx = player->jx, jy = player->jy;
7377 if (!player->active)
7381 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7389 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7390 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7391 game.engine_version < VERSION_IDENT(3,1,0,0)))
7397 if (element == EL_PENGUIN)
7400 static int xy[4][2] =
7408 for (i = 0; i < NUM_DIRECTIONS; i++)
7410 int ex = x + xy[i][0];
7411 int ey = y + xy[i][1];
7413 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7414 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7415 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7416 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7425 MovDir[x][y] = MV_NONE;
7427 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7428 else if (attr_x > x)
7429 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7431 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7432 else if (attr_y > y)
7433 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7435 if (element == EL_ROBOT)
7439 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7440 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7441 Moving2Blocked(x, y, &newx, &newy);
7443 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7444 MovDelay[x][y] = 8 + 8 * !RND(3);
7446 MovDelay[x][y] = 16;
7448 else if (element == EL_PENGUIN)
7454 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7456 boolean first_horiz = RND(2);
7457 int new_move_dir = MovDir[x][y];
7460 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7461 Moving2Blocked(x, y, &newx, &newy);
7463 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7467 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7468 Moving2Blocked(x, y, &newx, &newy);
7470 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7473 MovDir[x][y] = old_move_dir;
7477 else if (element == EL_SATELLITE)
7483 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7485 boolean first_horiz = RND(2);
7486 int new_move_dir = MovDir[x][y];
7489 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7490 Moving2Blocked(x, y, &newx, &newy);
7492 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7496 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7497 Moving2Blocked(x, y, &newx, &newy);
7499 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7502 MovDir[x][y] = old_move_dir;
7506 else if (element == EL_EMC_ANDROID)
7508 static int check_pos[16] =
7510 -1, /* 0 => (invalid) */
7511 7, /* 1 => MV_LEFT */
7512 3, /* 2 => MV_RIGHT */
7513 -1, /* 3 => (invalid) */
7515 0, /* 5 => MV_LEFT | MV_UP */
7516 2, /* 6 => MV_RIGHT | MV_UP */
7517 -1, /* 7 => (invalid) */
7518 5, /* 8 => MV_DOWN */
7519 6, /* 9 => MV_LEFT | MV_DOWN */
7520 4, /* 10 => MV_RIGHT | MV_DOWN */
7521 -1, /* 11 => (invalid) */
7522 -1, /* 12 => (invalid) */
7523 -1, /* 13 => (invalid) */
7524 -1, /* 14 => (invalid) */
7525 -1, /* 15 => (invalid) */
7533 { -1, -1, MV_LEFT | MV_UP },
7535 { +1, -1, MV_RIGHT | MV_UP },
7536 { +1, 0, MV_RIGHT },
7537 { +1, +1, MV_RIGHT | MV_DOWN },
7539 { -1, +1, MV_LEFT | MV_DOWN },
7542 int start_pos, check_order;
7543 boolean can_clone = FALSE;
7546 /* check if there is any free field around current position */
7547 for (i = 0; i < 8; i++)
7549 int newx = x + check_xy[i].dx;
7550 int newy = y + check_xy[i].dy;
7552 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7560 if (can_clone) /* randomly find an element to clone */
7564 start_pos = check_pos[RND(8)];
7565 check_order = (RND(2) ? -1 : +1);
7567 for (i = 0; i < 8; i++)
7569 int pos_raw = start_pos + i * check_order;
7570 int pos = (pos_raw + 8) % 8;
7571 int newx = x + check_xy[pos].dx;
7572 int newy = y + check_xy[pos].dy;
7574 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7576 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7577 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7579 Store[x][y] = Feld[newx][newy];
7588 if (can_clone) /* randomly find a direction to move */
7592 start_pos = check_pos[RND(8)];
7593 check_order = (RND(2) ? -1 : +1);
7595 for (i = 0; i < 8; i++)
7597 int pos_raw = start_pos + i * check_order;
7598 int pos = (pos_raw + 8) % 8;
7599 int newx = x + check_xy[pos].dx;
7600 int newy = y + check_xy[pos].dy;
7601 int new_move_dir = check_xy[pos].dir;
7603 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7605 MovDir[x][y] = new_move_dir;
7606 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7615 if (can_clone) /* cloning and moving successful */
7618 /* cannot clone -- try to move towards player */
7620 start_pos = check_pos[MovDir[x][y] & 0x0f];
7621 check_order = (RND(2) ? -1 : +1);
7623 for (i = 0; i < 3; i++)
7625 /* first check start_pos, then previous/next or (next/previous) pos */
7626 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7627 int pos = (pos_raw + 8) % 8;
7628 int newx = x + check_xy[pos].dx;
7629 int newy = y + check_xy[pos].dy;
7630 int new_move_dir = check_xy[pos].dir;
7632 if (IS_PLAYER(newx, newy))
7635 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7637 MovDir[x][y] = new_move_dir;
7638 MovDelay[x][y] = level.android_move_time * 8 + 1;
7645 else if (move_pattern == MV_TURNING_LEFT ||
7646 move_pattern == MV_TURNING_RIGHT ||
7647 move_pattern == MV_TURNING_LEFT_RIGHT ||
7648 move_pattern == MV_TURNING_RIGHT_LEFT ||
7649 move_pattern == MV_TURNING_RANDOM ||
7650 move_pattern == MV_ALL_DIRECTIONS)
7652 boolean can_turn_left =
7653 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7654 boolean can_turn_right =
7655 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7657 if (element_info[element].move_stepsize == 0) /* "not moving" */
7660 if (move_pattern == MV_TURNING_LEFT)
7661 MovDir[x][y] = left_dir;
7662 else if (move_pattern == MV_TURNING_RIGHT)
7663 MovDir[x][y] = right_dir;
7664 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7665 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7666 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7667 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7668 else if (move_pattern == MV_TURNING_RANDOM)
7669 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7670 can_turn_right && !can_turn_left ? right_dir :
7671 RND(2) ? left_dir : right_dir);
7672 else if (can_turn_left && can_turn_right)
7673 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7674 else if (can_turn_left)
7675 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7676 else if (can_turn_right)
7677 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7679 MovDir[x][y] = back_dir;
7681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683 else if (move_pattern == MV_HORIZONTAL ||
7684 move_pattern == MV_VERTICAL)
7686 if (move_pattern & old_move_dir)
7687 MovDir[x][y] = back_dir;
7688 else if (move_pattern == MV_HORIZONTAL)
7689 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7690 else if (move_pattern == MV_VERTICAL)
7691 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7693 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7695 else if (move_pattern & MV_ANY_DIRECTION)
7697 MovDir[x][y] = move_pattern;
7698 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7700 else if (move_pattern & MV_WIND_DIRECTION)
7702 MovDir[x][y] = game.wind_direction;
7703 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7705 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7707 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7708 MovDir[x][y] = left_dir;
7709 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7710 MovDir[x][y] = right_dir;
7712 if (MovDir[x][y] != old_move_dir)
7713 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7715 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7717 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7718 MovDir[x][y] = right_dir;
7719 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7720 MovDir[x][y] = left_dir;
7722 if (MovDir[x][y] != old_move_dir)
7723 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7725 else if (move_pattern == MV_TOWARDS_PLAYER ||
7726 move_pattern == MV_AWAY_FROM_PLAYER)
7728 int attr_x = -1, attr_y = -1;
7730 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7741 for (i = 0; i < MAX_PLAYERS; i++)
7743 struct PlayerInfo *player = &stored_player[i];
7744 int jx = player->jx, jy = player->jy;
7746 if (!player->active)
7750 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7758 MovDir[x][y] = MV_NONE;
7760 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7761 else if (attr_x > x)
7762 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7764 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7765 else if (attr_y > y)
7766 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7768 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7770 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7772 boolean first_horiz = RND(2);
7773 int new_move_dir = MovDir[x][y];
7775 if (element_info[element].move_stepsize == 0) /* "not moving" */
7777 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7778 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7784 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7785 Moving2Blocked(x, y, &newx, &newy);
7787 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7791 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7792 Moving2Blocked(x, y, &newx, &newy);
7794 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7797 MovDir[x][y] = old_move_dir;
7800 else if (move_pattern == MV_WHEN_PUSHED ||
7801 move_pattern == MV_WHEN_DROPPED)
7803 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7804 MovDir[x][y] = MV_NONE;
7808 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7810 static int test_xy[7][2] =
7820 static int test_dir[7] =
7830 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7831 int move_preference = -1000000; /* start with very low preference */
7832 int new_move_dir = MV_NONE;
7833 int start_test = RND(4);
7836 for (i = 0; i < NUM_DIRECTIONS; i++)
7838 int move_dir = test_dir[start_test + i];
7839 int move_dir_preference;
7841 xx = x + test_xy[start_test + i][0];
7842 yy = y + test_xy[start_test + i][1];
7844 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7845 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7847 new_move_dir = move_dir;
7852 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7855 move_dir_preference = -1 * RunnerVisit[xx][yy];
7856 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7857 move_dir_preference = PlayerVisit[xx][yy];
7859 if (move_dir_preference > move_preference)
7861 /* prefer field that has not been visited for the longest time */
7862 move_preference = move_dir_preference;
7863 new_move_dir = move_dir;
7865 else if (move_dir_preference == move_preference &&
7866 move_dir == old_move_dir)
7868 /* prefer last direction when all directions are preferred equally */
7869 move_preference = move_dir_preference;
7870 new_move_dir = move_dir;
7874 MovDir[x][y] = new_move_dir;
7875 if (old_move_dir != new_move_dir)
7876 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7880 static void TurnRound(int x, int y)
7882 int direction = MovDir[x][y];
7886 GfxDir[x][y] = MovDir[x][y];
7888 if (direction != MovDir[x][y])
7892 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7894 ResetGfxFrame(x, y, FALSE);
7897 static boolean JustBeingPushed(int x, int y)
7901 for (i = 0; i < MAX_PLAYERS; i++)
7903 struct PlayerInfo *player = &stored_player[i];
7905 if (player->active && player->is_pushing && player->MovPos)
7907 int next_jx = player->jx + (player->jx - player->last_jx);
7908 int next_jy = player->jy + (player->jy - player->last_jy);
7910 if (x == next_jx && y == next_jy)
7918 void StartMoving(int x, int y)
7920 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7921 int element = Feld[x][y];
7926 if (MovDelay[x][y] == 0)
7927 GfxAction[x][y] = ACTION_DEFAULT;
7929 if (CAN_FALL(element) && y < lev_fieldy - 1)
7931 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7932 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7933 if (JustBeingPushed(x, y))
7936 if (element == EL_QUICKSAND_FULL)
7938 if (IS_FREE(x, y + 1))
7940 InitMovingField(x, y, MV_DOWN);
7941 started_moving = TRUE;
7943 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7944 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7945 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7946 Store[x][y] = EL_ROCK;
7948 Store[x][y] = EL_ROCK;
7951 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7953 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7955 if (!MovDelay[x][y])
7957 MovDelay[x][y] = TILEY + 1;
7959 ResetGfxAnimation(x, y);
7960 ResetGfxAnimation(x, y + 1);
7965 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7966 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7973 Feld[x][y] = EL_QUICKSAND_EMPTY;
7974 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7975 Store[x][y + 1] = Store[x][y];
7978 PlayLevelSoundAction(x, y, ACTION_FILLING);
7980 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7982 if (!MovDelay[x][y])
7984 MovDelay[x][y] = TILEY + 1;
7986 ResetGfxAnimation(x, y);
7987 ResetGfxAnimation(x, y + 1);
7992 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7993 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8000 Feld[x][y] = EL_QUICKSAND_EMPTY;
8001 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8002 Store[x][y + 1] = Store[x][y];
8005 PlayLevelSoundAction(x, y, ACTION_FILLING);
8008 else if (element == EL_QUICKSAND_FAST_FULL)
8010 if (IS_FREE(x, y + 1))
8012 InitMovingField(x, y, MV_DOWN);
8013 started_moving = TRUE;
8015 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8016 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8017 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8018 Store[x][y] = EL_ROCK;
8020 Store[x][y] = EL_ROCK;
8023 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8025 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8027 if (!MovDelay[x][y])
8029 MovDelay[x][y] = TILEY + 1;
8031 ResetGfxAnimation(x, y);
8032 ResetGfxAnimation(x, y + 1);
8037 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8038 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8045 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8046 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8047 Store[x][y + 1] = Store[x][y];
8050 PlayLevelSoundAction(x, y, ACTION_FILLING);
8052 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8054 if (!MovDelay[x][y])
8056 MovDelay[x][y] = TILEY + 1;
8058 ResetGfxAnimation(x, y);
8059 ResetGfxAnimation(x, y + 1);
8064 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8065 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8072 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8073 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8074 Store[x][y + 1] = Store[x][y];
8077 PlayLevelSoundAction(x, y, ACTION_FILLING);
8080 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8081 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8083 InitMovingField(x, y, MV_DOWN);
8084 started_moving = TRUE;
8086 Feld[x][y] = EL_QUICKSAND_FILLING;
8087 Store[x][y] = element;
8089 PlayLevelSoundAction(x, y, ACTION_FILLING);
8091 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8092 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8094 InitMovingField(x, y, MV_DOWN);
8095 started_moving = TRUE;
8097 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8098 Store[x][y] = element;
8100 PlayLevelSoundAction(x, y, ACTION_FILLING);
8102 else if (element == EL_MAGIC_WALL_FULL)
8104 if (IS_FREE(x, y + 1))
8106 InitMovingField(x, y, MV_DOWN);
8107 started_moving = TRUE;
8109 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8110 Store[x][y] = EL_CHANGED(Store[x][y]);
8112 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8114 if (!MovDelay[x][y])
8115 MovDelay[x][y] = TILEY / 4 + 1;
8124 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8125 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8126 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8130 else if (element == EL_BD_MAGIC_WALL_FULL)
8132 if (IS_FREE(x, y + 1))
8134 InitMovingField(x, y, MV_DOWN);
8135 started_moving = TRUE;
8137 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8138 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8140 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8142 if (!MovDelay[x][y])
8143 MovDelay[x][y] = TILEY / 4 + 1;
8152 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8153 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8154 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8158 else if (element == EL_DC_MAGIC_WALL_FULL)
8160 if (IS_FREE(x, y + 1))
8162 InitMovingField(x, y, MV_DOWN);
8163 started_moving = TRUE;
8165 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8166 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8168 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8170 if (!MovDelay[x][y])
8171 MovDelay[x][y] = TILEY / 4 + 1;
8180 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8181 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8182 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8186 else if ((CAN_PASS_MAGIC_WALL(element) &&
8187 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8188 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8189 (CAN_PASS_DC_MAGIC_WALL(element) &&
8190 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8193 InitMovingField(x, y, MV_DOWN);
8194 started_moving = TRUE;
8197 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8198 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8199 EL_DC_MAGIC_WALL_FILLING);
8200 Store[x][y] = element;
8202 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8204 SplashAcid(x, y + 1);
8206 InitMovingField(x, y, MV_DOWN);
8207 started_moving = TRUE;
8209 Store[x][y] = EL_ACID;
8212 #if USE_FIX_IMPACT_COLLISION
8213 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8214 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8216 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8219 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8220 CAN_FALL(element) && WasJustFalling[x][y] &&
8221 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8223 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8224 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8225 (Feld[x][y + 1] == EL_BLOCKED)))
8227 /* this is needed for a special case not covered by calling "Impact()"
8228 from "ContinueMoving()": if an element moves to a tile directly below
8229 another element which was just falling on that tile (which was empty
8230 in the previous frame), the falling element above would just stop
8231 instead of smashing the element below (in previous version, the above
8232 element was just checked for "moving" instead of "falling", resulting
8233 in incorrect smashes caused by horizontal movement of the above
8234 element; also, the case of the player being the element to smash was
8235 simply not covered here... :-/ ) */
8237 CheckCollision[x][y] = 0;
8238 CheckImpact[x][y] = 0;
8242 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8244 if (MovDir[x][y] == MV_NONE)
8246 InitMovingField(x, y, MV_DOWN);
8247 started_moving = TRUE;
8250 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8252 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8253 MovDir[x][y] = MV_DOWN;
8255 InitMovingField(x, y, MV_DOWN);
8256 started_moving = TRUE;
8258 else if (element == EL_AMOEBA_DROP)
8260 Feld[x][y] = EL_AMOEBA_GROWING;
8261 Store[x][y] = EL_AMOEBA_WET;
8263 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8264 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8265 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8266 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8268 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8269 (IS_FREE(x - 1, y + 1) ||
8270 Feld[x - 1][y + 1] == EL_ACID));
8271 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8272 (IS_FREE(x + 1, y + 1) ||
8273 Feld[x + 1][y + 1] == EL_ACID));
8274 boolean can_fall_any = (can_fall_left || can_fall_right);
8275 boolean can_fall_both = (can_fall_left && can_fall_right);
8276 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8278 #if USE_NEW_ALL_SLIPPERY
8279 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8281 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8282 can_fall_right = FALSE;
8283 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8284 can_fall_left = FALSE;
8285 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8286 can_fall_right = FALSE;
8287 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8288 can_fall_left = FALSE;
8290 can_fall_any = (can_fall_left || can_fall_right);
8291 can_fall_both = FALSE;
8294 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8296 if (slippery_type == SLIPPERY_ONLY_LEFT)
8297 can_fall_right = FALSE;
8298 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8299 can_fall_left = FALSE;
8300 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8301 can_fall_right = FALSE;
8302 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8303 can_fall_left = FALSE;
8305 can_fall_any = (can_fall_left || can_fall_right);
8306 can_fall_both = (can_fall_left && can_fall_right);
8310 #if USE_NEW_ALL_SLIPPERY
8312 #if USE_NEW_SP_SLIPPERY
8313 /* !!! better use the same properties as for custom elements here !!! */
8314 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8315 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8317 can_fall_right = FALSE; /* slip down on left side */
8318 can_fall_both = FALSE;
8323 #if USE_NEW_ALL_SLIPPERY
8326 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8327 can_fall_right = FALSE; /* slip down on left side */
8329 can_fall_left = !(can_fall_right = RND(2));
8331 can_fall_both = FALSE;
8336 if (game.emulation == EMU_BOULDERDASH ||
8337 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8338 can_fall_right = FALSE; /* slip down on left side */
8340 can_fall_left = !(can_fall_right = RND(2));
8342 can_fall_both = FALSE;
8348 /* if not determined otherwise, prefer left side for slipping down */
8349 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8350 started_moving = TRUE;
8354 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8356 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8359 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8360 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8361 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8362 int belt_dir = game.belt_dir[belt_nr];
8364 if ((belt_dir == MV_LEFT && left_is_free) ||
8365 (belt_dir == MV_RIGHT && right_is_free))
8367 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8369 InitMovingField(x, y, belt_dir);
8370 started_moving = TRUE;
8372 Pushed[x][y] = TRUE;
8373 Pushed[nextx][y] = TRUE;
8375 GfxAction[x][y] = ACTION_DEFAULT;
8379 MovDir[x][y] = 0; /* if element was moving, stop it */
8384 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8386 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8388 if (CAN_MOVE(element) && !started_moving)
8391 int move_pattern = element_info[element].move_pattern;
8396 if (MovDir[x][y] == MV_NONE)
8398 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8399 x, y, element, element_info[element].token_name);
8400 printf("StartMoving(): This should never happen!\n");
8405 Moving2Blocked(x, y, &newx, &newy);
8407 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8410 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8411 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8413 WasJustMoving[x][y] = 0;
8414 CheckCollision[x][y] = 0;
8416 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8418 if (Feld[x][y] != element) /* element has changed */
8422 if (!MovDelay[x][y]) /* start new movement phase */
8424 /* all objects that can change their move direction after each step
8425 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8427 if (element != EL_YAMYAM &&
8428 element != EL_DARK_YAMYAM &&
8429 element != EL_PACMAN &&
8430 !(move_pattern & MV_ANY_DIRECTION) &&
8431 move_pattern != MV_TURNING_LEFT &&
8432 move_pattern != MV_TURNING_RIGHT &&
8433 move_pattern != MV_TURNING_LEFT_RIGHT &&
8434 move_pattern != MV_TURNING_RIGHT_LEFT &&
8435 move_pattern != MV_TURNING_RANDOM)
8439 if (MovDelay[x][y] && (element == EL_BUG ||
8440 element == EL_SPACESHIP ||
8441 element == EL_SP_SNIKSNAK ||
8442 element == EL_SP_ELECTRON ||
8443 element == EL_MOLE))
8444 TEST_DrawLevelField(x, y);
8448 if (MovDelay[x][y]) /* wait some time before next movement */
8452 if (element == EL_ROBOT ||
8453 element == EL_YAMYAM ||
8454 element == EL_DARK_YAMYAM)
8456 DrawLevelElementAnimationIfNeeded(x, y, element);
8457 PlayLevelSoundAction(x, y, ACTION_WAITING);
8459 else if (element == EL_SP_ELECTRON)
8460 DrawLevelElementAnimationIfNeeded(x, y, element);
8461 else if (element == EL_DRAGON)
8464 int dir = MovDir[x][y];
8465 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8466 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8467 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8468 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8469 dir == MV_UP ? IMG_FLAMES_1_UP :
8470 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8471 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8473 GfxAction[x][y] = ACTION_ATTACKING;
8475 if (IS_PLAYER(x, y))
8476 DrawPlayerField(x, y);
8478 TEST_DrawLevelField(x, y);
8480 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8482 for (i = 1; i <= 3; i++)
8484 int xx = x + i * dx;
8485 int yy = y + i * dy;
8486 int sx = SCREENX(xx);
8487 int sy = SCREENY(yy);
8488 int flame_graphic = graphic + (i - 1);
8490 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8495 int flamed = MovingOrBlocked2Element(xx, yy);
8499 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8501 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8502 RemoveMovingField(xx, yy);
8504 RemoveField(xx, yy);
8506 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8509 RemoveMovingField(xx, yy);
8512 ChangeDelay[xx][yy] = 0;
8514 Feld[xx][yy] = EL_FLAMES;
8516 if (IN_SCR_FIELD(sx, sy))
8518 TEST_DrawLevelFieldCrumbled(xx, yy);
8519 DrawGraphic(sx, sy, flame_graphic, frame);
8524 if (Feld[xx][yy] == EL_FLAMES)
8525 Feld[xx][yy] = EL_EMPTY;
8526 TEST_DrawLevelField(xx, yy);
8531 if (MovDelay[x][y]) /* element still has to wait some time */
8533 PlayLevelSoundAction(x, y, ACTION_WAITING);
8539 /* now make next step */
8541 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8543 if (DONT_COLLIDE_WITH(element) &&
8544 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8545 !PLAYER_ENEMY_PROTECTED(newx, newy))
8547 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8552 else if (CAN_MOVE_INTO_ACID(element) &&
8553 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8554 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8555 (MovDir[x][y] == MV_DOWN ||
8556 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8558 SplashAcid(newx, newy);
8559 Store[x][y] = EL_ACID;
8561 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8563 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8564 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8565 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8566 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8569 TEST_DrawLevelField(x, y);
8571 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8572 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8573 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8575 local_player->friends_still_needed--;
8576 if (!local_player->friends_still_needed &&
8577 !local_player->GameOver && AllPlayersGone)
8578 PlayerWins(local_player);
8582 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8584 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8585 TEST_DrawLevelField(newx, newy);
8587 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8589 else if (!IS_FREE(newx, newy))
8591 GfxAction[x][y] = ACTION_WAITING;
8593 if (IS_PLAYER(x, y))
8594 DrawPlayerField(x, y);
8596 TEST_DrawLevelField(x, y);
8601 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8603 if (IS_FOOD_PIG(Feld[newx][newy]))
8605 if (IS_MOVING(newx, newy))
8606 RemoveMovingField(newx, newy);
8609 Feld[newx][newy] = EL_EMPTY;
8610 TEST_DrawLevelField(newx, newy);
8613 PlayLevelSound(x, y, SND_PIG_DIGGING);
8615 else if (!IS_FREE(newx, newy))
8617 if (IS_PLAYER(x, y))
8618 DrawPlayerField(x, y);
8620 TEST_DrawLevelField(x, y);
8625 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8627 if (Store[x][y] != EL_EMPTY)
8629 boolean can_clone = FALSE;
8632 /* check if element to clone is still there */
8633 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8635 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8643 /* cannot clone or target field not free anymore -- do not clone */
8644 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8645 Store[x][y] = EL_EMPTY;
8648 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8650 if (IS_MV_DIAGONAL(MovDir[x][y]))
8652 int diagonal_move_dir = MovDir[x][y];
8653 int stored = Store[x][y];
8654 int change_delay = 8;
8657 /* android is moving diagonally */
8659 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8661 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8662 GfxElement[x][y] = EL_EMC_ANDROID;
8663 GfxAction[x][y] = ACTION_SHRINKING;
8664 GfxDir[x][y] = diagonal_move_dir;
8665 ChangeDelay[x][y] = change_delay;
8667 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8670 DrawLevelGraphicAnimation(x, y, graphic);
8671 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8673 if (Feld[newx][newy] == EL_ACID)
8675 SplashAcid(newx, newy);
8680 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8682 Store[newx][newy] = EL_EMC_ANDROID;
8683 GfxElement[newx][newy] = EL_EMC_ANDROID;
8684 GfxAction[newx][newy] = ACTION_GROWING;
8685 GfxDir[newx][newy] = diagonal_move_dir;
8686 ChangeDelay[newx][newy] = change_delay;
8688 graphic = el_act_dir2img(GfxElement[newx][newy],
8689 GfxAction[newx][newy], GfxDir[newx][newy]);
8691 DrawLevelGraphicAnimation(newx, newy, graphic);
8692 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8698 Feld[newx][newy] = EL_EMPTY;
8699 TEST_DrawLevelField(newx, newy);
8701 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8704 else if (!IS_FREE(newx, newy))
8707 if (IS_PLAYER(x, y))
8708 DrawPlayerField(x, y);
8710 TEST_DrawLevelField(x, y);
8716 else if (IS_CUSTOM_ELEMENT(element) &&
8717 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8720 if (!DigFieldByCE(newx, newy, element))
8723 int new_element = Feld[newx][newy];
8725 if (!IS_FREE(newx, newy))
8727 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8728 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8731 /* no element can dig solid indestructible elements */
8732 if (IS_INDESTRUCTIBLE(new_element) &&
8733 !IS_DIGGABLE(new_element) &&
8734 !IS_COLLECTIBLE(new_element))
8737 if (AmoebaNr[newx][newy] &&
8738 (new_element == EL_AMOEBA_FULL ||
8739 new_element == EL_BD_AMOEBA ||
8740 new_element == EL_AMOEBA_GROWING))
8742 AmoebaCnt[AmoebaNr[newx][newy]]--;
8743 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8746 if (IS_MOVING(newx, newy))
8747 RemoveMovingField(newx, newy);
8750 RemoveField(newx, newy);
8751 TEST_DrawLevelField(newx, newy);
8754 /* if digged element was about to explode, prevent the explosion */
8755 ExplodeField[newx][newy] = EX_TYPE_NONE;
8757 PlayLevelSoundAction(x, y, action);
8760 Store[newx][newy] = EL_EMPTY;
8763 /* this makes it possible to leave the removed element again */
8764 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8765 Store[newx][newy] = new_element;
8767 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8769 int move_leave_element = element_info[element].move_leave_element;
8771 /* this makes it possible to leave the removed element again */
8772 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8773 new_element : move_leave_element);
8779 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8781 RunnerVisit[x][y] = FrameCounter;
8782 PlayerVisit[x][y] /= 8; /* expire player visit path */
8785 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8787 if (!IS_FREE(newx, newy))
8789 if (IS_PLAYER(x, y))
8790 DrawPlayerField(x, y);
8792 TEST_DrawLevelField(x, y);
8798 boolean wanna_flame = !RND(10);
8799 int dx = newx - x, dy = newy - y;
8800 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8801 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8802 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8803 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8804 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8805 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8808 IS_CLASSIC_ENEMY(element1) ||
8809 IS_CLASSIC_ENEMY(element2)) &&
8810 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8811 element1 != EL_FLAMES && element2 != EL_FLAMES)
8813 ResetGfxAnimation(x, y);
8814 GfxAction[x][y] = ACTION_ATTACKING;
8816 if (IS_PLAYER(x, y))
8817 DrawPlayerField(x, y);
8819 TEST_DrawLevelField(x, y);
8821 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8823 MovDelay[x][y] = 50;
8827 RemoveField(newx, newy);
8829 Feld[newx][newy] = EL_FLAMES;
8830 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8833 RemoveField(newx1, newy1);
8835 Feld[newx1][newy1] = EL_FLAMES;
8837 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8840 RemoveField(newx2, newy2);
8842 Feld[newx2][newy2] = EL_FLAMES;
8849 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8850 Feld[newx][newy] == EL_DIAMOND)
8852 if (IS_MOVING(newx, newy))
8853 RemoveMovingField(newx, newy);
8856 Feld[newx][newy] = EL_EMPTY;
8857 TEST_DrawLevelField(newx, newy);
8860 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8862 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8863 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8865 if (AmoebaNr[newx][newy])
8867 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8868 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8869 Feld[newx][newy] == EL_BD_AMOEBA)
8870 AmoebaCnt[AmoebaNr[newx][newy]]--;
8875 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8877 RemoveMovingField(newx, newy);
8880 if (IS_MOVING(newx, newy))
8882 RemoveMovingField(newx, newy);
8887 Feld[newx][newy] = EL_EMPTY;
8888 TEST_DrawLevelField(newx, newy);
8891 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8893 else if ((element == EL_PACMAN || element == EL_MOLE)
8894 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8896 if (AmoebaNr[newx][newy])
8898 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8899 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8900 Feld[newx][newy] == EL_BD_AMOEBA)
8901 AmoebaCnt[AmoebaNr[newx][newy]]--;
8904 if (element == EL_MOLE)
8906 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8907 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8909 ResetGfxAnimation(x, y);
8910 GfxAction[x][y] = ACTION_DIGGING;
8911 TEST_DrawLevelField(x, y);
8913 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8915 return; /* wait for shrinking amoeba */
8917 else /* element == EL_PACMAN */
8919 Feld[newx][newy] = EL_EMPTY;
8920 TEST_DrawLevelField(newx, newy);
8921 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8924 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8925 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8926 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8928 /* wait for shrinking amoeba to completely disappear */
8931 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8933 /* object was running against a wall */
8938 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8939 if (move_pattern & MV_ANY_DIRECTION &&
8940 move_pattern == MovDir[x][y])
8942 int blocking_element =
8943 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8945 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8948 element = Feld[x][y]; /* element might have changed */
8952 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8953 DrawLevelElementAnimation(x, y, element);
8955 if (DONT_TOUCH(element))
8956 TestIfBadThingTouchesPlayer(x, y);
8961 InitMovingField(x, y, MovDir[x][y]);
8963 PlayLevelSoundAction(x, y, ACTION_MOVING);
8967 ContinueMoving(x, y);
8970 void ContinueMoving(int x, int y)
8972 int element = Feld[x][y];
8973 struct ElementInfo *ei = &element_info[element];
8974 int direction = MovDir[x][y];
8975 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8976 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8977 int newx = x + dx, newy = y + dy;
8978 int stored = Store[x][y];
8979 int stored_new = Store[newx][newy];
8980 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8981 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8982 boolean last_line = (newy == lev_fieldy - 1);
8984 MovPos[x][y] += getElementMoveStepsize(x, y);
8986 if (pushed_by_player) /* special case: moving object pushed by player */
8987 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8989 if (ABS(MovPos[x][y]) < TILEX)
8992 int ee = Feld[x][y];
8993 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8994 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8996 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8997 x, y, ABS(MovPos[x][y]),
8999 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9002 TEST_DrawLevelField(x, y);
9004 return; /* element is still moving */
9007 /* element reached destination field */
9009 Feld[x][y] = EL_EMPTY;
9010 Feld[newx][newy] = element;
9011 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9013 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9015 element = Feld[newx][newy] = EL_ACID;
9017 else if (element == EL_MOLE)
9019 Feld[x][y] = EL_SAND;
9021 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9023 else if (element == EL_QUICKSAND_FILLING)
9025 element = Feld[newx][newy] = get_next_element(element);
9026 Store[newx][newy] = Store[x][y];
9028 else if (element == EL_QUICKSAND_EMPTYING)
9030 Feld[x][y] = get_next_element(element);
9031 element = Feld[newx][newy] = Store[x][y];
9033 else if (element == EL_QUICKSAND_FAST_FILLING)
9035 element = Feld[newx][newy] = get_next_element(element);
9036 Store[newx][newy] = Store[x][y];
9038 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9040 Feld[x][y] = get_next_element(element);
9041 element = Feld[newx][newy] = Store[x][y];
9043 else if (element == EL_MAGIC_WALL_FILLING)
9045 element = Feld[newx][newy] = get_next_element(element);
9046 if (!game.magic_wall_active)
9047 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9048 Store[newx][newy] = Store[x][y];
9050 else if (element == EL_MAGIC_WALL_EMPTYING)
9052 Feld[x][y] = get_next_element(element);
9053 if (!game.magic_wall_active)
9054 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9055 element = Feld[newx][newy] = Store[x][y];
9057 #if USE_NEW_CUSTOM_VALUE
9058 InitField(newx, newy, FALSE);
9061 else if (element == EL_BD_MAGIC_WALL_FILLING)
9063 element = Feld[newx][newy] = get_next_element(element);
9064 if (!game.magic_wall_active)
9065 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9066 Store[newx][newy] = Store[x][y];
9068 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9070 Feld[x][y] = get_next_element(element);
9071 if (!game.magic_wall_active)
9072 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9073 element = Feld[newx][newy] = Store[x][y];
9075 #if USE_NEW_CUSTOM_VALUE
9076 InitField(newx, newy, FALSE);
9079 else if (element == EL_DC_MAGIC_WALL_FILLING)
9081 element = Feld[newx][newy] = get_next_element(element);
9082 if (!game.magic_wall_active)
9083 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9084 Store[newx][newy] = Store[x][y];
9086 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9088 Feld[x][y] = get_next_element(element);
9089 if (!game.magic_wall_active)
9090 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9091 element = Feld[newx][newy] = Store[x][y];
9093 #if USE_NEW_CUSTOM_VALUE
9094 InitField(newx, newy, FALSE);
9097 else if (element == EL_AMOEBA_DROPPING)
9099 Feld[x][y] = get_next_element(element);
9100 element = Feld[newx][newy] = Store[x][y];
9102 else if (element == EL_SOKOBAN_OBJECT)
9105 Feld[x][y] = Back[x][y];
9107 if (Back[newx][newy])
9108 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9110 Back[x][y] = Back[newx][newy] = 0;
9113 Store[x][y] = EL_EMPTY;
9118 MovDelay[newx][newy] = 0;
9120 if (CAN_CHANGE_OR_HAS_ACTION(element))
9122 /* copy element change control values to new field */
9123 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9124 ChangePage[newx][newy] = ChangePage[x][y];
9125 ChangeCount[newx][newy] = ChangeCount[x][y];
9126 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9129 #if USE_NEW_CUSTOM_VALUE
9130 CustomValue[newx][newy] = CustomValue[x][y];
9133 ChangeDelay[x][y] = 0;
9134 ChangePage[x][y] = -1;
9135 ChangeCount[x][y] = 0;
9136 ChangeEvent[x][y] = -1;
9138 #if USE_NEW_CUSTOM_VALUE
9139 CustomValue[x][y] = 0;
9142 /* copy animation control values to new field */
9143 GfxFrame[newx][newy] = GfxFrame[x][y];
9144 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9145 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9146 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9148 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9150 /* some elements can leave other elements behind after moving */
9152 if (ei->move_leave_element != EL_EMPTY &&
9153 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9154 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9156 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9157 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9158 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9161 int move_leave_element = ei->move_leave_element;
9165 /* this makes it possible to leave the removed element again */
9166 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9167 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9169 /* this makes it possible to leave the removed element again */
9170 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9171 move_leave_element = stored;
9174 /* this makes it possible to leave the removed element again */
9175 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9176 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9177 move_leave_element = stored;
9180 Feld[x][y] = move_leave_element;
9182 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9183 MovDir[x][y] = direction;
9185 InitField(x, y, FALSE);
9187 if (GFX_CRUMBLED(Feld[x][y]))
9188 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9190 if (ELEM_IS_PLAYER(move_leave_element))
9191 RelocatePlayer(x, y, move_leave_element);
9194 /* do this after checking for left-behind element */
9195 ResetGfxAnimation(x, y); /* reset animation values for old field */
9197 if (!CAN_MOVE(element) ||
9198 (CAN_FALL(element) && direction == MV_DOWN &&
9199 (element == EL_SPRING ||
9200 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9201 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9202 GfxDir[x][y] = MovDir[newx][newy] = 0;
9204 TEST_DrawLevelField(x, y);
9205 TEST_DrawLevelField(newx, newy);
9207 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9209 /* prevent pushed element from moving on in pushed direction */
9210 if (pushed_by_player && CAN_MOVE(element) &&
9211 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9212 !(element_info[element].move_pattern & direction))
9213 TurnRound(newx, newy);
9215 /* prevent elements on conveyor belt from moving on in last direction */
9216 if (pushed_by_conveyor && CAN_FALL(element) &&
9217 direction & MV_HORIZONTAL)
9218 MovDir[newx][newy] = 0;
9220 if (!pushed_by_player)
9222 int nextx = newx + dx, nexty = newy + dy;
9223 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9225 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9227 if (CAN_FALL(element) && direction == MV_DOWN)
9228 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9230 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9231 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9233 #if USE_FIX_IMPACT_COLLISION
9234 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9235 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9239 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9241 TestIfBadThingTouchesPlayer(newx, newy);
9242 TestIfBadThingTouchesFriend(newx, newy);
9244 if (!IS_CUSTOM_ELEMENT(element))
9245 TestIfBadThingTouchesOtherBadThing(newx, newy);
9247 else if (element == EL_PENGUIN)
9248 TestIfFriendTouchesBadThing(newx, newy);
9250 if (DONT_GET_HIT_BY(element))
9252 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9255 /* give the player one last chance (one more frame) to move away */
9256 if (CAN_FALL(element) && direction == MV_DOWN &&
9257 (last_line || (!IS_FREE(x, newy + 1) &&
9258 (!IS_PLAYER(x, newy + 1) ||
9259 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9262 if (pushed_by_player && !game.use_change_when_pushing_bug)
9264 int push_side = MV_DIR_OPPOSITE(direction);
9265 struct PlayerInfo *player = PLAYERINFO(x, y);
9267 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9268 player->index_bit, push_side);
9269 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9270 player->index_bit, push_side);
9273 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9274 MovDelay[newx][newy] = 1;
9276 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9278 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9281 if (ChangePage[newx][newy] != -1) /* delayed change */
9283 int page = ChangePage[newx][newy];
9284 struct ElementChangeInfo *change = &ei->change_page[page];
9286 ChangePage[newx][newy] = -1;
9288 if (change->can_change)
9290 if (ChangeElement(newx, newy, element, page))
9292 if (change->post_change_function)
9293 change->post_change_function(newx, newy);
9297 if (change->has_action)
9298 ExecuteCustomElementAction(newx, newy, element, page);
9302 TestIfElementHitsCustomElement(newx, newy, direction);
9303 TestIfPlayerTouchesCustomElement(newx, newy);
9304 TestIfElementTouchesCustomElement(newx, newy);
9306 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9307 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9308 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9309 MV_DIR_OPPOSITE(direction));
9312 int AmoebeNachbarNr(int ax, int ay)
9315 int element = Feld[ax][ay];
9317 static int xy[4][2] =
9325 for (i = 0; i < NUM_DIRECTIONS; i++)
9327 int x = ax + xy[i][0];
9328 int y = ay + xy[i][1];
9330 if (!IN_LEV_FIELD(x, y))
9333 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9334 group_nr = AmoebaNr[x][y];
9340 void AmoebenVereinigen(int ax, int ay)
9342 int i, x, y, xx, yy;
9343 int new_group_nr = AmoebaNr[ax][ay];
9344 static int xy[4][2] =
9352 if (new_group_nr == 0)
9355 for (i = 0; i < NUM_DIRECTIONS; i++)
9360 if (!IN_LEV_FIELD(x, y))
9363 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9364 Feld[x][y] == EL_BD_AMOEBA ||
9365 Feld[x][y] == EL_AMOEBA_DEAD) &&
9366 AmoebaNr[x][y] != new_group_nr)
9368 int old_group_nr = AmoebaNr[x][y];
9370 if (old_group_nr == 0)
9373 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9374 AmoebaCnt[old_group_nr] = 0;
9375 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9376 AmoebaCnt2[old_group_nr] = 0;
9378 SCAN_PLAYFIELD(xx, yy)
9380 if (AmoebaNr[xx][yy] == old_group_nr)
9381 AmoebaNr[xx][yy] = new_group_nr;
9387 void AmoebeUmwandeln(int ax, int ay)
9391 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9393 int group_nr = AmoebaNr[ax][ay];
9398 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9399 printf("AmoebeUmwandeln(): This should never happen!\n");
9404 SCAN_PLAYFIELD(x, y)
9406 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9409 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9413 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9414 SND_AMOEBA_TURNING_TO_GEM :
9415 SND_AMOEBA_TURNING_TO_ROCK));
9420 static int xy[4][2] =
9428 for (i = 0; i < NUM_DIRECTIONS; i++)
9433 if (!IN_LEV_FIELD(x, y))
9436 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9438 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9439 SND_AMOEBA_TURNING_TO_GEM :
9440 SND_AMOEBA_TURNING_TO_ROCK));
9447 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9450 int group_nr = AmoebaNr[ax][ay];
9451 boolean done = FALSE;
9456 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9457 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9462 SCAN_PLAYFIELD(x, y)
9464 if (AmoebaNr[x][y] == group_nr &&
9465 (Feld[x][y] == EL_AMOEBA_DEAD ||
9466 Feld[x][y] == EL_BD_AMOEBA ||
9467 Feld[x][y] == EL_AMOEBA_GROWING))
9470 Feld[x][y] = new_element;
9471 InitField(x, y, FALSE);
9472 TEST_DrawLevelField(x, y);
9478 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9479 SND_BD_AMOEBA_TURNING_TO_ROCK :
9480 SND_BD_AMOEBA_TURNING_TO_GEM));
9483 void AmoebeWaechst(int x, int y)
9485 static unsigned int sound_delay = 0;
9486 static unsigned int sound_delay_value = 0;
9488 if (!MovDelay[x][y]) /* start new growing cycle */
9492 if (DelayReached(&sound_delay, sound_delay_value))
9494 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9495 sound_delay_value = 30;
9499 if (MovDelay[x][y]) /* wait some time before growing bigger */
9502 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9504 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9505 6 - MovDelay[x][y]);
9507 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9510 if (!MovDelay[x][y])
9512 Feld[x][y] = Store[x][y];
9514 TEST_DrawLevelField(x, y);
9519 void AmoebaDisappearing(int x, int y)
9521 static unsigned int sound_delay = 0;
9522 static unsigned int sound_delay_value = 0;
9524 if (!MovDelay[x][y]) /* start new shrinking cycle */
9528 if (DelayReached(&sound_delay, sound_delay_value))
9529 sound_delay_value = 30;
9532 if (MovDelay[x][y]) /* wait some time before shrinking */
9535 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9537 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9538 6 - MovDelay[x][y]);
9540 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9543 if (!MovDelay[x][y])
9545 Feld[x][y] = EL_EMPTY;
9546 TEST_DrawLevelField(x, y);
9548 /* don't let mole enter this field in this cycle;
9549 (give priority to objects falling to this field from above) */
9555 void AmoebeAbleger(int ax, int ay)
9558 int element = Feld[ax][ay];
9559 int graphic = el2img(element);
9560 int newax = ax, neway = ay;
9561 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9562 static int xy[4][2] =
9570 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9572 Feld[ax][ay] = EL_AMOEBA_DEAD;
9573 TEST_DrawLevelField(ax, ay);
9577 if (IS_ANIMATED(graphic))
9578 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9580 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9581 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9583 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9586 if (MovDelay[ax][ay])
9590 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9593 int x = ax + xy[start][0];
9594 int y = ay + xy[start][1];
9596 if (!IN_LEV_FIELD(x, y))
9599 if (IS_FREE(x, y) ||
9600 CAN_GROW_INTO(Feld[x][y]) ||
9601 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9602 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9608 if (newax == ax && neway == ay)
9611 else /* normal or "filled" (BD style) amoeba */
9614 boolean waiting_for_player = FALSE;
9616 for (i = 0; i < NUM_DIRECTIONS; i++)
9618 int j = (start + i) % 4;
9619 int x = ax + xy[j][0];
9620 int y = ay + xy[j][1];
9622 if (!IN_LEV_FIELD(x, y))
9625 if (IS_FREE(x, y) ||
9626 CAN_GROW_INTO(Feld[x][y]) ||
9627 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9628 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9634 else if (IS_PLAYER(x, y))
9635 waiting_for_player = TRUE;
9638 if (newax == ax && neway == ay) /* amoeba cannot grow */
9640 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9642 Feld[ax][ay] = EL_AMOEBA_DEAD;
9643 TEST_DrawLevelField(ax, ay);
9644 AmoebaCnt[AmoebaNr[ax][ay]]--;
9646 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9648 if (element == EL_AMOEBA_FULL)
9649 AmoebeUmwandeln(ax, ay);
9650 else if (element == EL_BD_AMOEBA)
9651 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9656 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9658 /* amoeba gets larger by growing in some direction */
9660 int new_group_nr = AmoebaNr[ax][ay];
9663 if (new_group_nr == 0)
9665 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9666 printf("AmoebeAbleger(): This should never happen!\n");
9671 AmoebaNr[newax][neway] = new_group_nr;
9672 AmoebaCnt[new_group_nr]++;
9673 AmoebaCnt2[new_group_nr]++;
9675 /* if amoeba touches other amoeba(s) after growing, unify them */
9676 AmoebenVereinigen(newax, neway);
9678 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9680 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9686 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9687 (neway == lev_fieldy - 1 && newax != ax))
9689 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9690 Store[newax][neway] = element;
9692 else if (neway == ay || element == EL_EMC_DRIPPER)
9694 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9696 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9700 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9701 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9702 Store[ax][ay] = EL_AMOEBA_DROP;
9703 ContinueMoving(ax, ay);
9707 TEST_DrawLevelField(newax, neway);
9710 void Life(int ax, int ay)
9714 int element = Feld[ax][ay];
9715 int graphic = el2img(element);
9716 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9718 boolean changed = FALSE;
9720 if (IS_ANIMATED(graphic))
9721 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9726 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9727 MovDelay[ax][ay] = life_time;
9729 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9732 if (MovDelay[ax][ay])
9736 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9738 int xx = ax+x1, yy = ay+y1;
9741 if (!IN_LEV_FIELD(xx, yy))
9744 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9746 int x = xx+x2, y = yy+y2;
9748 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9751 if (((Feld[x][y] == element ||
9752 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9754 (IS_FREE(x, y) && Stop[x][y]))
9758 if (xx == ax && yy == ay) /* field in the middle */
9760 if (nachbarn < life_parameter[0] ||
9761 nachbarn > life_parameter[1])
9763 Feld[xx][yy] = EL_EMPTY;
9765 TEST_DrawLevelField(xx, yy);
9766 Stop[xx][yy] = TRUE;
9770 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9771 { /* free border field */
9772 if (nachbarn >= life_parameter[2] &&
9773 nachbarn <= life_parameter[3])
9775 Feld[xx][yy] = element;
9776 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9778 TEST_DrawLevelField(xx, yy);
9779 Stop[xx][yy] = TRUE;
9786 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9787 SND_GAME_OF_LIFE_GROWING);
9790 static void InitRobotWheel(int x, int y)
9792 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9795 static void RunRobotWheel(int x, int y)
9797 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9800 static void StopRobotWheel(int x, int y)
9802 if (ZX == x && ZY == y)
9806 game.robot_wheel_active = FALSE;
9810 static void InitTimegateWheel(int x, int y)
9812 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9815 static void RunTimegateWheel(int x, int y)
9817 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9820 static void InitMagicBallDelay(int x, int y)
9823 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9825 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9829 static void ActivateMagicBall(int bx, int by)
9833 if (level.ball_random)
9835 int pos_border = RND(8); /* select one of the eight border elements */
9836 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9837 int xx = pos_content % 3;
9838 int yy = pos_content / 3;
9843 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9844 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9848 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9850 int xx = x - bx + 1;
9851 int yy = y - by + 1;
9853 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9854 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9858 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9861 void CheckExit(int x, int y)
9863 if (local_player->gems_still_needed > 0 ||
9864 local_player->sokobanfields_still_needed > 0 ||
9865 local_player->lights_still_needed > 0)
9867 int element = Feld[x][y];
9868 int graphic = el2img(element);
9870 if (IS_ANIMATED(graphic))
9871 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9876 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9879 Feld[x][y] = EL_EXIT_OPENING;
9881 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9884 void CheckExitEM(int x, int y)
9886 if (local_player->gems_still_needed > 0 ||
9887 local_player->sokobanfields_still_needed > 0 ||
9888 local_player->lights_still_needed > 0)
9890 int element = Feld[x][y];
9891 int graphic = el2img(element);
9893 if (IS_ANIMATED(graphic))
9894 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9899 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9902 Feld[x][y] = EL_EM_EXIT_OPENING;
9904 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9907 void CheckExitSteel(int x, int y)
9909 if (local_player->gems_still_needed > 0 ||
9910 local_player->sokobanfields_still_needed > 0 ||
9911 local_player->lights_still_needed > 0)
9913 int element = Feld[x][y];
9914 int graphic = el2img(element);
9916 if (IS_ANIMATED(graphic))
9917 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9922 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9925 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9927 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9930 void CheckExitSteelEM(int x, int y)
9932 if (local_player->gems_still_needed > 0 ||
9933 local_player->sokobanfields_still_needed > 0 ||
9934 local_player->lights_still_needed > 0)
9936 int element = Feld[x][y];
9937 int graphic = el2img(element);
9939 if (IS_ANIMATED(graphic))
9940 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9945 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9948 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9950 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9953 void CheckExitSP(int x, int y)
9955 if (local_player->gems_still_needed > 0)
9957 int element = Feld[x][y];
9958 int graphic = el2img(element);
9960 if (IS_ANIMATED(graphic))
9961 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9966 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9969 Feld[x][y] = EL_SP_EXIT_OPENING;
9971 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9974 static void CloseAllOpenTimegates()
9978 SCAN_PLAYFIELD(x, y)
9980 int element = Feld[x][y];
9982 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9984 Feld[x][y] = EL_TIMEGATE_CLOSING;
9986 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9991 void DrawTwinkleOnField(int x, int y)
9993 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9996 if (Feld[x][y] == EL_BD_DIAMOND)
9999 if (MovDelay[x][y] == 0) /* next animation frame */
10000 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10002 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10006 DrawLevelElementAnimation(x, y, Feld[x][y]);
10008 if (MovDelay[x][y] != 0)
10010 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10011 10 - MovDelay[x][y]);
10013 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10018 void MauerWaechst(int x, int y)
10022 if (!MovDelay[x][y]) /* next animation frame */
10023 MovDelay[x][y] = 3 * delay;
10025 if (MovDelay[x][y]) /* wait some time before next frame */
10029 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10031 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10032 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10034 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10037 if (!MovDelay[x][y])
10039 if (MovDir[x][y] == MV_LEFT)
10041 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10042 TEST_DrawLevelField(x - 1, y);
10044 else if (MovDir[x][y] == MV_RIGHT)
10046 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10047 TEST_DrawLevelField(x + 1, y);
10049 else if (MovDir[x][y] == MV_UP)
10051 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10052 TEST_DrawLevelField(x, y - 1);
10056 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10057 TEST_DrawLevelField(x, y + 1);
10060 Feld[x][y] = Store[x][y];
10062 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10063 TEST_DrawLevelField(x, y);
10068 void MauerAbleger(int ax, int ay)
10070 int element = Feld[ax][ay];
10071 int graphic = el2img(element);
10072 boolean oben_frei = FALSE, unten_frei = FALSE;
10073 boolean links_frei = FALSE, rechts_frei = FALSE;
10074 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10075 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10076 boolean new_wall = FALSE;
10078 if (IS_ANIMATED(graphic))
10079 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10081 if (!MovDelay[ax][ay]) /* start building new wall */
10082 MovDelay[ax][ay] = 6;
10084 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10086 MovDelay[ax][ay]--;
10087 if (MovDelay[ax][ay])
10091 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10093 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10095 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10097 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10098 rechts_frei = TRUE;
10100 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10101 element == EL_EXPANDABLE_WALL_ANY)
10105 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10106 Store[ax][ay-1] = element;
10107 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10108 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10109 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10110 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10115 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10116 Store[ax][ay+1] = element;
10117 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10118 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10119 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10120 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10125 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126 element == EL_EXPANDABLE_WALL_ANY ||
10127 element == EL_EXPANDABLE_WALL ||
10128 element == EL_BD_EXPANDABLE_WALL)
10132 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10133 Store[ax-1][ay] = element;
10134 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10135 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10136 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10137 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10143 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10144 Store[ax+1][ay] = element;
10145 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10146 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10147 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10148 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10153 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10154 TEST_DrawLevelField(ax, ay);
10156 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10157 oben_massiv = TRUE;
10158 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10159 unten_massiv = TRUE;
10160 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10161 links_massiv = TRUE;
10162 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10163 rechts_massiv = TRUE;
10165 if (((oben_massiv && unten_massiv) ||
10166 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10167 element == EL_EXPANDABLE_WALL) &&
10168 ((links_massiv && rechts_massiv) ||
10169 element == EL_EXPANDABLE_WALL_VERTICAL))
10170 Feld[ax][ay] = EL_WALL;
10173 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10176 void MauerAblegerStahl(int ax, int ay)
10178 int element = Feld[ax][ay];
10179 int graphic = el2img(element);
10180 boolean oben_frei = FALSE, unten_frei = FALSE;
10181 boolean links_frei = FALSE, rechts_frei = FALSE;
10182 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10183 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10184 boolean new_wall = FALSE;
10186 if (IS_ANIMATED(graphic))
10187 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10189 if (!MovDelay[ax][ay]) /* start building new wall */
10190 MovDelay[ax][ay] = 6;
10192 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10194 MovDelay[ax][ay]--;
10195 if (MovDelay[ax][ay])
10199 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10201 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10203 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10205 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10206 rechts_frei = TRUE;
10208 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10209 element == EL_EXPANDABLE_STEELWALL_ANY)
10213 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10214 Store[ax][ay-1] = element;
10215 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10216 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10217 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10218 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10223 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10224 Store[ax][ay+1] = element;
10225 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10226 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10227 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10228 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10233 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10234 element == EL_EXPANDABLE_STEELWALL_ANY)
10238 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10239 Store[ax-1][ay] = element;
10240 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10241 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10242 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10243 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10249 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10250 Store[ax+1][ay] = element;
10251 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10252 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10253 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10254 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10259 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10260 oben_massiv = TRUE;
10261 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10262 unten_massiv = TRUE;
10263 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10264 links_massiv = TRUE;
10265 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10266 rechts_massiv = TRUE;
10268 if (((oben_massiv && unten_massiv) ||
10269 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10270 ((links_massiv && rechts_massiv) ||
10271 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10272 Feld[ax][ay] = EL_STEELWALL;
10275 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10278 void CheckForDragon(int x, int y)
10281 boolean dragon_found = FALSE;
10282 static int xy[4][2] =
10290 for (i = 0; i < NUM_DIRECTIONS; i++)
10292 for (j = 0; j < 4; j++)
10294 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10296 if (IN_LEV_FIELD(xx, yy) &&
10297 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10299 if (Feld[xx][yy] == EL_DRAGON)
10300 dragon_found = TRUE;
10309 for (i = 0; i < NUM_DIRECTIONS; i++)
10311 for (j = 0; j < 3; j++)
10313 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10315 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10317 Feld[xx][yy] = EL_EMPTY;
10318 TEST_DrawLevelField(xx, yy);
10327 static void InitBuggyBase(int x, int y)
10329 int element = Feld[x][y];
10330 int activating_delay = FRAMES_PER_SECOND / 4;
10332 ChangeDelay[x][y] =
10333 (element == EL_SP_BUGGY_BASE ?
10334 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10335 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10337 element == EL_SP_BUGGY_BASE_ACTIVE ?
10338 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10341 static void WarnBuggyBase(int x, int y)
10344 static int xy[4][2] =
10352 for (i = 0; i < NUM_DIRECTIONS; i++)
10354 int xx = x + xy[i][0];
10355 int yy = y + xy[i][1];
10357 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10359 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10366 static void InitTrap(int x, int y)
10368 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10371 static void ActivateTrap(int x, int y)
10373 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10376 static void ChangeActiveTrap(int x, int y)
10378 int graphic = IMG_TRAP_ACTIVE;
10380 /* if new animation frame was drawn, correct crumbled sand border */
10381 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10382 TEST_DrawLevelFieldCrumbled(x, y);
10385 static int getSpecialActionElement(int element, int number, int base_element)
10387 return (element != EL_EMPTY ? element :
10388 number != -1 ? base_element + number - 1 :
10392 static int getModifiedActionNumber(int value_old, int operator, int operand,
10393 int value_min, int value_max)
10395 int value_new = (operator == CA_MODE_SET ? operand :
10396 operator == CA_MODE_ADD ? value_old + operand :
10397 operator == CA_MODE_SUBTRACT ? value_old - operand :
10398 operator == CA_MODE_MULTIPLY ? value_old * operand :
10399 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10400 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10403 return (value_new < value_min ? value_min :
10404 value_new > value_max ? value_max :
10408 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10410 struct ElementInfo *ei = &element_info[element];
10411 struct ElementChangeInfo *change = &ei->change_page[page];
10412 int target_element = change->target_element;
10413 int action_type = change->action_type;
10414 int action_mode = change->action_mode;
10415 int action_arg = change->action_arg;
10416 int action_element = change->action_element;
10419 if (!change->has_action)
10422 /* ---------- determine action paramater values -------------------------- */
10424 int level_time_value =
10425 (level.time > 0 ? TimeLeft :
10428 int action_arg_element_raw =
10429 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10430 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10431 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10432 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10433 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10434 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10435 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10437 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10440 if (action_arg_element_raw == EL_GROUP_START)
10441 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10444 int action_arg_direction =
10445 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10446 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10447 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10448 change->actual_trigger_side :
10449 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10450 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10453 int action_arg_number_min =
10454 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10457 int action_arg_number_max =
10458 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10459 action_type == CA_SET_LEVEL_GEMS ? 999 :
10460 action_type == CA_SET_LEVEL_TIME ? 9999 :
10461 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10462 action_type == CA_SET_CE_VALUE ? 9999 :
10463 action_type == CA_SET_CE_SCORE ? 9999 :
10466 int action_arg_number_reset =
10467 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10468 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10469 action_type == CA_SET_LEVEL_TIME ? level.time :
10470 action_type == CA_SET_LEVEL_SCORE ? 0 :
10471 #if USE_NEW_CUSTOM_VALUE
10472 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10474 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10476 action_type == CA_SET_CE_SCORE ? 0 :
10479 int action_arg_number =
10480 (action_arg <= CA_ARG_MAX ? action_arg :
10481 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10482 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10483 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10484 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10485 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10486 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10487 #if USE_NEW_CUSTOM_VALUE
10488 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10490 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10492 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10493 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10494 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10495 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10496 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10497 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10498 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10499 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10500 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10501 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10502 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10503 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10504 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10505 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10508 int action_arg_number_old =
10509 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10510 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10511 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10512 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10513 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10516 int action_arg_number_new =
10517 getModifiedActionNumber(action_arg_number_old,
10518 action_mode, action_arg_number,
10519 action_arg_number_min, action_arg_number_max);
10522 int trigger_player_bits =
10523 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10524 change->actual_trigger_player_bits : change->trigger_player);
10526 int trigger_player_bits =
10527 (change->actual_trigger_player >= EL_PLAYER_1 &&
10528 change->actual_trigger_player <= EL_PLAYER_4 ?
10529 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10533 int action_arg_player_bits =
10534 (action_arg >= CA_ARG_PLAYER_1 &&
10535 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10536 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10537 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10540 /* ---------- execute action -------------------------------------------- */
10542 switch (action_type)
10549 /* ---------- level actions ------------------------------------------- */
10551 case CA_RESTART_LEVEL:
10553 game.restart_level = TRUE;
10558 case CA_SHOW_ENVELOPE:
10560 int element = getSpecialActionElement(action_arg_element,
10561 action_arg_number, EL_ENVELOPE_1);
10563 if (IS_ENVELOPE(element))
10564 local_player->show_envelope = element;
10569 case CA_SET_LEVEL_TIME:
10571 if (level.time > 0) /* only modify limited time value */
10573 TimeLeft = action_arg_number_new;
10576 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10578 DisplayGameControlValues();
10580 DrawGameValue_Time(TimeLeft);
10583 if (!TimeLeft && setup.time_limit)
10584 for (i = 0; i < MAX_PLAYERS; i++)
10585 KillPlayer(&stored_player[i]);
10591 case CA_SET_LEVEL_SCORE:
10593 local_player->score = action_arg_number_new;
10596 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10598 DisplayGameControlValues();
10600 DrawGameValue_Score(local_player->score);
10606 case CA_SET_LEVEL_GEMS:
10608 local_player->gems_still_needed = action_arg_number_new;
10611 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10613 DisplayGameControlValues();
10615 DrawGameValue_Emeralds(local_player->gems_still_needed);
10621 #if !USE_PLAYER_GRAVITY
10622 case CA_SET_LEVEL_GRAVITY:
10624 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10625 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10626 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10632 case CA_SET_LEVEL_WIND:
10634 game.wind_direction = action_arg_direction;
10639 case CA_SET_LEVEL_RANDOM_SEED:
10642 /* ensure that setting a new random seed while playing is predictable */
10643 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10645 InitRND(action_arg_number_new);
10649 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10657 for (i = 0; i < 9; i++)
10658 printf("%d, ", RND(2));
10666 /* ---------- player actions ------------------------------------------ */
10668 case CA_MOVE_PLAYER:
10670 /* automatically move to the next field in specified direction */
10671 for (i = 0; i < MAX_PLAYERS; i++)
10672 if (trigger_player_bits & (1 << i))
10673 stored_player[i].programmed_action = action_arg_direction;
10678 case CA_EXIT_PLAYER:
10680 for (i = 0; i < MAX_PLAYERS; i++)
10681 if (action_arg_player_bits & (1 << i))
10682 PlayerWins(&stored_player[i]);
10687 case CA_KILL_PLAYER:
10689 for (i = 0; i < MAX_PLAYERS; i++)
10690 if (action_arg_player_bits & (1 << i))
10691 KillPlayer(&stored_player[i]);
10696 case CA_SET_PLAYER_KEYS:
10698 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10699 int element = getSpecialActionElement(action_arg_element,
10700 action_arg_number, EL_KEY_1);
10702 if (IS_KEY(element))
10704 for (i = 0; i < MAX_PLAYERS; i++)
10706 if (trigger_player_bits & (1 << i))
10708 stored_player[i].key[KEY_NR(element)] = key_state;
10710 DrawGameDoorValues();
10718 case CA_SET_PLAYER_SPEED:
10721 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10724 for (i = 0; i < MAX_PLAYERS; i++)
10726 if (trigger_player_bits & (1 << i))
10728 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10730 if (action_arg == CA_ARG_SPEED_FASTER &&
10731 stored_player[i].cannot_move)
10733 action_arg_number = STEPSIZE_VERY_SLOW;
10735 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10736 action_arg == CA_ARG_SPEED_FASTER)
10738 action_arg_number = 2;
10739 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10742 else if (action_arg == CA_ARG_NUMBER_RESET)
10744 action_arg_number = level.initial_player_stepsize[i];
10748 getModifiedActionNumber(move_stepsize,
10751 action_arg_number_min,
10752 action_arg_number_max);
10754 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10761 case CA_SET_PLAYER_SHIELD:
10763 for (i = 0; i < MAX_PLAYERS; i++)
10765 if (trigger_player_bits & (1 << i))
10767 if (action_arg == CA_ARG_SHIELD_OFF)
10769 stored_player[i].shield_normal_time_left = 0;
10770 stored_player[i].shield_deadly_time_left = 0;
10772 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10774 stored_player[i].shield_normal_time_left = 999999;
10776 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10778 stored_player[i].shield_normal_time_left = 999999;
10779 stored_player[i].shield_deadly_time_left = 999999;
10787 #if USE_PLAYER_GRAVITY
10788 case CA_SET_PLAYER_GRAVITY:
10790 for (i = 0; i < MAX_PLAYERS; i++)
10792 if (trigger_player_bits & (1 << i))
10794 stored_player[i].gravity =
10795 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10796 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10797 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10798 stored_player[i].gravity);
10806 case CA_SET_PLAYER_ARTWORK:
10808 for (i = 0; i < MAX_PLAYERS; i++)
10810 if (trigger_player_bits & (1 << i))
10812 int artwork_element = action_arg_element;
10814 if (action_arg == CA_ARG_ELEMENT_RESET)
10816 (level.use_artwork_element[i] ? level.artwork_element[i] :
10817 stored_player[i].element_nr);
10819 #if USE_GFX_RESET_PLAYER_ARTWORK
10820 if (stored_player[i].artwork_element != artwork_element)
10821 stored_player[i].Frame = 0;
10824 stored_player[i].artwork_element = artwork_element;
10826 SetPlayerWaiting(&stored_player[i], FALSE);
10828 /* set number of special actions for bored and sleeping animation */
10829 stored_player[i].num_special_action_bored =
10830 get_num_special_action(artwork_element,
10831 ACTION_BORING_1, ACTION_BORING_LAST);
10832 stored_player[i].num_special_action_sleeping =
10833 get_num_special_action(artwork_element,
10834 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10841 case CA_SET_PLAYER_INVENTORY:
10843 for (i = 0; i < MAX_PLAYERS; i++)
10845 struct PlayerInfo *player = &stored_player[i];
10848 if (trigger_player_bits & (1 << i))
10850 int inventory_element = action_arg_element;
10852 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10853 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10854 action_arg == CA_ARG_ELEMENT_ACTION)
10856 int element = inventory_element;
10857 int collect_count = element_info[element].collect_count_initial;
10859 if (!IS_CUSTOM_ELEMENT(element))
10862 if (collect_count == 0)
10863 player->inventory_infinite_element = element;
10865 for (k = 0; k < collect_count; k++)
10866 if (player->inventory_size < MAX_INVENTORY_SIZE)
10867 player->inventory_element[player->inventory_size++] =
10870 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10871 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10872 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10874 if (player->inventory_infinite_element != EL_UNDEFINED &&
10875 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10876 action_arg_element_raw))
10877 player->inventory_infinite_element = EL_UNDEFINED;
10879 for (k = 0, j = 0; j < player->inventory_size; j++)
10881 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10882 action_arg_element_raw))
10883 player->inventory_element[k++] = player->inventory_element[j];
10886 player->inventory_size = k;
10888 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10890 if (player->inventory_size > 0)
10892 for (j = 0; j < player->inventory_size - 1; j++)
10893 player->inventory_element[j] = player->inventory_element[j + 1];
10895 player->inventory_size--;
10898 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10900 if (player->inventory_size > 0)
10901 player->inventory_size--;
10903 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10905 player->inventory_infinite_element = EL_UNDEFINED;
10906 player->inventory_size = 0;
10908 else if (action_arg == CA_ARG_INVENTORY_RESET)
10910 player->inventory_infinite_element = EL_UNDEFINED;
10911 player->inventory_size = 0;
10913 if (level.use_initial_inventory[i])
10915 for (j = 0; j < level.initial_inventory_size[i]; j++)
10917 int element = level.initial_inventory_content[i][j];
10918 int collect_count = element_info[element].collect_count_initial;
10920 if (!IS_CUSTOM_ELEMENT(element))
10923 if (collect_count == 0)
10924 player->inventory_infinite_element = element;
10926 for (k = 0; k < collect_count; k++)
10927 if (player->inventory_size < MAX_INVENTORY_SIZE)
10928 player->inventory_element[player->inventory_size++] =
10939 /* ---------- CE actions ---------------------------------------------- */
10941 case CA_SET_CE_VALUE:
10943 #if USE_NEW_CUSTOM_VALUE
10944 int last_ce_value = CustomValue[x][y];
10946 CustomValue[x][y] = action_arg_number_new;
10948 if (CustomValue[x][y] != last_ce_value)
10950 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10951 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10953 if (CustomValue[x][y] == 0)
10955 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10956 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10964 case CA_SET_CE_SCORE:
10966 #if USE_NEW_CUSTOM_VALUE
10967 int last_ce_score = ei->collect_score;
10969 ei->collect_score = action_arg_number_new;
10971 if (ei->collect_score != last_ce_score)
10973 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10974 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10976 if (ei->collect_score == 0)
10980 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10981 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10984 This is a very special case that seems to be a mixture between
10985 CheckElementChange() and CheckTriggeredElementChange(): while
10986 the first one only affects single elements that are triggered
10987 directly, the second one affects multiple elements in the playfield
10988 that are triggered indirectly by another element. This is a third
10989 case: Changing the CE score always affects multiple identical CEs,
10990 so every affected CE must be checked, not only the single CE for
10991 which the CE score was changed in the first place (as every instance
10992 of that CE shares the same CE score, and therefore also can change)!
10994 SCAN_PLAYFIELD(xx, yy)
10996 if (Feld[xx][yy] == element)
10997 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10998 CE_SCORE_GETS_ZERO);
11007 case CA_SET_CE_ARTWORK:
11009 int artwork_element = action_arg_element;
11010 boolean reset_frame = FALSE;
11013 if (action_arg == CA_ARG_ELEMENT_RESET)
11014 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11017 if (ei->gfx_element != artwork_element)
11018 reset_frame = TRUE;
11020 ei->gfx_element = artwork_element;
11022 SCAN_PLAYFIELD(xx, yy)
11024 if (Feld[xx][yy] == element)
11028 ResetGfxAnimation(xx, yy);
11029 ResetRandomAnimationValue(xx, yy);
11032 TEST_DrawLevelField(xx, yy);
11039 /* ---------- engine actions ------------------------------------------ */
11041 case CA_SET_ENGINE_SCAN_MODE:
11043 InitPlayfieldScanMode(action_arg);
11053 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11055 int old_element = Feld[x][y];
11056 int new_element = GetElementFromGroupElement(element);
11057 int previous_move_direction = MovDir[x][y];
11058 #if USE_NEW_CUSTOM_VALUE
11059 int last_ce_value = CustomValue[x][y];
11061 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11062 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11063 boolean add_player_onto_element = (new_element_is_player &&
11064 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11065 /* this breaks SnakeBite when a snake is
11066 halfway through a door that closes */
11067 /* NOW FIXED AT LEVEL INIT IN files.c */
11068 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11070 IS_WALKABLE(old_element));
11073 /* check if element under the player changes from accessible to unaccessible
11074 (needed for special case of dropping element which then changes) */
11075 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11076 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11084 if (!add_player_onto_element)
11086 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11087 RemoveMovingField(x, y);
11091 Feld[x][y] = new_element;
11093 #if !USE_GFX_RESET_GFX_ANIMATION
11094 ResetGfxAnimation(x, y);
11095 ResetRandomAnimationValue(x, y);
11098 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11099 MovDir[x][y] = previous_move_direction;
11101 #if USE_NEW_CUSTOM_VALUE
11102 if (element_info[new_element].use_last_ce_value)
11103 CustomValue[x][y] = last_ce_value;
11106 InitField_WithBug1(x, y, FALSE);
11108 new_element = Feld[x][y]; /* element may have changed */
11110 #if USE_GFX_RESET_GFX_ANIMATION
11111 ResetGfxAnimation(x, y);
11112 ResetRandomAnimationValue(x, y);
11115 TEST_DrawLevelField(x, y);
11117 if (GFX_CRUMBLED(new_element))
11118 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11122 /* check if element under the player changes from accessible to unaccessible
11123 (needed for special case of dropping element which then changes) */
11124 /* (must be checked after creating new element for walkable group elements) */
11125 #if USE_FIX_KILLED_BY_NON_WALKABLE
11126 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11127 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11134 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11135 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11144 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11145 if (new_element_is_player)
11146 RelocatePlayer(x, y, new_element);
11149 ChangeCount[x][y]++; /* count number of changes in the same frame */
11151 TestIfBadThingTouchesPlayer(x, y);
11152 TestIfPlayerTouchesCustomElement(x, y);
11153 TestIfElementTouchesCustomElement(x, y);
11156 static void CreateField(int x, int y, int element)
11158 CreateFieldExt(x, y, element, FALSE);
11161 static void CreateElementFromChange(int x, int y, int element)
11163 element = GET_VALID_RUNTIME_ELEMENT(element);
11165 #if USE_STOP_CHANGED_ELEMENTS
11166 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11168 int old_element = Feld[x][y];
11170 /* prevent changed element from moving in same engine frame
11171 unless both old and new element can either fall or move */
11172 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11173 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11178 CreateFieldExt(x, y, element, TRUE);
11181 static boolean ChangeElement(int x, int y, int element, int page)
11183 struct ElementInfo *ei = &element_info[element];
11184 struct ElementChangeInfo *change = &ei->change_page[page];
11185 int ce_value = CustomValue[x][y];
11186 int ce_score = ei->collect_score;
11187 int target_element;
11188 int old_element = Feld[x][y];
11190 /* always use default change event to prevent running into a loop */
11191 if (ChangeEvent[x][y] == -1)
11192 ChangeEvent[x][y] = CE_DELAY;
11194 if (ChangeEvent[x][y] == CE_DELAY)
11196 /* reset actual trigger element, trigger player and action element */
11197 change->actual_trigger_element = EL_EMPTY;
11198 change->actual_trigger_player = EL_EMPTY;
11199 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11200 change->actual_trigger_side = CH_SIDE_NONE;
11201 change->actual_trigger_ce_value = 0;
11202 change->actual_trigger_ce_score = 0;
11205 /* do not change elements more than a specified maximum number of changes */
11206 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11209 ChangeCount[x][y]++; /* count number of changes in the same frame */
11211 if (change->explode)
11218 if (change->use_target_content)
11220 boolean complete_replace = TRUE;
11221 boolean can_replace[3][3];
11224 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11227 boolean is_walkable;
11228 boolean is_diggable;
11229 boolean is_collectible;
11230 boolean is_removable;
11231 boolean is_destructible;
11232 int ex = x + xx - 1;
11233 int ey = y + yy - 1;
11234 int content_element = change->target_content.e[xx][yy];
11237 can_replace[xx][yy] = TRUE;
11239 if (ex == x && ey == y) /* do not check changing element itself */
11242 if (content_element == EL_EMPTY_SPACE)
11244 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11249 if (!IN_LEV_FIELD(ex, ey))
11251 can_replace[xx][yy] = FALSE;
11252 complete_replace = FALSE;
11259 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11260 e = MovingOrBlocked2Element(ex, ey);
11262 is_empty = (IS_FREE(ex, ey) ||
11263 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11265 is_walkable = (is_empty || IS_WALKABLE(e));
11266 is_diggable = (is_empty || IS_DIGGABLE(e));
11267 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11268 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11269 is_removable = (is_diggable || is_collectible);
11271 can_replace[xx][yy] =
11272 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11273 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11274 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11275 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11276 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11277 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11278 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11280 if (!can_replace[xx][yy])
11281 complete_replace = FALSE;
11284 if (!change->only_if_complete || complete_replace)
11286 boolean something_has_changed = FALSE;
11288 if (change->only_if_complete && change->use_random_replace &&
11289 RND(100) < change->random_percentage)
11292 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11294 int ex = x + xx - 1;
11295 int ey = y + yy - 1;
11296 int content_element;
11298 if (can_replace[xx][yy] && (!change->use_random_replace ||
11299 RND(100) < change->random_percentage))
11301 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11302 RemoveMovingField(ex, ey);
11304 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11306 content_element = change->target_content.e[xx][yy];
11307 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11308 ce_value, ce_score);
11310 CreateElementFromChange(ex, ey, target_element);
11312 something_has_changed = TRUE;
11314 /* for symmetry reasons, freeze newly created border elements */
11315 if (ex != x || ey != y)
11316 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11320 if (something_has_changed)
11322 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11323 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11329 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11330 ce_value, ce_score);
11332 if (element == EL_DIAGONAL_GROWING ||
11333 element == EL_DIAGONAL_SHRINKING)
11335 target_element = Store[x][y];
11337 Store[x][y] = EL_EMPTY;
11340 CreateElementFromChange(x, y, target_element);
11342 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11343 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11346 /* this uses direct change before indirect change */
11347 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11352 #if USE_NEW_DELAYED_ACTION
11354 static void HandleElementChange(int x, int y, int page)
11356 int element = MovingOrBlocked2Element(x, y);
11357 struct ElementInfo *ei = &element_info[element];
11358 struct ElementChangeInfo *change = &ei->change_page[page];
11359 boolean handle_action_before_change = FALSE;
11362 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11363 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11366 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11367 x, y, element, element_info[element].token_name);
11368 printf("HandleElementChange(): This should never happen!\n");
11373 /* this can happen with classic bombs on walkable, changing elements */
11374 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11377 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11378 ChangeDelay[x][y] = 0;
11384 if (ChangeDelay[x][y] == 0) /* initialize element change */
11386 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11388 if (change->can_change)
11391 /* !!! not clear why graphic animation should be reset at all here !!! */
11392 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11393 #if USE_GFX_RESET_WHEN_NOT_MOVING
11394 /* when a custom element is about to change (for example by change delay),
11395 do not reset graphic animation when the custom element is moving */
11396 if (!IS_MOVING(x, y))
11399 ResetGfxAnimation(x, y);
11400 ResetRandomAnimationValue(x, y);
11404 if (change->pre_change_function)
11405 change->pre_change_function(x, y);
11409 ChangeDelay[x][y]--;
11411 if (ChangeDelay[x][y] != 0) /* continue element change */
11413 if (change->can_change)
11415 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11417 if (IS_ANIMATED(graphic))
11418 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11420 if (change->change_function)
11421 change->change_function(x, y);
11424 else /* finish element change */
11426 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11428 page = ChangePage[x][y];
11429 ChangePage[x][y] = -1;
11431 change = &ei->change_page[page];
11434 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11436 ChangeDelay[x][y] = 1; /* try change after next move step */
11437 ChangePage[x][y] = page; /* remember page to use for change */
11443 /* special case: set new level random seed before changing element */
11444 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11445 handle_action_before_change = TRUE;
11447 if (change->has_action && handle_action_before_change)
11448 ExecuteCustomElementAction(x, y, element, page);
11451 if (change->can_change)
11453 if (ChangeElement(x, y, element, page))
11455 if (change->post_change_function)
11456 change->post_change_function(x, y);
11460 if (change->has_action && !handle_action_before_change)
11461 ExecuteCustomElementAction(x, y, element, page);
11467 static void HandleElementChange(int x, int y, int page)
11469 int element = MovingOrBlocked2Element(x, y);
11470 struct ElementInfo *ei = &element_info[element];
11471 struct ElementChangeInfo *change = &ei->change_page[page];
11474 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11477 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11478 x, y, element, element_info[element].token_name);
11479 printf("HandleElementChange(): This should never happen!\n");
11484 /* this can happen with classic bombs on walkable, changing elements */
11485 if (!CAN_CHANGE(element))
11488 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11489 ChangeDelay[x][y] = 0;
11495 if (ChangeDelay[x][y] == 0) /* initialize element change */
11497 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11499 ResetGfxAnimation(x, y);
11500 ResetRandomAnimationValue(x, y);
11502 if (change->pre_change_function)
11503 change->pre_change_function(x, y);
11506 ChangeDelay[x][y]--;
11508 if (ChangeDelay[x][y] != 0) /* continue element change */
11510 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11512 if (IS_ANIMATED(graphic))
11513 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11515 if (change->change_function)
11516 change->change_function(x, y);
11518 else /* finish element change */
11520 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11522 page = ChangePage[x][y];
11523 ChangePage[x][y] = -1;
11525 change = &ei->change_page[page];
11528 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11530 ChangeDelay[x][y] = 1; /* try change after next move step */
11531 ChangePage[x][y] = page; /* remember page to use for change */
11536 if (ChangeElement(x, y, element, page))
11538 if (change->post_change_function)
11539 change->post_change_function(x, y);
11546 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11547 int trigger_element,
11549 int trigger_player,
11553 boolean change_done_any = FALSE;
11554 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11557 if (!(trigger_events[trigger_element][trigger_event]))
11561 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11562 trigger_event, recursion_loop_depth, recursion_loop_detected,
11563 recursion_loop_element, EL_NAME(recursion_loop_element));
11566 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11568 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11570 int element = EL_CUSTOM_START + i;
11571 boolean change_done = FALSE;
11574 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11575 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11578 for (p = 0; p < element_info[element].num_change_pages; p++)
11580 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11582 if (change->can_change_or_has_action &&
11583 change->has_event[trigger_event] &&
11584 change->trigger_side & trigger_side &&
11585 change->trigger_player & trigger_player &&
11586 change->trigger_page & trigger_page_bits &&
11587 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11589 change->actual_trigger_element = trigger_element;
11590 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11591 change->actual_trigger_player_bits = trigger_player;
11592 change->actual_trigger_side = trigger_side;
11593 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11594 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11597 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11598 element, EL_NAME(element), p);
11601 if ((change->can_change && !change_done) || change->has_action)
11605 SCAN_PLAYFIELD(x, y)
11607 if (Feld[x][y] == element)
11609 if (change->can_change && !change_done)
11611 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11612 /* if element already changed in this frame, not only prevent
11613 another element change (checked in ChangeElement()), but
11614 also prevent additional element actions for this element */
11616 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11617 !level.use_action_after_change_bug)
11622 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11623 element, EL_NAME(element), p);
11626 ChangeDelay[x][y] = 1;
11627 ChangeEvent[x][y] = trigger_event;
11629 HandleElementChange(x, y, p);
11631 #if USE_NEW_DELAYED_ACTION
11632 else if (change->has_action)
11634 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11635 /* if element already changed in this frame, not only prevent
11636 another element change (checked in ChangeElement()), but
11637 also prevent additional element actions for this element */
11639 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11640 !level.use_action_after_change_bug)
11646 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11647 element, EL_NAME(element), p);
11650 ExecuteCustomElementAction(x, y, element, p);
11651 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11654 if (change->has_action)
11656 ExecuteCustomElementAction(x, y, element, p);
11657 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11663 if (change->can_change)
11665 change_done = TRUE;
11666 change_done_any = TRUE;
11669 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11670 element, EL_NAME(element), p);
11679 RECURSION_LOOP_DETECTION_END();
11681 return change_done_any;
11684 static boolean CheckElementChangeExt(int x, int y,
11686 int trigger_element,
11688 int trigger_player,
11691 boolean change_done = FALSE;
11694 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11695 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11698 if (Feld[x][y] == EL_BLOCKED)
11700 Blocked2Moving(x, y, &x, &y);
11701 element = Feld[x][y];
11705 /* check if element has already changed */
11706 if (Feld[x][y] != element)
11709 /* check if element has already changed or is about to change after moving */
11710 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11711 Feld[x][y] != element) ||
11713 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11714 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11715 ChangePage[x][y] != -1)))
11720 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11721 trigger_event, recursion_loop_depth, recursion_loop_detected,
11722 recursion_loop_element, EL_NAME(recursion_loop_element));
11725 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11728 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11731 for (p = 0; p < element_info[element].num_change_pages; p++)
11733 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11735 /* check trigger element for all events where the element that is checked
11736 for changing interacts with a directly adjacent element -- this is
11737 different to element changes that affect other elements to change on the
11738 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11739 boolean check_trigger_element =
11740 (trigger_event == CE_TOUCHING_X ||
11741 trigger_event == CE_HITTING_X ||
11742 trigger_event == CE_HIT_BY_X ||
11744 /* this one was forgotten until 3.2.3 */
11745 trigger_event == CE_DIGGING_X);
11748 if (change->can_change_or_has_action &&
11749 change->has_event[trigger_event] &&
11750 change->trigger_side & trigger_side &&
11751 change->trigger_player & trigger_player &&
11752 (!check_trigger_element ||
11753 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11755 change->actual_trigger_element = trigger_element;
11756 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11757 change->actual_trigger_player_bits = trigger_player;
11758 change->actual_trigger_side = trigger_side;
11759 change->actual_trigger_ce_value = CustomValue[x][y];
11760 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11762 /* special case: trigger element not at (x,y) position for some events */
11763 if (check_trigger_element)
11775 { 0, 0 }, { 0, 0 }, { 0, 0 },
11779 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11780 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11782 change->actual_trigger_ce_value = CustomValue[xx][yy];
11783 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11786 if (change->can_change && !change_done)
11788 ChangeDelay[x][y] = 1;
11789 ChangeEvent[x][y] = trigger_event;
11791 HandleElementChange(x, y, p);
11793 change_done = TRUE;
11795 #if USE_NEW_DELAYED_ACTION
11796 else if (change->has_action)
11798 ExecuteCustomElementAction(x, y, element, p);
11799 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11802 if (change->has_action)
11804 ExecuteCustomElementAction(x, y, element, p);
11805 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11811 RECURSION_LOOP_DETECTION_END();
11813 return change_done;
11816 static void PlayPlayerSound(struct PlayerInfo *player)
11818 int jx = player->jx, jy = player->jy;
11819 int sound_element = player->artwork_element;
11820 int last_action = player->last_action_waiting;
11821 int action = player->action_waiting;
11823 if (player->is_waiting)
11825 if (action != last_action)
11826 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11828 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11832 if (action != last_action)
11833 StopSound(element_info[sound_element].sound[last_action]);
11835 if (last_action == ACTION_SLEEPING)
11836 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11840 static void PlayAllPlayersSound()
11844 for (i = 0; i < MAX_PLAYERS; i++)
11845 if (stored_player[i].active)
11846 PlayPlayerSound(&stored_player[i]);
11849 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11851 boolean last_waiting = player->is_waiting;
11852 int move_dir = player->MovDir;
11854 player->dir_waiting = move_dir;
11855 player->last_action_waiting = player->action_waiting;
11859 if (!last_waiting) /* not waiting -> waiting */
11861 player->is_waiting = TRUE;
11863 player->frame_counter_bored =
11865 game.player_boring_delay_fixed +
11866 GetSimpleRandom(game.player_boring_delay_random);
11867 player->frame_counter_sleeping =
11869 game.player_sleeping_delay_fixed +
11870 GetSimpleRandom(game.player_sleeping_delay_random);
11872 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11875 if (game.player_sleeping_delay_fixed +
11876 game.player_sleeping_delay_random > 0 &&
11877 player->anim_delay_counter == 0 &&
11878 player->post_delay_counter == 0 &&
11879 FrameCounter >= player->frame_counter_sleeping)
11880 player->is_sleeping = TRUE;
11881 else if (game.player_boring_delay_fixed +
11882 game.player_boring_delay_random > 0 &&
11883 FrameCounter >= player->frame_counter_bored)
11884 player->is_bored = TRUE;
11886 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11887 player->is_bored ? ACTION_BORING :
11890 if (player->is_sleeping && player->use_murphy)
11892 /* special case for sleeping Murphy when leaning against non-free tile */
11894 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11895 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11896 !IS_MOVING(player->jx - 1, player->jy)))
11897 move_dir = MV_LEFT;
11898 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11899 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11900 !IS_MOVING(player->jx + 1, player->jy)))
11901 move_dir = MV_RIGHT;
11903 player->is_sleeping = FALSE;
11905 player->dir_waiting = move_dir;
11908 if (player->is_sleeping)
11910 if (player->num_special_action_sleeping > 0)
11912 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11914 int last_special_action = player->special_action_sleeping;
11915 int num_special_action = player->num_special_action_sleeping;
11916 int special_action =
11917 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11918 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11919 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11920 last_special_action + 1 : ACTION_SLEEPING);
11921 int special_graphic =
11922 el_act_dir2img(player->artwork_element, special_action, move_dir);
11924 player->anim_delay_counter =
11925 graphic_info[special_graphic].anim_delay_fixed +
11926 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11927 player->post_delay_counter =
11928 graphic_info[special_graphic].post_delay_fixed +
11929 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11931 player->special_action_sleeping = special_action;
11934 if (player->anim_delay_counter > 0)
11936 player->action_waiting = player->special_action_sleeping;
11937 player->anim_delay_counter--;
11939 else if (player->post_delay_counter > 0)
11941 player->post_delay_counter--;
11945 else if (player->is_bored)
11947 if (player->num_special_action_bored > 0)
11949 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11951 int special_action =
11952 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11953 int special_graphic =
11954 el_act_dir2img(player->artwork_element, special_action, move_dir);
11956 player->anim_delay_counter =
11957 graphic_info[special_graphic].anim_delay_fixed +
11958 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11959 player->post_delay_counter =
11960 graphic_info[special_graphic].post_delay_fixed +
11961 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11963 player->special_action_bored = special_action;
11966 if (player->anim_delay_counter > 0)
11968 player->action_waiting = player->special_action_bored;
11969 player->anim_delay_counter--;
11971 else if (player->post_delay_counter > 0)
11973 player->post_delay_counter--;
11978 else if (last_waiting) /* waiting -> not waiting */
11980 player->is_waiting = FALSE;
11981 player->is_bored = FALSE;
11982 player->is_sleeping = FALSE;
11984 player->frame_counter_bored = -1;
11985 player->frame_counter_sleeping = -1;
11987 player->anim_delay_counter = 0;
11988 player->post_delay_counter = 0;
11990 player->dir_waiting = player->MovDir;
11991 player->action_waiting = ACTION_DEFAULT;
11993 player->special_action_bored = ACTION_DEFAULT;
11994 player->special_action_sleeping = ACTION_DEFAULT;
11998 static void CheckSingleStepMode(struct PlayerInfo *player)
12000 if (tape.single_step && tape.recording && !tape.pausing)
12002 /* as it is called "single step mode", just return to pause mode when the
12003 player stopped moving after one tile (or never starts moving at all) */
12004 if (!player->is_moving && !player->is_pushing)
12006 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12007 SnapField(player, 0, 0); /* stop snapping */
12012 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12014 int left = player_action & JOY_LEFT;
12015 int right = player_action & JOY_RIGHT;
12016 int up = player_action & JOY_UP;
12017 int down = player_action & JOY_DOWN;
12018 int button1 = player_action & JOY_BUTTON_1;
12019 int button2 = player_action & JOY_BUTTON_2;
12020 int dx = (left ? -1 : right ? 1 : 0);
12021 int dy = (up ? -1 : down ? 1 : 0);
12023 if (!player->active || tape.pausing)
12029 SnapField(player, dx, dy);
12033 DropElement(player);
12035 MovePlayer(player, dx, dy);
12038 CheckSingleStepMode(player);
12040 SetPlayerWaiting(player, FALSE);
12042 return player_action;
12046 /* no actions for this player (no input at player's configured device) */
12048 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12049 SnapField(player, 0, 0);
12050 CheckGravityMovementWhenNotMoving(player);
12052 if (player->MovPos == 0)
12053 SetPlayerWaiting(player, TRUE);
12055 if (player->MovPos == 0) /* needed for tape.playing */
12056 player->is_moving = FALSE;
12058 player->is_dropping = FALSE;
12059 player->is_dropping_pressed = FALSE;
12060 player->drop_pressed_delay = 0;
12062 CheckSingleStepMode(player);
12068 static void CheckLevelTime()
12072 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12073 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12075 if (level.native_em_level->lev->home == 0) /* all players at home */
12077 PlayerWins(local_player);
12079 AllPlayersGone = TRUE;
12081 level.native_em_level->lev->home = -1;
12084 if (level.native_em_level->ply[0]->alive == 0 &&
12085 level.native_em_level->ply[1]->alive == 0 &&
12086 level.native_em_level->ply[2]->alive == 0 &&
12087 level.native_em_level->ply[3]->alive == 0) /* all dead */
12088 AllPlayersGone = TRUE;
12090 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12092 if (game_sp.LevelSolved &&
12093 !game_sp.GameOver) /* game won */
12095 PlayerWins(local_player);
12097 game_sp.GameOver = TRUE;
12099 AllPlayersGone = TRUE;
12102 if (game_sp.GameOver) /* game lost */
12103 AllPlayersGone = TRUE;
12106 if (TimeFrames >= FRAMES_PER_SECOND)
12111 for (i = 0; i < MAX_PLAYERS; i++)
12113 struct PlayerInfo *player = &stored_player[i];
12115 if (SHIELD_ON(player))
12117 player->shield_normal_time_left--;
12119 if (player->shield_deadly_time_left > 0)
12120 player->shield_deadly_time_left--;
12124 if (!local_player->LevelSolved && !level.use_step_counter)
12132 if (TimeLeft <= 10 && setup.time_limit)
12133 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12136 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12137 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12139 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12141 /* (already called by UpdateAndDisplayGameControlValues() below) */
12142 // DisplayGameControlValues();
12144 DrawGameValue_Time(TimeLeft);
12147 if (!TimeLeft && setup.time_limit)
12149 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150 level.native_em_level->lev->killed_out_of_time = TRUE;
12152 for (i = 0; i < MAX_PLAYERS; i++)
12153 KillPlayer(&stored_player[i]);
12157 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12159 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12161 /* (already called by UpdateAndDisplayGameControlValues() below) */
12162 // DisplayGameControlValues();
12165 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12166 DrawGameValue_Time(TimePlayed);
12169 level.native_em_level->lev->time =
12170 (game.no_time_limit ? TimePlayed : TimeLeft);
12173 if (tape.recording || tape.playing)
12174 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12178 UpdateAndDisplayGameControlValues();
12180 UpdateGameDoorValues();
12181 DrawGameDoorValues();
12185 void AdvanceFrameAndPlayerCounters(int player_nr)
12189 /* advance frame counters (global frame counter and time frame counter) */
12193 /* advance player counters (counters for move delay, move animation etc.) */
12194 for (i = 0; i < MAX_PLAYERS; i++)
12196 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12197 int move_delay_value = stored_player[i].move_delay_value;
12198 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12200 if (!advance_player_counters) /* not all players may be affected */
12203 #if USE_NEW_PLAYER_ANIM
12204 if (move_frames == 0) /* less than one move per game frame */
12206 int stepsize = TILEX / move_delay_value;
12207 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12208 int count = (stored_player[i].is_moving ?
12209 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12211 if (count % delay == 0)
12216 stored_player[i].Frame += move_frames;
12218 if (stored_player[i].MovPos != 0)
12219 stored_player[i].StepFrame += move_frames;
12221 if (stored_player[i].move_delay > 0)
12222 stored_player[i].move_delay--;
12224 /* due to bugs in previous versions, counter must count up, not down */
12225 if (stored_player[i].push_delay != -1)
12226 stored_player[i].push_delay++;
12228 if (stored_player[i].drop_delay > 0)
12229 stored_player[i].drop_delay--;
12231 if (stored_player[i].is_dropping_pressed)
12232 stored_player[i].drop_pressed_delay++;
12236 void StartGameActions(boolean init_network_game, boolean record_tape,
12239 unsigned int new_random_seed = InitRND(random_seed);
12242 TapeStartRecording(new_random_seed);
12244 #if defined(NETWORK_AVALIABLE)
12245 if (init_network_game)
12247 SendToServer_StartPlaying();
12258 static unsigned int game_frame_delay = 0;
12259 unsigned int game_frame_delay_value;
12260 byte *recorded_player_action;
12261 byte summarized_player_action = 0;
12262 byte tape_action[MAX_PLAYERS];
12265 /* detect endless loops, caused by custom element programming */
12266 if (recursion_loop_detected && recursion_loop_depth == 0)
12268 char *message = getStringCat3("Internal Error! Element ",
12269 EL_NAME(recursion_loop_element),
12270 " caused endless loop! Quit the game?");
12272 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12273 EL_NAME(recursion_loop_element));
12275 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12277 recursion_loop_detected = FALSE; /* if game should be continued */
12284 if (game.restart_level)
12285 StartGameActions(options.network, setup.autorecord, level.random_seed);
12287 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12288 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12290 if (level.native_em_level->lev->home == 0) /* all players at home */
12292 PlayerWins(local_player);
12294 AllPlayersGone = TRUE;
12296 level.native_em_level->lev->home = -1;
12299 if (level.native_em_level->ply[0]->alive == 0 &&
12300 level.native_em_level->ply[1]->alive == 0 &&
12301 level.native_em_level->ply[2]->alive == 0 &&
12302 level.native_em_level->ply[3]->alive == 0) /* all dead */
12303 AllPlayersGone = TRUE;
12305 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12307 if (game_sp.LevelSolved &&
12308 !game_sp.GameOver) /* game won */
12310 PlayerWins(local_player);
12312 game_sp.GameOver = TRUE;
12314 AllPlayersGone = TRUE;
12317 if (game_sp.GameOver) /* game lost */
12318 AllPlayersGone = TRUE;
12321 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12324 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12327 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12330 game_frame_delay_value =
12331 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12333 if (tape.playing && tape.warp_forward && !tape.pausing)
12334 game_frame_delay_value = 0;
12336 /* ---------- main game synchronization point ---------- */
12338 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12340 if (network_playing && !network_player_action_received)
12342 /* try to get network player actions in time */
12344 #if defined(NETWORK_AVALIABLE)
12345 /* last chance to get network player actions without main loop delay */
12346 HandleNetworking();
12349 /* game was quit by network peer */
12350 if (game_status != GAME_MODE_PLAYING)
12353 if (!network_player_action_received)
12354 return; /* failed to get network player actions in time */
12356 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12362 /* at this point we know that we really continue executing the game */
12364 network_player_action_received = FALSE;
12366 /* when playing tape, read previously recorded player input from tape data */
12367 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12370 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12375 if (tape.set_centered_player)
12377 game.centered_player_nr_next = tape.centered_player_nr_next;
12378 game.set_centered_player = TRUE;
12381 for (i = 0; i < MAX_PLAYERS; i++)
12383 summarized_player_action |= stored_player[i].action;
12386 if (!network_playing && (game.team_mode || tape.playing))
12387 stored_player[i].effective_action = stored_player[i].action;
12389 if (!network_playing)
12390 stored_player[i].effective_action = stored_player[i].action;
12394 #if defined(NETWORK_AVALIABLE)
12395 if (network_playing)
12396 SendToServer_MovePlayer(summarized_player_action);
12399 if (!options.network && !game.team_mode)
12400 local_player->effective_action = summarized_player_action;
12402 if (tape.recording &&
12404 setup.input_on_focus &&
12405 game.centered_player_nr != -1)
12407 for (i = 0; i < MAX_PLAYERS; i++)
12408 stored_player[i].effective_action =
12409 (i == game.centered_player_nr ? summarized_player_action : 0);
12412 if (recorded_player_action != NULL)
12413 for (i = 0; i < MAX_PLAYERS; i++)
12414 stored_player[i].effective_action = recorded_player_action[i];
12416 for (i = 0; i < MAX_PLAYERS; i++)
12418 tape_action[i] = stored_player[i].effective_action;
12421 /* (this may happen in the RND game engine if a player was not present on
12422 the playfield on level start, but appeared later from a custom element */
12423 if (tape.recording &&
12426 !tape.player_participates[i])
12427 tape.player_participates[i] = TRUE;
12429 /* (this can only happen in the R'n'D game engine) */
12430 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12431 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12435 /* only record actions from input devices, but not programmed actions */
12436 if (tape.recording)
12437 TapeRecordAction(tape_action);
12439 #if USE_NEW_PLAYER_ASSIGNMENTS
12441 if (game.team_mode)
12444 byte mapped_action[MAX_PLAYERS];
12446 #if DEBUG_PLAYER_ACTIONS
12448 for (i = 0; i < MAX_PLAYERS; i++)
12449 printf(" %d, ", stored_player[i].effective_action);
12452 for (i = 0; i < MAX_PLAYERS; i++)
12453 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12455 for (i = 0; i < MAX_PLAYERS; i++)
12456 stored_player[i].effective_action = mapped_action[i];
12458 #if DEBUG_PLAYER_ACTIONS
12460 for (i = 0; i < MAX_PLAYERS; i++)
12461 printf(" %d, ", stored_player[i].effective_action);
12465 #if DEBUG_PLAYER_ACTIONS
12469 for (i = 0; i < MAX_PLAYERS; i++)
12470 printf(" %d, ", stored_player[i].effective_action);
12477 printf("::: summarized_player_action == %d\n",
12478 local_player->effective_action);
12485 #if DEBUG_INIT_PLAYER
12488 printf("Player status (final):\n");
12490 for (i = 0; i < MAX_PLAYERS; i++)
12492 struct PlayerInfo *player = &stored_player[i];
12494 printf("- player %d: present == %d, connected == %d, active == %d",
12500 if (local_player == player)
12501 printf(" (local player)");
12511 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12513 GameActions_EM_Main();
12515 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12517 GameActions_SP_Main();
12525 void GameActions_EM_Main()
12527 byte effective_action[MAX_PLAYERS];
12528 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12531 for (i = 0; i < MAX_PLAYERS; i++)
12532 effective_action[i] = stored_player[i].effective_action;
12534 GameActions_EM(effective_action, warp_mode);
12538 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12541 void GameActions_SP_Main()
12543 byte effective_action[MAX_PLAYERS];
12544 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12547 for (i = 0; i < MAX_PLAYERS; i++)
12548 effective_action[i] = stored_player[i].effective_action;
12550 GameActions_SP(effective_action, warp_mode);
12554 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12557 void GameActions_RND()
12559 int magic_wall_x = 0, magic_wall_y = 0;
12560 int i, x, y, element, graphic;
12562 InitPlayfieldScanModeVars();
12564 #if USE_ONE_MORE_CHANGE_PER_FRAME
12565 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12567 SCAN_PLAYFIELD(x, y)
12569 ChangeCount[x][y] = 0;
12570 ChangeEvent[x][y] = -1;
12575 if (game.set_centered_player)
12577 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12579 /* switching to "all players" only possible if all players fit to screen */
12580 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12582 game.centered_player_nr_next = game.centered_player_nr;
12583 game.set_centered_player = FALSE;
12586 /* do not switch focus to non-existing (or non-active) player */
12587 if (game.centered_player_nr_next >= 0 &&
12588 !stored_player[game.centered_player_nr_next].active)
12590 game.centered_player_nr_next = game.centered_player_nr;
12591 game.set_centered_player = FALSE;
12595 if (game.set_centered_player &&
12596 ScreenMovPos == 0) /* screen currently aligned at tile position */
12600 if (game.centered_player_nr_next == -1)
12602 setScreenCenteredToAllPlayers(&sx, &sy);
12606 sx = stored_player[game.centered_player_nr_next].jx;
12607 sy = stored_player[game.centered_player_nr_next].jy;
12610 game.centered_player_nr = game.centered_player_nr_next;
12611 game.set_centered_player = FALSE;
12613 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12614 DrawGameDoorValues();
12617 for (i = 0; i < MAX_PLAYERS; i++)
12619 int actual_player_action = stored_player[i].effective_action;
12622 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12623 - rnd_equinox_tetrachloride 048
12624 - rnd_equinox_tetrachloride_ii 096
12625 - rnd_emanuel_schmieg 002
12626 - doctor_sloan_ww 001, 020
12628 if (stored_player[i].MovPos == 0)
12629 CheckGravityMovement(&stored_player[i]);
12632 /* overwrite programmed action with tape action */
12633 if (stored_player[i].programmed_action)
12634 actual_player_action = stored_player[i].programmed_action;
12636 PlayerActions(&stored_player[i], actual_player_action);
12638 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12641 ScrollScreen(NULL, SCROLL_GO_ON);
12643 /* for backwards compatibility, the following code emulates a fixed bug that
12644 occured when pushing elements (causing elements that just made their last
12645 pushing step to already (if possible) make their first falling step in the
12646 same game frame, which is bad); this code is also needed to use the famous
12647 "spring push bug" which is used in older levels and might be wanted to be
12648 used also in newer levels, but in this case the buggy pushing code is only
12649 affecting the "spring" element and no other elements */
12651 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12653 for (i = 0; i < MAX_PLAYERS; i++)
12655 struct PlayerInfo *player = &stored_player[i];
12656 int x = player->jx;
12657 int y = player->jy;
12659 if (player->active && player->is_pushing && player->is_moving &&
12661 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12662 Feld[x][y] == EL_SPRING))
12664 ContinueMoving(x, y);
12666 /* continue moving after pushing (this is actually a bug) */
12667 if (!IS_MOVING(x, y))
12668 Stop[x][y] = FALSE;
12674 debug_print_timestamp(0, "start main loop profiling");
12677 SCAN_PLAYFIELD(x, y)
12679 ChangeCount[x][y] = 0;
12680 ChangeEvent[x][y] = -1;
12682 /* this must be handled before main playfield loop */
12683 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12686 if (MovDelay[x][y] <= 0)
12690 #if USE_NEW_SNAP_DELAY
12691 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12694 if (MovDelay[x][y] <= 0)
12697 TEST_DrawLevelField(x, y);
12699 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12705 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12707 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12708 printf("GameActions(): This should never happen!\n");
12710 ChangePage[x][y] = -1;
12714 Stop[x][y] = FALSE;
12715 if (WasJustMoving[x][y] > 0)
12716 WasJustMoving[x][y]--;
12717 if (WasJustFalling[x][y] > 0)
12718 WasJustFalling[x][y]--;
12719 if (CheckCollision[x][y] > 0)
12720 CheckCollision[x][y]--;
12721 if (CheckImpact[x][y] > 0)
12722 CheckImpact[x][y]--;
12726 /* reset finished pushing action (not done in ContinueMoving() to allow
12727 continuous pushing animation for elements with zero push delay) */
12728 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12730 ResetGfxAnimation(x, y);
12731 TEST_DrawLevelField(x, y);
12735 if (IS_BLOCKED(x, y))
12739 Blocked2Moving(x, y, &oldx, &oldy);
12740 if (!IS_MOVING(oldx, oldy))
12742 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12743 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12744 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12745 printf("GameActions(): This should never happen!\n");
12752 debug_print_timestamp(0, "- time for pre-main loop:");
12755 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12756 SCAN_PLAYFIELD(x, y)
12758 element = Feld[x][y];
12759 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12764 int element2 = element;
12765 int graphic2 = graphic;
12767 int element2 = Feld[x][y];
12768 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12770 int last_gfx_frame = GfxFrame[x][y];
12772 if (graphic_info[graphic2].anim_global_sync)
12773 GfxFrame[x][y] = FrameCounter;
12774 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12775 GfxFrame[x][y] = CustomValue[x][y];
12776 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12777 GfxFrame[x][y] = element_info[element2].collect_score;
12778 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12779 GfxFrame[x][y] = ChangeDelay[x][y];
12781 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12782 DrawLevelGraphicAnimation(x, y, graphic2);
12785 ResetGfxFrame(x, y, TRUE);
12789 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12790 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12791 ResetRandomAnimationValue(x, y);
12795 SetRandomAnimationValue(x, y);
12799 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12802 #endif // -------------------- !!! TEST ONLY !!! --------------------
12805 debug_print_timestamp(0, "- time for TEST loop: -->");
12808 SCAN_PLAYFIELD(x, y)
12810 element = Feld[x][y];
12811 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12813 ResetGfxFrame(x, y, TRUE);
12815 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12816 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12817 ResetRandomAnimationValue(x, y);
12819 SetRandomAnimationValue(x, y);
12821 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12823 if (IS_INACTIVE(element))
12825 if (IS_ANIMATED(graphic))
12826 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12831 /* this may take place after moving, so 'element' may have changed */
12832 if (IS_CHANGING(x, y) &&
12833 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12835 int page = element_info[element].event_page_nr[CE_DELAY];
12838 HandleElementChange(x, y, page);
12840 if (CAN_CHANGE(element))
12841 HandleElementChange(x, y, page);
12843 if (HAS_ACTION(element))
12844 ExecuteCustomElementAction(x, y, element, page);
12847 element = Feld[x][y];
12848 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12851 #if 0 // ---------------------------------------------------------------------
12853 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12857 element = Feld[x][y];
12858 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12860 if (IS_ANIMATED(graphic) &&
12861 !IS_MOVING(x, y) &&
12863 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12865 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12866 TEST_DrawTwinkleOnField(x, y);
12868 else if (IS_MOVING(x, y))
12869 ContinueMoving(x, y);
12876 case EL_EM_EXIT_OPEN:
12877 case EL_SP_EXIT_OPEN:
12878 case EL_STEEL_EXIT_OPEN:
12879 case EL_EM_STEEL_EXIT_OPEN:
12880 case EL_SP_TERMINAL:
12881 case EL_SP_TERMINAL_ACTIVE:
12882 case EL_EXTRA_TIME:
12883 case EL_SHIELD_NORMAL:
12884 case EL_SHIELD_DEADLY:
12885 if (IS_ANIMATED(graphic))
12886 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12889 case EL_DYNAMITE_ACTIVE:
12890 case EL_EM_DYNAMITE_ACTIVE:
12891 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12892 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12893 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12894 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12895 case EL_SP_DISK_RED_ACTIVE:
12896 CheckDynamite(x, y);
12899 case EL_AMOEBA_GROWING:
12900 AmoebeWaechst(x, y);
12903 case EL_AMOEBA_SHRINKING:
12904 AmoebaDisappearing(x, y);
12907 #if !USE_NEW_AMOEBA_CODE
12908 case EL_AMOEBA_WET:
12909 case EL_AMOEBA_DRY:
12910 case EL_AMOEBA_FULL:
12912 case EL_EMC_DRIPPER:
12913 AmoebeAbleger(x, y);
12917 case EL_GAME_OF_LIFE:
12922 case EL_EXIT_CLOSED:
12926 case EL_EM_EXIT_CLOSED:
12930 case EL_STEEL_EXIT_CLOSED:
12931 CheckExitSteel(x, y);
12934 case EL_EM_STEEL_EXIT_CLOSED:
12935 CheckExitSteelEM(x, y);
12938 case EL_SP_EXIT_CLOSED:
12942 case EL_EXPANDABLE_WALL_GROWING:
12943 case EL_EXPANDABLE_STEELWALL_GROWING:
12944 MauerWaechst(x, y);
12947 case EL_EXPANDABLE_WALL:
12948 case EL_EXPANDABLE_WALL_HORIZONTAL:
12949 case EL_EXPANDABLE_WALL_VERTICAL:
12950 case EL_EXPANDABLE_WALL_ANY:
12951 case EL_BD_EXPANDABLE_WALL:
12952 MauerAbleger(x, y);
12955 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12956 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12957 case EL_EXPANDABLE_STEELWALL_ANY:
12958 MauerAblegerStahl(x, y);
12962 CheckForDragon(x, y);
12968 case EL_ELEMENT_SNAPPING:
12969 case EL_DIAGONAL_SHRINKING:
12970 case EL_DIAGONAL_GROWING:
12973 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12975 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12980 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12981 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12986 #else // ---------------------------------------------------------------------
12988 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12992 element = Feld[x][y];
12993 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12995 if (IS_ANIMATED(graphic) &&
12996 !IS_MOVING(x, y) &&
12998 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13000 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13001 TEST_DrawTwinkleOnField(x, y);
13003 else if ((element == EL_ACID ||
13004 element == EL_EXIT_OPEN ||
13005 element == EL_EM_EXIT_OPEN ||
13006 element == EL_SP_EXIT_OPEN ||
13007 element == EL_STEEL_EXIT_OPEN ||
13008 element == EL_EM_STEEL_EXIT_OPEN ||
13009 element == EL_SP_TERMINAL ||
13010 element == EL_SP_TERMINAL_ACTIVE ||
13011 element == EL_EXTRA_TIME ||
13012 element == EL_SHIELD_NORMAL ||
13013 element == EL_SHIELD_DEADLY) &&
13014 IS_ANIMATED(graphic))
13015 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13016 else if (IS_MOVING(x, y))
13017 ContinueMoving(x, y);
13018 else if (IS_ACTIVE_BOMB(element))
13019 CheckDynamite(x, y);
13020 else if (element == EL_AMOEBA_GROWING)
13021 AmoebeWaechst(x, y);
13022 else if (element == EL_AMOEBA_SHRINKING)
13023 AmoebaDisappearing(x, y);
13025 #if !USE_NEW_AMOEBA_CODE
13026 else if (IS_AMOEBALIVE(element))
13027 AmoebeAbleger(x, y);
13030 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13032 else if (element == EL_EXIT_CLOSED)
13034 else if (element == EL_EM_EXIT_CLOSED)
13036 else if (element == EL_STEEL_EXIT_CLOSED)
13037 CheckExitSteel(x, y);
13038 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13039 CheckExitSteelEM(x, y);
13040 else if (element == EL_SP_EXIT_CLOSED)
13042 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13043 element == EL_EXPANDABLE_STEELWALL_GROWING)
13044 MauerWaechst(x, y);
13045 else if (element == EL_EXPANDABLE_WALL ||
13046 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13047 element == EL_EXPANDABLE_WALL_VERTICAL ||
13048 element == EL_EXPANDABLE_WALL_ANY ||
13049 element == EL_BD_EXPANDABLE_WALL)
13050 MauerAbleger(x, y);
13051 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13052 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13053 element == EL_EXPANDABLE_STEELWALL_ANY)
13054 MauerAblegerStahl(x, y);
13055 else if (element == EL_FLAMES)
13056 CheckForDragon(x, y);
13057 else if (element == EL_EXPLOSION)
13058 ; /* drawing of correct explosion animation is handled separately */
13059 else if (element == EL_ELEMENT_SNAPPING ||
13060 element == EL_DIAGONAL_SHRINKING ||
13061 element == EL_DIAGONAL_GROWING)
13063 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13065 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13067 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13068 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13070 #endif // ---------------------------------------------------------------------
13072 if (IS_BELT_ACTIVE(element))
13073 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13075 if (game.magic_wall_active)
13077 int jx = local_player->jx, jy = local_player->jy;
13079 /* play the element sound at the position nearest to the player */
13080 if ((element == EL_MAGIC_WALL_FULL ||
13081 element == EL_MAGIC_WALL_ACTIVE ||
13082 element == EL_MAGIC_WALL_EMPTYING ||
13083 element == EL_BD_MAGIC_WALL_FULL ||
13084 element == EL_BD_MAGIC_WALL_ACTIVE ||
13085 element == EL_BD_MAGIC_WALL_EMPTYING ||
13086 element == EL_DC_MAGIC_WALL_FULL ||
13087 element == EL_DC_MAGIC_WALL_ACTIVE ||
13088 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13089 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13098 debug_print_timestamp(0, "- time for MAIN loop: -->");
13101 #if USE_NEW_AMOEBA_CODE
13102 /* new experimental amoeba growth stuff */
13103 if (!(FrameCounter % 8))
13105 static unsigned int random = 1684108901;
13107 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13109 x = RND(lev_fieldx);
13110 y = RND(lev_fieldy);
13111 element = Feld[x][y];
13113 if (!IS_PLAYER(x,y) &&
13114 (element == EL_EMPTY ||
13115 CAN_GROW_INTO(element) ||
13116 element == EL_QUICKSAND_EMPTY ||
13117 element == EL_QUICKSAND_FAST_EMPTY ||
13118 element == EL_ACID_SPLASH_LEFT ||
13119 element == EL_ACID_SPLASH_RIGHT))
13121 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13122 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13123 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13124 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13125 Feld[x][y] = EL_AMOEBA_DROP;
13128 random = random * 129 + 1;
13134 if (game.explosions_delayed)
13137 game.explosions_delayed = FALSE;
13139 SCAN_PLAYFIELD(x, y)
13141 element = Feld[x][y];
13143 if (ExplodeField[x][y])
13144 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13145 else if (element == EL_EXPLOSION)
13146 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13148 ExplodeField[x][y] = EX_TYPE_NONE;
13151 game.explosions_delayed = TRUE;
13154 if (game.magic_wall_active)
13156 if (!(game.magic_wall_time_left % 4))
13158 int element = Feld[magic_wall_x][magic_wall_y];
13160 if (element == EL_BD_MAGIC_WALL_FULL ||
13161 element == EL_BD_MAGIC_WALL_ACTIVE ||
13162 element == EL_BD_MAGIC_WALL_EMPTYING)
13163 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13164 else if (element == EL_DC_MAGIC_WALL_FULL ||
13165 element == EL_DC_MAGIC_WALL_ACTIVE ||
13166 element == EL_DC_MAGIC_WALL_EMPTYING)
13167 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13169 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13172 if (game.magic_wall_time_left > 0)
13174 game.magic_wall_time_left--;
13176 if (!game.magic_wall_time_left)
13178 SCAN_PLAYFIELD(x, y)
13180 element = Feld[x][y];
13182 if (element == EL_MAGIC_WALL_ACTIVE ||
13183 element == EL_MAGIC_WALL_FULL)
13185 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13186 TEST_DrawLevelField(x, y);
13188 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13189 element == EL_BD_MAGIC_WALL_FULL)
13191 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13192 TEST_DrawLevelField(x, y);
13194 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13195 element == EL_DC_MAGIC_WALL_FULL)
13197 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13198 TEST_DrawLevelField(x, y);
13202 game.magic_wall_active = FALSE;
13207 if (game.light_time_left > 0)
13209 game.light_time_left--;
13211 if (game.light_time_left == 0)
13212 RedrawAllLightSwitchesAndInvisibleElements();
13215 if (game.timegate_time_left > 0)
13217 game.timegate_time_left--;
13219 if (game.timegate_time_left == 0)
13220 CloseAllOpenTimegates();
13223 if (game.lenses_time_left > 0)
13225 game.lenses_time_left--;
13227 if (game.lenses_time_left == 0)
13228 RedrawAllInvisibleElementsForLenses();
13231 if (game.magnify_time_left > 0)
13233 game.magnify_time_left--;
13235 if (game.magnify_time_left == 0)
13236 RedrawAllInvisibleElementsForMagnifier();
13239 for (i = 0; i < MAX_PLAYERS; i++)
13241 struct PlayerInfo *player = &stored_player[i];
13243 if (SHIELD_ON(player))
13245 if (player->shield_deadly_time_left)
13246 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13247 else if (player->shield_normal_time_left)
13248 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13252 #if USE_DELAYED_GFX_REDRAW
13253 SCAN_PLAYFIELD(x, y)
13256 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13258 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13259 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13262 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13263 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13265 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13266 DrawLevelField(x, y);
13268 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13269 DrawLevelFieldCrumbled(x, y);
13271 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13272 DrawLevelFieldCrumbledNeighbours(x, y);
13274 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13275 DrawTwinkleOnField(x, y);
13278 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13285 PlayAllPlayersSound();
13287 if (options.debug) /* calculate frames per second */
13289 static unsigned int fps_counter = 0;
13290 static int fps_frames = 0;
13291 unsigned int fps_delay_ms = Counter() - fps_counter;
13295 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13297 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13300 fps_counter = Counter();
13303 redraw_mask |= REDRAW_FPS;
13306 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13308 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13310 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13312 local_player->show_envelope = 0;
13316 debug_print_timestamp(0, "stop main loop profiling ");
13317 printf("----------------------------------------------------------\n");
13320 /* use random number generator in every frame to make it less predictable */
13321 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13325 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13327 int min_x = x, min_y = y, max_x = x, max_y = y;
13330 for (i = 0; i < MAX_PLAYERS; i++)
13332 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13334 if (!stored_player[i].active || &stored_player[i] == player)
13337 min_x = MIN(min_x, jx);
13338 min_y = MIN(min_y, jy);
13339 max_x = MAX(max_x, jx);
13340 max_y = MAX(max_y, jy);
13343 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13346 static boolean AllPlayersInVisibleScreen()
13350 for (i = 0; i < MAX_PLAYERS; i++)
13352 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13354 if (!stored_player[i].active)
13357 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13364 void ScrollLevel(int dx, int dy)
13367 /* (directly solved in BlitBitmap() now) */
13368 static Bitmap *bitmap_db_field2 = NULL;
13369 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13376 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13377 /* only horizontal XOR vertical scroll direction allowed */
13378 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13383 /* (directly solved in BlitBitmap() now) */
13384 if (bitmap_db_field2 == NULL)
13385 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13387 /* needed when blitting directly to same bitmap -- should not be needed with
13388 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13389 BlitBitmap(drawto_field, bitmap_db_field2,
13390 FX + TILEX * (dx == -1) - softscroll_offset,
13391 FY + TILEY * (dy == -1) - softscroll_offset,
13392 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13393 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13394 FX + TILEX * (dx == 1) - softscroll_offset,
13395 FY + TILEY * (dy == 1) - softscroll_offset);
13396 BlitBitmap(bitmap_db_field2, drawto_field,
13397 FX + TILEX * (dx == 1) - softscroll_offset,
13398 FY + TILEY * (dy == 1) - softscroll_offset,
13399 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13400 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13401 FX + TILEX * (dx == 1) - softscroll_offset,
13402 FY + TILEY * (dy == 1) - softscroll_offset);
13407 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13408 int xsize = (BX2 - BX1 + 1);
13409 int ysize = (BY2 - BY1 + 1);
13410 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13411 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13412 int step = (start < end ? +1 : -1);
13414 for (i = start; i != end; i += step)
13416 BlitBitmap(drawto_field, drawto_field,
13417 FX + TILEX * (dx != 0 ? i + step : 0),
13418 FY + TILEY * (dy != 0 ? i + step : 0),
13419 TILEX * (dx != 0 ? 1 : xsize),
13420 TILEY * (dy != 0 ? 1 : ysize),
13421 FX + TILEX * (dx != 0 ? i : 0),
13422 FY + TILEY * (dy != 0 ? i : 0));
13429 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13431 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13435 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13437 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13442 BlitBitmap(drawto_field, drawto_field,
13443 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13444 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13445 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13446 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13447 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13448 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13450 BlitBitmap(drawto_field, drawto_field,
13451 FX + TILEX * (dx == -1) - softscroll_offset,
13452 FY + TILEY * (dy == -1) - softscroll_offset,
13453 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13454 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13455 FX + TILEX * (dx == 1) - softscroll_offset,
13456 FY + TILEY * (dy == 1) - softscroll_offset);
13464 x = (dx == 1 ? BX1 : BX2);
13465 for (y = BY1; y <= BY2; y++)
13466 DrawScreenField(x, y);
13471 y = (dy == 1 ? BY1 : BY2);
13472 for (x = BX1; x <= BX2; x++)
13473 DrawScreenField(x, y);
13476 redraw_mask |= REDRAW_FIELD;
13479 static boolean canFallDown(struct PlayerInfo *player)
13481 int jx = player->jx, jy = player->jy;
13483 return (IN_LEV_FIELD(jx, jy + 1) &&
13484 (IS_FREE(jx, jy + 1) ||
13485 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13486 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13487 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13490 static boolean canPassField(int x, int y, int move_dir)
13492 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13493 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13494 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13495 int nextx = x + dx;
13496 int nexty = y + dy;
13497 int element = Feld[x][y];
13499 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13500 !CAN_MOVE(element) &&
13501 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13502 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13503 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13506 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13508 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13509 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13510 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13514 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13515 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13516 (IS_DIGGABLE(Feld[newx][newy]) ||
13517 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13518 canPassField(newx, newy, move_dir)));
13521 static void CheckGravityMovement(struct PlayerInfo *player)
13523 #if USE_PLAYER_GRAVITY
13524 if (player->gravity && !player->programmed_action)
13526 if (game.gravity && !player->programmed_action)
13529 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13530 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13531 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13532 int jx = player->jx, jy = player->jy;
13533 boolean player_is_moving_to_valid_field =
13534 (!player_is_snapping &&
13535 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13536 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13537 boolean player_can_fall_down = canFallDown(player);
13539 if (player_can_fall_down &&
13540 !player_is_moving_to_valid_field)
13541 player->programmed_action = MV_DOWN;
13545 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13547 return CheckGravityMovement(player);
13549 #if USE_PLAYER_GRAVITY
13550 if (player->gravity && !player->programmed_action)
13552 if (game.gravity && !player->programmed_action)
13555 int jx = player->jx, jy = player->jy;
13556 boolean field_under_player_is_free =
13557 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13558 boolean player_is_standing_on_valid_field =
13559 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13560 (IS_WALKABLE(Feld[jx][jy]) &&
13561 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13563 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13564 player->programmed_action = MV_DOWN;
13569 MovePlayerOneStep()
13570 -----------------------------------------------------------------------------
13571 dx, dy: direction (non-diagonal) to try to move the player to
13572 real_dx, real_dy: direction as read from input device (can be diagonal)
13575 boolean MovePlayerOneStep(struct PlayerInfo *player,
13576 int dx, int dy, int real_dx, int real_dy)
13578 int jx = player->jx, jy = player->jy;
13579 int new_jx = jx + dx, new_jy = jy + dy;
13580 #if !USE_FIXED_DONT_RUN_INTO
13584 boolean player_can_move = !player->cannot_move;
13586 if (!player->active || (!dx && !dy))
13587 return MP_NO_ACTION;
13589 player->MovDir = (dx < 0 ? MV_LEFT :
13590 dx > 0 ? MV_RIGHT :
13592 dy > 0 ? MV_DOWN : MV_NONE);
13594 if (!IN_LEV_FIELD(new_jx, new_jy))
13595 return MP_NO_ACTION;
13597 if (!player_can_move)
13599 if (player->MovPos == 0)
13601 player->is_moving = FALSE;
13602 player->is_digging = FALSE;
13603 player->is_collecting = FALSE;
13604 player->is_snapping = FALSE;
13605 player->is_pushing = FALSE;
13610 if (!options.network && game.centered_player_nr == -1 &&
13611 !AllPlayersInSight(player, new_jx, new_jy))
13612 return MP_NO_ACTION;
13614 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13615 return MP_NO_ACTION;
13618 #if !USE_FIXED_DONT_RUN_INTO
13619 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13621 /* (moved to DigField()) */
13622 if (player_can_move && DONT_RUN_INTO(element))
13624 if (element == EL_ACID && dx == 0 && dy == 1)
13626 SplashAcid(new_jx, new_jy);
13627 Feld[jx][jy] = EL_PLAYER_1;
13628 InitMovingField(jx, jy, MV_DOWN);
13629 Store[jx][jy] = EL_ACID;
13630 ContinueMoving(jx, jy);
13631 BuryPlayer(player);
13634 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13640 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13641 if (can_move != MP_MOVING)
13644 /* check if DigField() has caused relocation of the player */
13645 if (player->jx != jx || player->jy != jy)
13646 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13648 StorePlayer[jx][jy] = 0;
13649 player->last_jx = jx;
13650 player->last_jy = jy;
13651 player->jx = new_jx;
13652 player->jy = new_jy;
13653 StorePlayer[new_jx][new_jy] = player->element_nr;
13655 if (player->move_delay_value_next != -1)
13657 player->move_delay_value = player->move_delay_value_next;
13658 player->move_delay_value_next = -1;
13662 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13664 player->step_counter++;
13666 PlayerVisit[jx][jy] = FrameCounter;
13668 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13669 player->is_moving = TRUE;
13673 /* should better be called in MovePlayer(), but this breaks some tapes */
13674 ScrollPlayer(player, SCROLL_INIT);
13680 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13682 int jx = player->jx, jy = player->jy;
13683 int old_jx = jx, old_jy = jy;
13684 int moved = MP_NO_ACTION;
13686 if (!player->active)
13691 if (player->MovPos == 0)
13693 player->is_moving = FALSE;
13694 player->is_digging = FALSE;
13695 player->is_collecting = FALSE;
13696 player->is_snapping = FALSE;
13697 player->is_pushing = FALSE;
13703 if (player->move_delay > 0)
13706 player->move_delay = -1; /* set to "uninitialized" value */
13708 /* store if player is automatically moved to next field */
13709 player->is_auto_moving = (player->programmed_action != MV_NONE);
13711 /* remove the last programmed player action */
13712 player->programmed_action = 0;
13714 if (player->MovPos)
13716 /* should only happen if pre-1.2 tape recordings are played */
13717 /* this is only for backward compatibility */
13719 int original_move_delay_value = player->move_delay_value;
13722 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13726 /* scroll remaining steps with finest movement resolution */
13727 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13729 while (player->MovPos)
13731 ScrollPlayer(player, SCROLL_GO_ON);
13732 ScrollScreen(NULL, SCROLL_GO_ON);
13734 AdvanceFrameAndPlayerCounters(player->index_nr);
13740 player->move_delay_value = original_move_delay_value;
13743 player->is_active = FALSE;
13745 if (player->last_move_dir & MV_HORIZONTAL)
13747 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13748 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13752 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13753 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13756 #if USE_FIXED_BORDER_RUNNING_GFX
13757 if (!moved && !player->is_active)
13759 player->is_moving = FALSE;
13760 player->is_digging = FALSE;
13761 player->is_collecting = FALSE;
13762 player->is_snapping = FALSE;
13763 player->is_pushing = FALSE;
13771 if (moved & MP_MOVING && !ScreenMovPos &&
13772 (player->index_nr == game.centered_player_nr ||
13773 game.centered_player_nr == -1))
13775 if (moved & MP_MOVING && !ScreenMovPos &&
13776 (player == local_player || !options.network))
13779 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13780 int offset = game.scroll_delay_value;
13782 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13784 /* actual player has left the screen -- scroll in that direction */
13785 if (jx != old_jx) /* player has moved horizontally */
13786 scroll_x += (jx - old_jx);
13787 else /* player has moved vertically */
13788 scroll_y += (jy - old_jy);
13792 if (jx != old_jx) /* player has moved horizontally */
13794 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13795 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13796 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13798 /* don't scroll over playfield boundaries */
13799 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13800 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13802 /* don't scroll more than one field at a time */
13803 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13805 /* don't scroll against the player's moving direction */
13806 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13807 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13808 scroll_x = old_scroll_x;
13810 else /* player has moved vertically */
13812 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13813 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13814 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13816 /* don't scroll over playfield boundaries */
13817 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13818 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13820 /* don't scroll more than one field at a time */
13821 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13823 /* don't scroll against the player's moving direction */
13824 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13825 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13826 scroll_y = old_scroll_y;
13830 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13833 if (!options.network && game.centered_player_nr == -1 &&
13834 !AllPlayersInVisibleScreen())
13836 scroll_x = old_scroll_x;
13837 scroll_y = old_scroll_y;
13841 if (!options.network && !AllPlayersInVisibleScreen())
13843 scroll_x = old_scroll_x;
13844 scroll_y = old_scroll_y;
13849 ScrollScreen(player, SCROLL_INIT);
13850 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13855 player->StepFrame = 0;
13857 if (moved & MP_MOVING)
13859 if (old_jx != jx && old_jy == jy)
13860 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13861 else if (old_jx == jx && old_jy != jy)
13862 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13864 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13866 player->last_move_dir = player->MovDir;
13867 player->is_moving = TRUE;
13868 player->is_snapping = FALSE;
13869 player->is_switching = FALSE;
13870 player->is_dropping = FALSE;
13871 player->is_dropping_pressed = FALSE;
13872 player->drop_pressed_delay = 0;
13875 /* should better be called here than above, but this breaks some tapes */
13876 ScrollPlayer(player, SCROLL_INIT);
13881 CheckGravityMovementWhenNotMoving(player);
13883 player->is_moving = FALSE;
13885 /* at this point, the player is allowed to move, but cannot move right now
13886 (e.g. because of something blocking the way) -- ensure that the player
13887 is also allowed to move in the next frame (in old versions before 3.1.1,
13888 the player was forced to wait again for eight frames before next try) */
13890 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13891 player->move_delay = 0; /* allow direct movement in the next frame */
13894 if (player->move_delay == -1) /* not yet initialized by DigField() */
13895 player->move_delay = player->move_delay_value;
13897 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13899 TestIfPlayerTouchesBadThing(jx, jy);
13900 TestIfPlayerTouchesCustomElement(jx, jy);
13903 if (!player->active)
13904 RemovePlayer(player);
13909 void ScrollPlayer(struct PlayerInfo *player, int mode)
13911 int jx = player->jx, jy = player->jy;
13912 int last_jx = player->last_jx, last_jy = player->last_jy;
13913 int move_stepsize = TILEX / player->move_delay_value;
13915 #if USE_NEW_PLAYER_SPEED
13916 if (!player->active)
13919 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13922 if (!player->active || player->MovPos == 0)
13926 if (mode == SCROLL_INIT)
13928 player->actual_frame_counter = FrameCounter;
13929 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13931 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13932 Feld[last_jx][last_jy] == EL_EMPTY)
13934 int last_field_block_delay = 0; /* start with no blocking at all */
13935 int block_delay_adjustment = player->block_delay_adjustment;
13937 /* if player blocks last field, add delay for exactly one move */
13938 if (player->block_last_field)
13940 last_field_block_delay += player->move_delay_value;
13942 /* when blocking enabled, prevent moving up despite gravity */
13943 #if USE_PLAYER_GRAVITY
13944 if (player->gravity && player->MovDir == MV_UP)
13945 block_delay_adjustment = -1;
13947 if (game.gravity && player->MovDir == MV_UP)
13948 block_delay_adjustment = -1;
13952 /* add block delay adjustment (also possible when not blocking) */
13953 last_field_block_delay += block_delay_adjustment;
13955 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13956 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13959 #if USE_NEW_PLAYER_SPEED
13960 if (player->MovPos != 0) /* player has not yet reached destination */
13966 else if (!FrameReached(&player->actual_frame_counter, 1))
13969 #if USE_NEW_PLAYER_SPEED
13970 if (player->MovPos != 0)
13972 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13973 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13975 /* before DrawPlayer() to draw correct player graphic for this case */
13976 if (player->MovPos == 0)
13977 CheckGravityMovement(player);
13980 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13981 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13983 /* before DrawPlayer() to draw correct player graphic for this case */
13984 if (player->MovPos == 0)
13985 CheckGravityMovement(player);
13988 if (player->MovPos == 0) /* player reached destination field */
13990 if (player->move_delay_reset_counter > 0)
13992 player->move_delay_reset_counter--;
13994 if (player->move_delay_reset_counter == 0)
13996 /* continue with normal speed after quickly moving through gate */
13997 HALVE_PLAYER_SPEED(player);
13999 /* be able to make the next move without delay */
14000 player->move_delay = 0;
14004 player->last_jx = jx;
14005 player->last_jy = jy;
14007 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14008 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14010 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14012 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14013 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14015 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14017 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14018 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14020 DrawPlayer(player); /* needed here only to cleanup last field */
14021 RemovePlayer(player);
14023 if (local_player->friends_still_needed == 0 ||
14024 IS_SP_ELEMENT(Feld[jx][jy]))
14025 PlayerWins(player);
14028 /* this breaks one level: "machine", level 000 */
14030 int move_direction = player->MovDir;
14031 int enter_side = MV_DIR_OPPOSITE(move_direction);
14032 int leave_side = move_direction;
14033 int old_jx = last_jx;
14034 int old_jy = last_jy;
14035 int old_element = Feld[old_jx][old_jy];
14036 int new_element = Feld[jx][jy];
14038 if (IS_CUSTOM_ELEMENT(old_element))
14039 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14041 player->index_bit, leave_side);
14043 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14044 CE_PLAYER_LEAVES_X,
14045 player->index_bit, leave_side);
14047 if (IS_CUSTOM_ELEMENT(new_element))
14048 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14049 player->index_bit, enter_side);
14051 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14052 CE_PLAYER_ENTERS_X,
14053 player->index_bit, enter_side);
14055 #if USE_FIX_CE_ACTION_WITH_PLAYER
14056 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14057 CE_MOVE_OF_X, move_direction);
14059 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14060 CE_MOVE_OF_X, move_direction);
14064 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14066 TestIfPlayerTouchesBadThing(jx, jy);
14067 TestIfPlayerTouchesCustomElement(jx, jy);
14069 /* needed because pushed element has not yet reached its destination,
14070 so it would trigger a change event at its previous field location */
14071 if (!player->is_pushing)
14072 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14074 if (!player->active)
14075 RemovePlayer(player);
14078 if (!local_player->LevelSolved && level.use_step_counter)
14088 if (TimeLeft <= 10 && setup.time_limit)
14089 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14092 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14094 DisplayGameControlValues();
14096 DrawGameValue_Time(TimeLeft);
14099 if (!TimeLeft && setup.time_limit)
14100 for (i = 0; i < MAX_PLAYERS; i++)
14101 KillPlayer(&stored_player[i]);
14104 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14106 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14108 DisplayGameControlValues();
14111 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14112 DrawGameValue_Time(TimePlayed);
14116 if (tape.single_step && tape.recording && !tape.pausing &&
14117 !player->programmed_action)
14118 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14122 void ScrollScreen(struct PlayerInfo *player, int mode)
14124 static unsigned int screen_frame_counter = 0;
14126 if (mode == SCROLL_INIT)
14128 /* set scrolling step size according to actual player's moving speed */
14129 ScrollStepSize = TILEX / player->move_delay_value;
14131 screen_frame_counter = FrameCounter;
14132 ScreenMovDir = player->MovDir;
14133 ScreenMovPos = player->MovPos;
14134 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14137 else if (!FrameReached(&screen_frame_counter, 1))
14142 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14143 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14144 redraw_mask |= REDRAW_FIELD;
14147 ScreenMovDir = MV_NONE;
14150 void TestIfPlayerTouchesCustomElement(int x, int y)
14152 static int xy[4][2] =
14159 static int trigger_sides[4][2] =
14161 /* center side border side */
14162 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14163 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14164 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14165 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14167 static int touch_dir[4] =
14169 MV_LEFT | MV_RIGHT,
14174 int center_element = Feld[x][y]; /* should always be non-moving! */
14177 for (i = 0; i < NUM_DIRECTIONS; i++)
14179 int xx = x + xy[i][0];
14180 int yy = y + xy[i][1];
14181 int center_side = trigger_sides[i][0];
14182 int border_side = trigger_sides[i][1];
14183 int border_element;
14185 if (!IN_LEV_FIELD(xx, yy))
14188 if (IS_PLAYER(x, y)) /* player found at center element */
14190 struct PlayerInfo *player = PLAYERINFO(x, y);
14192 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14193 border_element = Feld[xx][yy]; /* may be moving! */
14194 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14195 border_element = Feld[xx][yy];
14196 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14197 border_element = MovingOrBlocked2Element(xx, yy);
14199 continue; /* center and border element do not touch */
14201 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14202 player->index_bit, border_side);
14203 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14204 CE_PLAYER_TOUCHES_X,
14205 player->index_bit, border_side);
14207 #if USE_FIX_CE_ACTION_WITH_PLAYER
14209 /* use player element that is initially defined in the level playfield,
14210 not the player element that corresponds to the runtime player number
14211 (example: a level that contains EL_PLAYER_3 as the only player would
14212 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14213 int player_element = PLAYERINFO(x, y)->initial_element;
14215 CheckElementChangeBySide(xx, yy, border_element, player_element,
14216 CE_TOUCHING_X, border_side);
14220 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14222 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14224 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14226 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14227 continue; /* center and border element do not touch */
14230 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14231 player->index_bit, center_side);
14232 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14233 CE_PLAYER_TOUCHES_X,
14234 player->index_bit, center_side);
14236 #if USE_FIX_CE_ACTION_WITH_PLAYER
14238 /* use player element that is initially defined in the level playfield,
14239 not the player element that corresponds to the runtime player number
14240 (example: a level that contains EL_PLAYER_3 as the only player would
14241 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14242 int player_element = PLAYERINFO(xx, yy)->initial_element;
14244 CheckElementChangeBySide(x, y, center_element, player_element,
14245 CE_TOUCHING_X, center_side);
14254 #if USE_ELEMENT_TOUCHING_BUGFIX
14256 void TestIfElementTouchesCustomElement(int x, int y)
14258 static int xy[4][2] =
14265 static int trigger_sides[4][2] =
14267 /* center side border side */
14268 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14269 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14270 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14271 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14273 static int touch_dir[4] =
14275 MV_LEFT | MV_RIGHT,
14280 boolean change_center_element = FALSE;
14281 int center_element = Feld[x][y]; /* should always be non-moving! */
14282 int border_element_old[NUM_DIRECTIONS];
14285 for (i = 0; i < NUM_DIRECTIONS; i++)
14287 int xx = x + xy[i][0];
14288 int yy = y + xy[i][1];
14289 int border_element;
14291 border_element_old[i] = -1;
14293 if (!IN_LEV_FIELD(xx, yy))
14296 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14297 border_element = Feld[xx][yy]; /* may be moving! */
14298 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14299 border_element = Feld[xx][yy];
14300 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14301 border_element = MovingOrBlocked2Element(xx, yy);
14303 continue; /* center and border element do not touch */
14305 border_element_old[i] = border_element;
14308 for (i = 0; i < NUM_DIRECTIONS; i++)
14310 int xx = x + xy[i][0];
14311 int yy = y + xy[i][1];
14312 int center_side = trigger_sides[i][0];
14313 int border_element = border_element_old[i];
14315 if (border_element == -1)
14318 /* check for change of border element */
14319 CheckElementChangeBySide(xx, yy, border_element, center_element,
14320 CE_TOUCHING_X, center_side);
14322 /* (center element cannot be player, so we dont have to check this here) */
14325 for (i = 0; i < NUM_DIRECTIONS; i++)
14327 int xx = x + xy[i][0];
14328 int yy = y + xy[i][1];
14329 int border_side = trigger_sides[i][1];
14330 int border_element = border_element_old[i];
14332 if (border_element == -1)
14335 /* check for change of center element (but change it only once) */
14336 if (!change_center_element)
14337 change_center_element =
14338 CheckElementChangeBySide(x, y, center_element, border_element,
14339 CE_TOUCHING_X, border_side);
14341 #if USE_FIX_CE_ACTION_WITH_PLAYER
14342 if (IS_PLAYER(xx, yy))
14344 /* use player element that is initially defined in the level playfield,
14345 not the player element that corresponds to the runtime player number
14346 (example: a level that contains EL_PLAYER_3 as the only player would
14347 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14348 int player_element = PLAYERINFO(xx, yy)->initial_element;
14350 CheckElementChangeBySide(x, y, center_element, player_element,
14351 CE_TOUCHING_X, border_side);
14359 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14361 static int xy[4][2] =
14368 static int trigger_sides[4][2] =
14370 /* center side border side */
14371 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14372 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14373 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14374 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14376 static int touch_dir[4] =
14378 MV_LEFT | MV_RIGHT,
14383 boolean change_center_element = FALSE;
14384 int center_element = Feld[x][y]; /* should always be non-moving! */
14387 for (i = 0; i < NUM_DIRECTIONS; i++)
14389 int xx = x + xy[i][0];
14390 int yy = y + xy[i][1];
14391 int center_side = trigger_sides[i][0];
14392 int border_side = trigger_sides[i][1];
14393 int border_element;
14395 if (!IN_LEV_FIELD(xx, yy))
14398 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14399 border_element = Feld[xx][yy]; /* may be moving! */
14400 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14401 border_element = Feld[xx][yy];
14402 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14403 border_element = MovingOrBlocked2Element(xx, yy);
14405 continue; /* center and border element do not touch */
14407 /* check for change of center element (but change it only once) */
14408 if (!change_center_element)
14409 change_center_element =
14410 CheckElementChangeBySide(x, y, center_element, border_element,
14411 CE_TOUCHING_X, border_side);
14413 /* check for change of border element */
14414 CheckElementChangeBySide(xx, yy, border_element, center_element,
14415 CE_TOUCHING_X, center_side);
14421 void TestIfElementHitsCustomElement(int x, int y, int direction)
14423 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14424 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14425 int hitx = x + dx, hity = y + dy;
14426 int hitting_element = Feld[x][y];
14427 int touched_element;
14429 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14432 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14433 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14435 if (IN_LEV_FIELD(hitx, hity))
14437 int opposite_direction = MV_DIR_OPPOSITE(direction);
14438 int hitting_side = direction;
14439 int touched_side = opposite_direction;
14440 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14441 MovDir[hitx][hity] != direction ||
14442 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14448 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14449 CE_HITTING_X, touched_side);
14451 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14452 CE_HIT_BY_X, hitting_side);
14454 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14455 CE_HIT_BY_SOMETHING, opposite_direction);
14457 #if USE_FIX_CE_ACTION_WITH_PLAYER
14458 if (IS_PLAYER(hitx, hity))
14460 /* use player element that is initially defined in the level playfield,
14461 not the player element that corresponds to the runtime player number
14462 (example: a level that contains EL_PLAYER_3 as the only player would
14463 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14464 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14466 CheckElementChangeBySide(x, y, hitting_element, player_element,
14467 CE_HITTING_X, touched_side);
14473 /* "hitting something" is also true when hitting the playfield border */
14474 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14475 CE_HITTING_SOMETHING, direction);
14479 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14481 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14482 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14483 int hitx = x + dx, hity = y + dy;
14484 int hitting_element = Feld[x][y];
14485 int touched_element;
14487 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14488 !IS_FREE(hitx, hity) &&
14489 (!IS_MOVING(hitx, hity) ||
14490 MovDir[hitx][hity] != direction ||
14491 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14494 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14498 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14502 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14503 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14505 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14506 EP_CAN_SMASH_EVERYTHING, direction);
14508 if (IN_LEV_FIELD(hitx, hity))
14510 int opposite_direction = MV_DIR_OPPOSITE(direction);
14511 int hitting_side = direction;
14512 int touched_side = opposite_direction;
14514 int touched_element = MovingOrBlocked2Element(hitx, hity);
14517 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14518 MovDir[hitx][hity] != direction ||
14519 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14528 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14529 CE_SMASHED_BY_SOMETHING, opposite_direction);
14531 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14532 CE_OTHER_IS_SMASHING, touched_side);
14534 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14535 CE_OTHER_GETS_SMASHED, hitting_side);
14541 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14543 int i, kill_x = -1, kill_y = -1;
14545 int bad_element = -1;
14546 static int test_xy[4][2] =
14553 static int test_dir[4] =
14561 for (i = 0; i < NUM_DIRECTIONS; i++)
14563 int test_x, test_y, test_move_dir, test_element;
14565 test_x = good_x + test_xy[i][0];
14566 test_y = good_y + test_xy[i][1];
14568 if (!IN_LEV_FIELD(test_x, test_y))
14572 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14574 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14576 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14577 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14579 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14580 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14584 bad_element = test_element;
14590 if (kill_x != -1 || kill_y != -1)
14592 if (IS_PLAYER(good_x, good_y))
14594 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14596 if (player->shield_deadly_time_left > 0 &&
14597 !IS_INDESTRUCTIBLE(bad_element))
14598 Bang(kill_x, kill_y);
14599 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14600 KillPlayer(player);
14603 Bang(good_x, good_y);
14607 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14609 int i, kill_x = -1, kill_y = -1;
14610 int bad_element = Feld[bad_x][bad_y];
14611 static int test_xy[4][2] =
14618 static int touch_dir[4] =
14620 MV_LEFT | MV_RIGHT,
14625 static int test_dir[4] =
14633 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14636 for (i = 0; i < NUM_DIRECTIONS; i++)
14638 int test_x, test_y, test_move_dir, test_element;
14640 test_x = bad_x + test_xy[i][0];
14641 test_y = bad_y + test_xy[i][1];
14643 if (!IN_LEV_FIELD(test_x, test_y))
14647 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14649 test_element = Feld[test_x][test_y];
14651 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14652 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14654 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14655 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14657 /* good thing is player or penguin that does not move away */
14658 if (IS_PLAYER(test_x, test_y))
14660 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14662 if (bad_element == EL_ROBOT && player->is_moving)
14663 continue; /* robot does not kill player if he is moving */
14665 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14667 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14668 continue; /* center and border element do not touch */
14676 else if (test_element == EL_PENGUIN)
14686 if (kill_x != -1 || kill_y != -1)
14688 if (IS_PLAYER(kill_x, kill_y))
14690 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14692 if (player->shield_deadly_time_left > 0 &&
14693 !IS_INDESTRUCTIBLE(bad_element))
14694 Bang(bad_x, bad_y);
14695 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14696 KillPlayer(player);
14699 Bang(kill_x, kill_y);
14703 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14705 int bad_element = Feld[bad_x][bad_y];
14706 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14707 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14708 int test_x = bad_x + dx, test_y = bad_y + dy;
14709 int test_move_dir, test_element;
14710 int kill_x = -1, kill_y = -1;
14712 if (!IN_LEV_FIELD(test_x, test_y))
14716 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14718 test_element = Feld[test_x][test_y];
14720 if (test_move_dir != bad_move_dir)
14722 /* good thing can be player or penguin that does not move away */
14723 if (IS_PLAYER(test_x, test_y))
14725 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14727 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14728 player as being hit when he is moving towards the bad thing, because
14729 the "get hit by" condition would be lost after the player stops) */
14730 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14731 return; /* player moves away from bad thing */
14736 else if (test_element == EL_PENGUIN)
14743 if (kill_x != -1 || kill_y != -1)
14745 if (IS_PLAYER(kill_x, kill_y))
14747 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14749 if (player->shield_deadly_time_left > 0 &&
14750 !IS_INDESTRUCTIBLE(bad_element))
14751 Bang(bad_x, bad_y);
14752 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14753 KillPlayer(player);
14756 Bang(kill_x, kill_y);
14760 void TestIfPlayerTouchesBadThing(int x, int y)
14762 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14765 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14767 TestIfGoodThingHitsBadThing(x, y, move_dir);
14770 void TestIfBadThingTouchesPlayer(int x, int y)
14772 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14775 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14777 TestIfBadThingHitsGoodThing(x, y, move_dir);
14780 void TestIfFriendTouchesBadThing(int x, int y)
14782 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14785 void TestIfBadThingTouchesFriend(int x, int y)
14787 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14790 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14792 int i, kill_x = bad_x, kill_y = bad_y;
14793 static int xy[4][2] =
14801 for (i = 0; i < NUM_DIRECTIONS; i++)
14805 x = bad_x + xy[i][0];
14806 y = bad_y + xy[i][1];
14807 if (!IN_LEV_FIELD(x, y))
14810 element = Feld[x][y];
14811 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14812 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14820 if (kill_x != bad_x || kill_y != bad_y)
14821 Bang(bad_x, bad_y);
14824 void KillPlayer(struct PlayerInfo *player)
14826 int jx = player->jx, jy = player->jy;
14828 if (!player->active)
14832 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14833 player->killed, player->active, player->reanimated);
14836 /* the following code was introduced to prevent an infinite loop when calling
14838 -> CheckTriggeredElementChangeExt()
14839 -> ExecuteCustomElementAction()
14841 -> (infinitely repeating the above sequence of function calls)
14842 which occurs when killing the player while having a CE with the setting
14843 "kill player X when explosion of <player X>"; the solution using a new
14844 field "player->killed" was chosen for backwards compatibility, although
14845 clever use of the fields "player->active" etc. would probably also work */
14847 if (player->killed)
14851 player->killed = TRUE;
14853 /* remove accessible field at the player's position */
14854 Feld[jx][jy] = EL_EMPTY;
14856 /* deactivate shield (else Bang()/Explode() would not work right) */
14857 player->shield_normal_time_left = 0;
14858 player->shield_deadly_time_left = 0;
14861 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14862 player->killed, player->active, player->reanimated);
14868 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14869 player->killed, player->active, player->reanimated);
14872 #if USE_PLAYER_REANIMATION
14874 if (player->reanimated) /* killed player may have been reanimated */
14875 player->killed = player->reanimated = FALSE;
14877 BuryPlayer(player);
14879 if (player->killed) /* player may have been reanimated */
14880 BuryPlayer(player);
14883 BuryPlayer(player);
14887 static void KillPlayerUnlessEnemyProtected(int x, int y)
14889 if (!PLAYER_ENEMY_PROTECTED(x, y))
14890 KillPlayer(PLAYERINFO(x, y));
14893 static void KillPlayerUnlessExplosionProtected(int x, int y)
14895 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14896 KillPlayer(PLAYERINFO(x, y));
14899 void BuryPlayer(struct PlayerInfo *player)
14901 int jx = player->jx, jy = player->jy;
14903 if (!player->active)
14906 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14907 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14909 player->GameOver = TRUE;
14910 RemovePlayer(player);
14913 void RemovePlayer(struct PlayerInfo *player)
14915 int jx = player->jx, jy = player->jy;
14916 int i, found = FALSE;
14918 player->present = FALSE;
14919 player->active = FALSE;
14921 if (!ExplodeField[jx][jy])
14922 StorePlayer[jx][jy] = 0;
14924 if (player->is_moving)
14925 TEST_DrawLevelField(player->last_jx, player->last_jy);
14927 for (i = 0; i < MAX_PLAYERS; i++)
14928 if (stored_player[i].active)
14932 AllPlayersGone = TRUE;
14938 #if USE_NEW_SNAP_DELAY
14939 static void setFieldForSnapping(int x, int y, int element, int direction)
14941 struct ElementInfo *ei = &element_info[element];
14942 int direction_bit = MV_DIR_TO_BIT(direction);
14943 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14944 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14945 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14947 Feld[x][y] = EL_ELEMENT_SNAPPING;
14948 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14950 ResetGfxAnimation(x, y);
14952 GfxElement[x][y] = element;
14953 GfxAction[x][y] = action;
14954 GfxDir[x][y] = direction;
14955 GfxFrame[x][y] = -1;
14960 =============================================================================
14961 checkDiagonalPushing()
14962 -----------------------------------------------------------------------------
14963 check if diagonal input device direction results in pushing of object
14964 (by checking if the alternative direction is walkable, diggable, ...)
14965 =============================================================================
14968 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14969 int x, int y, int real_dx, int real_dy)
14971 int jx, jy, dx, dy, xx, yy;
14973 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14976 /* diagonal direction: check alternative direction */
14981 xx = jx + (dx == 0 ? real_dx : 0);
14982 yy = jy + (dy == 0 ? real_dy : 0);
14984 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14988 =============================================================================
14990 -----------------------------------------------------------------------------
14991 x, y: field next to player (non-diagonal) to try to dig to
14992 real_dx, real_dy: direction as read from input device (can be diagonal)
14993 =============================================================================
14996 static int DigField(struct PlayerInfo *player,
14997 int oldx, int oldy, int x, int y,
14998 int real_dx, int real_dy, int mode)
15000 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15001 boolean player_was_pushing = player->is_pushing;
15002 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15003 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15004 int jx = oldx, jy = oldy;
15005 int dx = x - jx, dy = y - jy;
15006 int nextx = x + dx, nexty = y + dy;
15007 int move_direction = (dx == -1 ? MV_LEFT :
15008 dx == +1 ? MV_RIGHT :
15010 dy == +1 ? MV_DOWN : MV_NONE);
15011 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15012 int dig_side = MV_DIR_OPPOSITE(move_direction);
15013 int old_element = Feld[jx][jy];
15014 #if USE_FIXED_DONT_RUN_INTO
15015 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15021 if (is_player) /* function can also be called by EL_PENGUIN */
15023 if (player->MovPos == 0)
15025 player->is_digging = FALSE;
15026 player->is_collecting = FALSE;
15029 if (player->MovPos == 0) /* last pushing move finished */
15030 player->is_pushing = FALSE;
15032 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15034 player->is_switching = FALSE;
15035 player->push_delay = -1;
15037 return MP_NO_ACTION;
15041 #if !USE_FIXED_DONT_RUN_INTO
15042 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15043 return MP_NO_ACTION;
15046 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15047 old_element = Back[jx][jy];
15049 /* in case of element dropped at player position, check background */
15050 else if (Back[jx][jy] != EL_EMPTY &&
15051 game.engine_version >= VERSION_IDENT(2,2,0,0))
15052 old_element = Back[jx][jy];
15054 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15055 return MP_NO_ACTION; /* field has no opening in this direction */
15057 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15058 return MP_NO_ACTION; /* field has no opening in this direction */
15060 #if USE_FIXED_DONT_RUN_INTO
15061 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15065 Feld[jx][jy] = player->artwork_element;
15066 InitMovingField(jx, jy, MV_DOWN);
15067 Store[jx][jy] = EL_ACID;
15068 ContinueMoving(jx, jy);
15069 BuryPlayer(player);
15071 return MP_DONT_RUN_INTO;
15075 #if USE_FIXED_DONT_RUN_INTO
15076 if (player_can_move && DONT_RUN_INTO(element))
15078 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15080 return MP_DONT_RUN_INTO;
15084 #if USE_FIXED_DONT_RUN_INTO
15085 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15086 return MP_NO_ACTION;
15089 #if !USE_FIXED_DONT_RUN_INTO
15090 element = Feld[x][y];
15093 collect_count = element_info[element].collect_count_initial;
15095 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15096 return MP_NO_ACTION;
15098 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15099 player_can_move = player_can_move_or_snap;
15101 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15102 game.engine_version >= VERSION_IDENT(2,2,0,0))
15104 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15105 player->index_bit, dig_side);
15106 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15107 player->index_bit, dig_side);
15109 if (element == EL_DC_LANDMINE)
15112 if (Feld[x][y] != element) /* field changed by snapping */
15115 return MP_NO_ACTION;
15118 #if USE_PLAYER_GRAVITY
15119 if (player->gravity && is_player && !player->is_auto_moving &&
15120 canFallDown(player) && move_direction != MV_DOWN &&
15121 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15122 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15124 if (game.gravity && is_player && !player->is_auto_moving &&
15125 canFallDown(player) && move_direction != MV_DOWN &&
15126 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15127 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15130 if (player_can_move &&
15131 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15133 int sound_element = SND_ELEMENT(element);
15134 int sound_action = ACTION_WALKING;
15136 if (IS_RND_GATE(element))
15138 if (!player->key[RND_GATE_NR(element)])
15139 return MP_NO_ACTION;
15141 else if (IS_RND_GATE_GRAY(element))
15143 if (!player->key[RND_GATE_GRAY_NR(element)])
15144 return MP_NO_ACTION;
15146 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15148 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15149 return MP_NO_ACTION;
15151 else if (element == EL_EXIT_OPEN ||
15152 element == EL_EM_EXIT_OPEN ||
15154 element == EL_EM_EXIT_OPENING ||
15156 element == EL_STEEL_EXIT_OPEN ||
15157 element == EL_EM_STEEL_EXIT_OPEN ||
15159 element == EL_EM_STEEL_EXIT_OPENING ||
15161 element == EL_SP_EXIT_OPEN ||
15162 element == EL_SP_EXIT_OPENING)
15164 sound_action = ACTION_PASSING; /* player is passing exit */
15166 else if (element == EL_EMPTY)
15168 sound_action = ACTION_MOVING; /* nothing to walk on */
15171 /* play sound from background or player, whatever is available */
15172 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15173 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15175 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15177 else if (player_can_move &&
15178 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15180 if (!ACCESS_FROM(element, opposite_direction))
15181 return MP_NO_ACTION; /* field not accessible from this direction */
15183 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15184 return MP_NO_ACTION;
15186 if (IS_EM_GATE(element))
15188 if (!player->key[EM_GATE_NR(element)])
15189 return MP_NO_ACTION;
15191 else if (IS_EM_GATE_GRAY(element))
15193 if (!player->key[EM_GATE_GRAY_NR(element)])
15194 return MP_NO_ACTION;
15196 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15198 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15199 return MP_NO_ACTION;
15201 else if (IS_EMC_GATE(element))
15203 if (!player->key[EMC_GATE_NR(element)])
15204 return MP_NO_ACTION;
15206 else if (IS_EMC_GATE_GRAY(element))
15208 if (!player->key[EMC_GATE_GRAY_NR(element)])
15209 return MP_NO_ACTION;
15211 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15213 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15214 return MP_NO_ACTION;
15216 else if (element == EL_DC_GATE_WHITE ||
15217 element == EL_DC_GATE_WHITE_GRAY ||
15218 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15220 if (player->num_white_keys == 0)
15221 return MP_NO_ACTION;
15223 player->num_white_keys--;
15225 else if (IS_SP_PORT(element))
15227 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15228 element == EL_SP_GRAVITY_PORT_RIGHT ||
15229 element == EL_SP_GRAVITY_PORT_UP ||
15230 element == EL_SP_GRAVITY_PORT_DOWN)
15231 #if USE_PLAYER_GRAVITY
15232 player->gravity = !player->gravity;
15234 game.gravity = !game.gravity;
15236 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15237 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15238 element == EL_SP_GRAVITY_ON_PORT_UP ||
15239 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15240 #if USE_PLAYER_GRAVITY
15241 player->gravity = TRUE;
15243 game.gravity = TRUE;
15245 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15246 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15247 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15248 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15249 #if USE_PLAYER_GRAVITY
15250 player->gravity = FALSE;
15252 game.gravity = FALSE;
15256 /* automatically move to the next field with double speed */
15257 player->programmed_action = move_direction;
15259 if (player->move_delay_reset_counter == 0)
15261 player->move_delay_reset_counter = 2; /* two double speed steps */
15263 DOUBLE_PLAYER_SPEED(player);
15266 PlayLevelSoundAction(x, y, ACTION_PASSING);
15268 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15272 if (mode != DF_SNAP)
15274 GfxElement[x][y] = GFX_ELEMENT(element);
15275 player->is_digging = TRUE;
15278 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15280 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15281 player->index_bit, dig_side);
15283 if (mode == DF_SNAP)
15285 #if USE_NEW_SNAP_DELAY
15286 if (level.block_snap_field)
15287 setFieldForSnapping(x, y, element, move_direction);
15289 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15291 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15294 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15295 player->index_bit, dig_side);
15298 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15302 if (is_player && mode != DF_SNAP)
15304 GfxElement[x][y] = element;
15305 player->is_collecting = TRUE;
15308 if (element == EL_SPEED_PILL)
15310 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15312 else if (element == EL_EXTRA_TIME && level.time > 0)
15314 TimeLeft += level.extra_time;
15317 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15319 DisplayGameControlValues();
15321 DrawGameValue_Time(TimeLeft);
15324 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15326 player->shield_normal_time_left += level.shield_normal_time;
15327 if (element == EL_SHIELD_DEADLY)
15328 player->shield_deadly_time_left += level.shield_deadly_time;
15330 else if (element == EL_DYNAMITE ||
15331 element == EL_EM_DYNAMITE ||
15332 element == EL_SP_DISK_RED)
15334 if (player->inventory_size < MAX_INVENTORY_SIZE)
15335 player->inventory_element[player->inventory_size++] = element;
15337 DrawGameDoorValues();
15339 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15341 player->dynabomb_count++;
15342 player->dynabombs_left++;
15344 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15346 player->dynabomb_size++;
15348 else if (element == EL_DYNABOMB_INCREASE_POWER)
15350 player->dynabomb_xl = TRUE;
15352 else if (IS_KEY(element))
15354 player->key[KEY_NR(element)] = TRUE;
15356 DrawGameDoorValues();
15358 else if (element == EL_DC_KEY_WHITE)
15360 player->num_white_keys++;
15362 /* display white keys? */
15363 /* DrawGameDoorValues(); */
15365 else if (IS_ENVELOPE(element))
15367 player->show_envelope = element;
15369 else if (element == EL_EMC_LENSES)
15371 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15373 RedrawAllInvisibleElementsForLenses();
15375 else if (element == EL_EMC_MAGNIFIER)
15377 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15379 RedrawAllInvisibleElementsForMagnifier();
15381 else if (IS_DROPPABLE(element) ||
15382 IS_THROWABLE(element)) /* can be collected and dropped */
15386 if (collect_count == 0)
15387 player->inventory_infinite_element = element;
15389 for (i = 0; i < collect_count; i++)
15390 if (player->inventory_size < MAX_INVENTORY_SIZE)
15391 player->inventory_element[player->inventory_size++] = element;
15393 DrawGameDoorValues();
15395 else if (collect_count > 0)
15397 local_player->gems_still_needed -= collect_count;
15398 if (local_player->gems_still_needed < 0)
15399 local_player->gems_still_needed = 0;
15402 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15404 DisplayGameControlValues();
15406 DrawGameValue_Emeralds(local_player->gems_still_needed);
15410 RaiseScoreElement(element);
15411 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15414 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15415 player->index_bit, dig_side);
15417 if (mode == DF_SNAP)
15419 #if USE_NEW_SNAP_DELAY
15420 if (level.block_snap_field)
15421 setFieldForSnapping(x, y, element, move_direction);
15423 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15425 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15428 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15429 player->index_bit, dig_side);
15432 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15434 if (mode == DF_SNAP && element != EL_BD_ROCK)
15435 return MP_NO_ACTION;
15437 if (CAN_FALL(element) && dy)
15438 return MP_NO_ACTION;
15440 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15441 !(element == EL_SPRING && level.use_spring_bug))
15442 return MP_NO_ACTION;
15444 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15445 ((move_direction & MV_VERTICAL &&
15446 ((element_info[element].move_pattern & MV_LEFT &&
15447 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15448 (element_info[element].move_pattern & MV_RIGHT &&
15449 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15450 (move_direction & MV_HORIZONTAL &&
15451 ((element_info[element].move_pattern & MV_UP &&
15452 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15453 (element_info[element].move_pattern & MV_DOWN &&
15454 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15455 return MP_NO_ACTION;
15457 /* do not push elements already moving away faster than player */
15458 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15459 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15460 return MP_NO_ACTION;
15462 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15464 if (player->push_delay_value == -1 || !player_was_pushing)
15465 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15467 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15469 if (player->push_delay_value == -1)
15470 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15472 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15474 if (!player->is_pushing)
15475 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15478 player->is_pushing = TRUE;
15479 player->is_active = TRUE;
15481 if (!(IN_LEV_FIELD(nextx, nexty) &&
15482 (IS_FREE(nextx, nexty) ||
15483 (IS_SB_ELEMENT(element) &&
15484 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15485 (IS_CUSTOM_ELEMENT(element) &&
15486 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15487 return MP_NO_ACTION;
15489 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15490 return MP_NO_ACTION;
15492 if (player->push_delay == -1) /* new pushing; restart delay */
15493 player->push_delay = 0;
15495 if (player->push_delay < player->push_delay_value &&
15496 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15497 element != EL_SPRING && element != EL_BALLOON)
15499 /* make sure that there is no move delay before next try to push */
15500 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15501 player->move_delay = 0;
15503 return MP_NO_ACTION;
15506 if (IS_CUSTOM_ELEMENT(element) &&
15507 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15509 if (!DigFieldByCE(nextx, nexty, element))
15510 return MP_NO_ACTION;
15513 if (IS_SB_ELEMENT(element))
15515 if (element == EL_SOKOBAN_FIELD_FULL)
15517 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15518 local_player->sokobanfields_still_needed++;
15521 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15523 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15524 local_player->sokobanfields_still_needed--;
15527 Feld[x][y] = EL_SOKOBAN_OBJECT;
15529 if (Back[x][y] == Back[nextx][nexty])
15530 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15531 else if (Back[x][y] != 0)
15532 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15535 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15539 if (local_player->sokobanfields_still_needed == 0 &&
15540 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15542 if (local_player->sokobanfields_still_needed == 0 &&
15543 game.emulation == EMU_SOKOBAN)
15546 PlayerWins(player);
15548 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15552 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15554 InitMovingField(x, y, move_direction);
15555 GfxAction[x][y] = ACTION_PUSHING;
15557 if (mode == DF_SNAP)
15558 ContinueMoving(x, y);
15560 MovPos[x][y] = (dx != 0 ? dx : dy);
15562 Pushed[x][y] = TRUE;
15563 Pushed[nextx][nexty] = TRUE;
15565 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15566 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15568 player->push_delay_value = -1; /* get new value later */
15570 /* check for element change _after_ element has been pushed */
15571 if (game.use_change_when_pushing_bug)
15573 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15574 player->index_bit, dig_side);
15575 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15576 player->index_bit, dig_side);
15579 else if (IS_SWITCHABLE(element))
15581 if (PLAYER_SWITCHING(player, x, y))
15583 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15584 player->index_bit, dig_side);
15589 player->is_switching = TRUE;
15590 player->switch_x = x;
15591 player->switch_y = y;
15593 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15595 if (element == EL_ROBOT_WHEEL)
15597 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15601 game.robot_wheel_active = TRUE;
15603 TEST_DrawLevelField(x, y);
15605 else if (element == EL_SP_TERMINAL)
15609 SCAN_PLAYFIELD(xx, yy)
15611 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15613 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15614 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15617 else if (IS_BELT_SWITCH(element))
15619 ToggleBeltSwitch(x, y);
15621 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15622 element == EL_SWITCHGATE_SWITCH_DOWN ||
15623 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15624 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15626 ToggleSwitchgateSwitch(x, y);
15628 else if (element == EL_LIGHT_SWITCH ||
15629 element == EL_LIGHT_SWITCH_ACTIVE)
15631 ToggleLightSwitch(x, y);
15633 else if (element == EL_TIMEGATE_SWITCH ||
15634 element == EL_DC_TIMEGATE_SWITCH)
15636 ActivateTimegateSwitch(x, y);
15638 else if (element == EL_BALLOON_SWITCH_LEFT ||
15639 element == EL_BALLOON_SWITCH_RIGHT ||
15640 element == EL_BALLOON_SWITCH_UP ||
15641 element == EL_BALLOON_SWITCH_DOWN ||
15642 element == EL_BALLOON_SWITCH_NONE ||
15643 element == EL_BALLOON_SWITCH_ANY)
15645 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15646 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15647 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15648 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15649 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15652 else if (element == EL_LAMP)
15654 Feld[x][y] = EL_LAMP_ACTIVE;
15655 local_player->lights_still_needed--;
15657 ResetGfxAnimation(x, y);
15658 TEST_DrawLevelField(x, y);
15660 else if (element == EL_TIME_ORB_FULL)
15662 Feld[x][y] = EL_TIME_ORB_EMPTY;
15664 if (level.time > 0 || level.use_time_orb_bug)
15666 TimeLeft += level.time_orb_time;
15667 game.no_time_limit = FALSE;
15670 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15672 DisplayGameControlValues();
15674 DrawGameValue_Time(TimeLeft);
15678 ResetGfxAnimation(x, y);
15679 TEST_DrawLevelField(x, y);
15681 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15682 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15686 game.ball_state = !game.ball_state;
15688 SCAN_PLAYFIELD(xx, yy)
15690 int e = Feld[xx][yy];
15692 if (game.ball_state)
15694 if (e == EL_EMC_MAGIC_BALL)
15695 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15696 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15697 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15701 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15702 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15703 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15704 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15709 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15710 player->index_bit, dig_side);
15712 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15713 player->index_bit, dig_side);
15715 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15716 player->index_bit, dig_side);
15722 if (!PLAYER_SWITCHING(player, x, y))
15724 player->is_switching = TRUE;
15725 player->switch_x = x;
15726 player->switch_y = y;
15728 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15729 player->index_bit, dig_side);
15730 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15731 player->index_bit, dig_side);
15733 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15734 player->index_bit, dig_side);
15735 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15736 player->index_bit, dig_side);
15739 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15740 player->index_bit, dig_side);
15741 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15742 player->index_bit, dig_side);
15744 return MP_NO_ACTION;
15747 player->push_delay = -1;
15749 if (is_player) /* function can also be called by EL_PENGUIN */
15751 if (Feld[x][y] != element) /* really digged/collected something */
15753 player->is_collecting = !player->is_digging;
15754 player->is_active = TRUE;
15761 static boolean DigFieldByCE(int x, int y, int digging_element)
15763 int element = Feld[x][y];
15765 if (!IS_FREE(x, y))
15767 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15768 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15771 /* no element can dig solid indestructible elements */
15772 if (IS_INDESTRUCTIBLE(element) &&
15773 !IS_DIGGABLE(element) &&
15774 !IS_COLLECTIBLE(element))
15777 if (AmoebaNr[x][y] &&
15778 (element == EL_AMOEBA_FULL ||
15779 element == EL_BD_AMOEBA ||
15780 element == EL_AMOEBA_GROWING))
15782 AmoebaCnt[AmoebaNr[x][y]]--;
15783 AmoebaCnt2[AmoebaNr[x][y]]--;
15786 if (IS_MOVING(x, y))
15787 RemoveMovingField(x, y);
15791 TEST_DrawLevelField(x, y);
15794 /* if digged element was about to explode, prevent the explosion */
15795 ExplodeField[x][y] = EX_TYPE_NONE;
15797 PlayLevelSoundAction(x, y, action);
15800 Store[x][y] = EL_EMPTY;
15803 /* this makes it possible to leave the removed element again */
15804 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15805 Store[x][y] = element;
15807 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15809 int move_leave_element = element_info[digging_element].move_leave_element;
15811 /* this makes it possible to leave the removed element again */
15812 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15813 element : move_leave_element);
15820 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15822 int jx = player->jx, jy = player->jy;
15823 int x = jx + dx, y = jy + dy;
15824 int snap_direction = (dx == -1 ? MV_LEFT :
15825 dx == +1 ? MV_RIGHT :
15827 dy == +1 ? MV_DOWN : MV_NONE);
15828 boolean can_continue_snapping = (level.continuous_snapping &&
15829 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15831 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15834 if (!player->active || !IN_LEV_FIELD(x, y))
15842 if (player->MovPos == 0)
15843 player->is_pushing = FALSE;
15845 player->is_snapping = FALSE;
15847 if (player->MovPos == 0)
15849 player->is_moving = FALSE;
15850 player->is_digging = FALSE;
15851 player->is_collecting = FALSE;
15857 #if USE_NEW_CONTINUOUS_SNAPPING
15858 /* prevent snapping with already pressed snap key when not allowed */
15859 if (player->is_snapping && !can_continue_snapping)
15862 if (player->is_snapping)
15866 player->MovDir = snap_direction;
15868 if (player->MovPos == 0)
15870 player->is_moving = FALSE;
15871 player->is_digging = FALSE;
15872 player->is_collecting = FALSE;
15875 player->is_dropping = FALSE;
15876 player->is_dropping_pressed = FALSE;
15877 player->drop_pressed_delay = 0;
15879 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15882 player->is_snapping = TRUE;
15883 player->is_active = TRUE;
15885 if (player->MovPos == 0)
15887 player->is_moving = FALSE;
15888 player->is_digging = FALSE;
15889 player->is_collecting = FALSE;
15892 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15893 TEST_DrawLevelField(player->last_jx, player->last_jy);
15895 TEST_DrawLevelField(x, y);
15900 static boolean DropElement(struct PlayerInfo *player)
15902 int old_element, new_element;
15903 int dropx = player->jx, dropy = player->jy;
15904 int drop_direction = player->MovDir;
15905 int drop_side = drop_direction;
15907 int drop_element = get_next_dropped_element(player);
15909 int drop_element = (player->inventory_size > 0 ?
15910 player->inventory_element[player->inventory_size - 1] :
15911 player->inventory_infinite_element != EL_UNDEFINED ?
15912 player->inventory_infinite_element :
15913 player->dynabombs_left > 0 ?
15914 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15918 player->is_dropping_pressed = TRUE;
15920 /* do not drop an element on top of another element; when holding drop key
15921 pressed without moving, dropped element must move away before the next
15922 element can be dropped (this is especially important if the next element
15923 is dynamite, which can be placed on background for historical reasons) */
15924 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15927 if (IS_THROWABLE(drop_element))
15929 dropx += GET_DX_FROM_DIR(drop_direction);
15930 dropy += GET_DY_FROM_DIR(drop_direction);
15932 if (!IN_LEV_FIELD(dropx, dropy))
15936 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15937 new_element = drop_element; /* default: no change when dropping */
15939 /* check if player is active, not moving and ready to drop */
15940 if (!player->active || player->MovPos || player->drop_delay > 0)
15943 /* check if player has anything that can be dropped */
15944 if (new_element == EL_UNDEFINED)
15947 /* check if drop key was pressed long enough for EM style dynamite */
15948 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15951 /* check if anything can be dropped at the current position */
15952 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15955 /* collected custom elements can only be dropped on empty fields */
15956 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15959 if (old_element != EL_EMPTY)
15960 Back[dropx][dropy] = old_element; /* store old element on this field */
15962 ResetGfxAnimation(dropx, dropy);
15963 ResetRandomAnimationValue(dropx, dropy);
15965 if (player->inventory_size > 0 ||
15966 player->inventory_infinite_element != EL_UNDEFINED)
15968 if (player->inventory_size > 0)
15970 player->inventory_size--;
15972 DrawGameDoorValues();
15974 if (new_element == EL_DYNAMITE)
15975 new_element = EL_DYNAMITE_ACTIVE;
15976 else if (new_element == EL_EM_DYNAMITE)
15977 new_element = EL_EM_DYNAMITE_ACTIVE;
15978 else if (new_element == EL_SP_DISK_RED)
15979 new_element = EL_SP_DISK_RED_ACTIVE;
15982 Feld[dropx][dropy] = new_element;
15984 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15985 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15986 el2img(Feld[dropx][dropy]), 0);
15988 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15990 /* needed if previous element just changed to "empty" in the last frame */
15991 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15993 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15994 player->index_bit, drop_side);
15995 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15997 player->index_bit, drop_side);
15999 TestIfElementTouchesCustomElement(dropx, dropy);
16001 else /* player is dropping a dyna bomb */
16003 player->dynabombs_left--;
16005 Feld[dropx][dropy] = new_element;
16007 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16008 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16009 el2img(Feld[dropx][dropy]), 0);
16011 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16014 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16015 InitField_WithBug1(dropx, dropy, FALSE);
16017 new_element = Feld[dropx][dropy]; /* element might have changed */
16019 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16020 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16023 int move_direction;
16027 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16028 MovDir[dropx][dropy] = drop_direction;
16031 move_direction = MovDir[dropx][dropy];
16032 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16033 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16036 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16038 #if USE_FIX_IMPACT_COLLISION
16039 /* do not cause impact style collision by dropping elements that can fall */
16040 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16042 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16046 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16047 player->is_dropping = TRUE;
16049 player->drop_pressed_delay = 0;
16050 player->is_dropping_pressed = FALSE;
16052 player->drop_x = dropx;
16053 player->drop_y = dropy;
16058 /* ------------------------------------------------------------------------- */
16059 /* game sound playing functions */
16060 /* ------------------------------------------------------------------------- */
16062 static int *loop_sound_frame = NULL;
16063 static int *loop_sound_volume = NULL;
16065 void InitPlayLevelSound()
16067 int num_sounds = getSoundListSize();
16069 checked_free(loop_sound_frame);
16070 checked_free(loop_sound_volume);
16072 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16073 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16076 static void PlayLevelSound(int x, int y, int nr)
16078 int sx = SCREENX(x), sy = SCREENY(y);
16079 int volume, stereo_position;
16080 int max_distance = 8;
16081 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16083 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16084 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16087 if (!IN_LEV_FIELD(x, y) ||
16088 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16089 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16092 volume = SOUND_MAX_VOLUME;
16094 if (!IN_SCR_FIELD(sx, sy))
16096 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16097 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16099 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16102 stereo_position = (SOUND_MAX_LEFT +
16103 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16104 (SCR_FIELDX + 2 * max_distance));
16106 if (IS_LOOP_SOUND(nr))
16108 /* This assures that quieter loop sounds do not overwrite louder ones,
16109 while restarting sound volume comparison with each new game frame. */
16111 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16114 loop_sound_volume[nr] = volume;
16115 loop_sound_frame[nr] = FrameCounter;
16118 PlaySoundExt(nr, volume, stereo_position, type);
16121 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16123 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16124 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16125 y < LEVELY(BY1) ? LEVELY(BY1) :
16126 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16130 static void PlayLevelSoundAction(int x, int y, int action)
16132 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16135 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16137 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16139 if (sound_effect != SND_UNDEFINED)
16140 PlayLevelSound(x, y, sound_effect);
16143 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16146 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16148 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16149 PlayLevelSound(x, y, sound_effect);
16152 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16154 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16156 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16157 PlayLevelSound(x, y, sound_effect);
16160 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16162 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16164 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16165 StopSound(sound_effect);
16168 static void PlayLevelMusic()
16170 if (levelset.music[level_nr] != MUS_UNDEFINED)
16171 PlayMusic(levelset.music[level_nr]); /* from config file */
16173 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16176 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16178 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16179 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16180 int x = xx - 1 - offset;
16181 int y = yy - 1 - offset;
16186 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16190 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16194 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16198 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16202 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16206 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16210 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16213 case SAMPLE_android_clone:
16214 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16217 case SAMPLE_android_move:
16218 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16221 case SAMPLE_spring:
16222 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16226 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16230 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16233 case SAMPLE_eater_eat:
16234 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16238 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16241 case SAMPLE_collect:
16242 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16245 case SAMPLE_diamond:
16246 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16249 case SAMPLE_squash:
16250 /* !!! CHECK THIS !!! */
16252 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16254 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16258 case SAMPLE_wonderfall:
16259 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16263 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16267 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16271 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16275 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16279 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16283 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16286 case SAMPLE_wonder:
16287 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16291 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16294 case SAMPLE_exit_open:
16295 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16298 case SAMPLE_exit_leave:
16299 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16302 case SAMPLE_dynamite:
16303 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16307 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16311 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16315 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16319 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16323 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16327 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16331 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16336 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16338 int element = map_element_SP_to_RND(element_sp);
16339 int action = map_action_SP_to_RND(action_sp);
16340 int offset = (setup.sp_show_border_elements ? 0 : 1);
16341 int x = xx - offset;
16342 int y = yy - offset;
16345 printf("::: %d -> %d\n", element_sp, action_sp);
16348 PlayLevelSoundElementAction(x, y, element, action);
16351 void RaiseScore(int value)
16353 local_player->score += value;
16356 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16358 DisplayGameControlValues();
16360 DrawGameValue_Score(local_player->score);
16364 void RaiseScoreElement(int element)
16369 case EL_BD_DIAMOND:
16370 case EL_EMERALD_YELLOW:
16371 case EL_EMERALD_RED:
16372 case EL_EMERALD_PURPLE:
16373 case EL_SP_INFOTRON:
16374 RaiseScore(level.score[SC_EMERALD]);
16377 RaiseScore(level.score[SC_DIAMOND]);
16380 RaiseScore(level.score[SC_CRYSTAL]);
16383 RaiseScore(level.score[SC_PEARL]);
16386 case EL_BD_BUTTERFLY:
16387 case EL_SP_ELECTRON:
16388 RaiseScore(level.score[SC_BUG]);
16391 case EL_BD_FIREFLY:
16392 case EL_SP_SNIKSNAK:
16393 RaiseScore(level.score[SC_SPACESHIP]);
16396 case EL_DARK_YAMYAM:
16397 RaiseScore(level.score[SC_YAMYAM]);
16400 RaiseScore(level.score[SC_ROBOT]);
16403 RaiseScore(level.score[SC_PACMAN]);
16406 RaiseScore(level.score[SC_NUT]);
16409 case EL_EM_DYNAMITE:
16410 case EL_SP_DISK_RED:
16411 case EL_DYNABOMB_INCREASE_NUMBER:
16412 case EL_DYNABOMB_INCREASE_SIZE:
16413 case EL_DYNABOMB_INCREASE_POWER:
16414 RaiseScore(level.score[SC_DYNAMITE]);
16416 case EL_SHIELD_NORMAL:
16417 case EL_SHIELD_DEADLY:
16418 RaiseScore(level.score[SC_SHIELD]);
16420 case EL_EXTRA_TIME:
16421 RaiseScore(level.extra_time_score);
16435 case EL_DC_KEY_WHITE:
16436 RaiseScore(level.score[SC_KEY]);
16439 RaiseScore(element_info[element].collect_score);
16444 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16446 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16448 #if defined(NETWORK_AVALIABLE)
16449 if (options.network)
16450 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16459 FadeSkipNextFadeIn();
16461 fading = fading_none;
16465 OpenDoor(DOOR_CLOSE_1);
16468 game_status = GAME_MODE_MAIN;
16471 DrawAndFadeInMainMenu(REDRAW_FIELD);
16479 FadeOut(REDRAW_FIELD);
16482 game_status = GAME_MODE_MAIN;
16484 DrawAndFadeInMainMenu(REDRAW_FIELD);
16488 else /* continue playing the game */
16490 if (tape.playing && tape.deactivate_display)
16491 TapeDeactivateDisplayOff(TRUE);
16493 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16495 if (tape.playing && tape.deactivate_display)
16496 TapeDeactivateDisplayOn();
16500 void RequestQuitGame(boolean ask_if_really_quit)
16502 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16503 boolean skip_request = AllPlayersGone || quick_quit;
16505 RequestQuitGameExt(skip_request, quick_quit,
16506 "Do you really want to quit the game?");
16510 /* ------------------------------------------------------------------------- */
16511 /* random generator functions */
16512 /* ------------------------------------------------------------------------- */
16514 unsigned int InitEngineRandom_RND(int seed)
16516 game.num_random_calls = 0;
16519 unsigned int rnd_seed = InitEngineRandom(seed);
16521 printf("::: START RND: %d\n", rnd_seed);
16526 return InitEngineRandom(seed);
16532 unsigned int RND(int max)
16536 game.num_random_calls++;
16538 return GetEngineRandom(max);
16545 /* ------------------------------------------------------------------------- */
16546 /* game engine snapshot handling functions */
16547 /* ------------------------------------------------------------------------- */
16549 struct EngineSnapshotInfo
16551 /* runtime values for custom element collect score */
16552 int collect_score[NUM_CUSTOM_ELEMENTS];
16554 /* runtime values for group element choice position */
16555 int choice_pos[NUM_GROUP_ELEMENTS];
16557 /* runtime values for belt position animations */
16558 int belt_graphic[4][NUM_BELT_PARTS];
16559 int belt_anim_mode[4][NUM_BELT_PARTS];
16562 static struct EngineSnapshotInfo engine_snapshot_rnd;
16563 static char *snapshot_level_identifier = NULL;
16564 static int snapshot_level_nr = -1;
16566 static void SaveEngineSnapshotValues_RND()
16568 static int belt_base_active_element[4] =
16570 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16571 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16572 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16573 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16577 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16579 int element = EL_CUSTOM_START + i;
16581 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16584 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16586 int element = EL_GROUP_START + i;
16588 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16591 for (i = 0; i < 4; i++)
16593 for (j = 0; j < NUM_BELT_PARTS; j++)
16595 int element = belt_base_active_element[i] + j;
16596 int graphic = el2img(element);
16597 int anim_mode = graphic_info[graphic].anim_mode;
16599 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16600 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16605 static void LoadEngineSnapshotValues_RND()
16607 unsigned int num_random_calls = game.num_random_calls;
16610 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16612 int element = EL_CUSTOM_START + i;
16614 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16617 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16619 int element = EL_GROUP_START + i;
16621 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16624 for (i = 0; i < 4; i++)
16626 for (j = 0; j < NUM_BELT_PARTS; j++)
16628 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16629 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16631 graphic_info[graphic].anim_mode = anim_mode;
16635 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16637 InitRND(tape.random_seed);
16638 for (i = 0; i < num_random_calls; i++)
16642 if (game.num_random_calls != num_random_calls)
16644 Error(ERR_INFO, "number of random calls out of sync");
16645 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16646 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16647 Error(ERR_EXIT, "this should not happen -- please debug");
16651 void SaveEngineSnapshot()
16653 /* do not save snapshots from editor */
16654 if (level_editor_test_game)
16657 /* free previous snapshot buffers, if needed */
16658 FreeEngineSnapshotBuffers();
16660 /* copy some special values to a structure better suited for the snapshot */
16662 SaveEngineSnapshotValues_RND();
16663 SaveEngineSnapshotValues_EM();
16664 SaveEngineSnapshotValues_SP();
16666 /* save values stored in special snapshot structure */
16668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16672 /* save further RND engine values */
16674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16691 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16723 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16738 /* save level identification information */
16740 setString(&snapshot_level_identifier, leveldir_current->identifier);
16741 snapshot_level_nr = level_nr;
16744 ListNode *node = engine_snapshot_list_rnd;
16747 while (node != NULL)
16749 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16754 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16758 void LoadEngineSnapshot()
16760 /* restore generically stored snapshot buffers */
16762 LoadEngineSnapshotBuffers();
16764 /* restore special values from snapshot structure */
16766 LoadEngineSnapshotValues_RND();
16767 LoadEngineSnapshotValues_EM();
16768 LoadEngineSnapshotValues_SP();
16771 boolean CheckEngineSnapshot()
16773 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16774 snapshot_level_nr == level_nr);
16778 /* ---------- new game button stuff ---------------------------------------- */
16786 } gamebutton_info[NUM_GAME_BUTTONS] =
16789 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16790 GAME_CTRL_ID_STOP, "stop game"
16793 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16794 GAME_CTRL_ID_PAUSE, "pause game"
16797 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16798 GAME_CTRL_ID_PLAY, "play game"
16801 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16802 SOUND_CTRL_ID_MUSIC, "background music on/off"
16805 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16806 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16809 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16810 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16813 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
16814 GAME_CTRL_ID_SAVE, "save game"
16817 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
16818 GAME_CTRL_ID_LOAD, "load game"
16822 void CreateGameButtons()
16826 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16828 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16829 struct Rect *pos = gamebutton_info[i].pos;
16830 struct GadgetInfo *gi;
16833 unsigned int event_mask;
16834 int base_x = (tape.show_game_buttons ? VX : DX);
16835 int base_y = (tape.show_game_buttons ? VY : DY);
16836 int gd_x = gfx->src_x;
16837 int gd_y = gfx->src_y;
16838 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16839 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16840 int gd_xa = gfx->src_x + gfx->active_xoffset;
16841 int gd_ya = gfx->src_y + gfx->active_yoffset;
16842 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16843 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16846 if (gfx->bitmap == NULL)
16848 game_gadget[id] = NULL;
16853 if (id == GAME_CTRL_ID_STOP ||
16854 id == GAME_CTRL_ID_PAUSE ||
16855 id == GAME_CTRL_ID_PLAY ||
16856 id == GAME_CTRL_ID_SAVE ||
16857 id == GAME_CTRL_ID_LOAD)
16859 button_type = GD_TYPE_NORMAL_BUTTON;
16861 event_mask = GD_EVENT_RELEASED;
16865 button_type = GD_TYPE_CHECK_BUTTON;
16867 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16868 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16869 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16870 event_mask = GD_EVENT_PRESSED;
16873 gi = CreateGadget(GDI_CUSTOM_ID, id,
16874 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16875 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
16876 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
16877 GDI_WIDTH, gfx->width,
16878 GDI_HEIGHT, gfx->height,
16879 GDI_TYPE, button_type,
16880 GDI_STATE, GD_BUTTON_UNPRESSED,
16881 GDI_CHECKED, checked,
16882 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16883 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16884 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16885 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16886 GDI_DIRECT_DRAW, FALSE,
16887 GDI_EVENT_MASK, event_mask,
16888 GDI_CALLBACK_ACTION, HandleGameButtons,
16892 Error(ERR_EXIT, "cannot create gadget");
16894 game_gadget[id] = gi;
16898 void FreeGameButtons()
16902 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16903 FreeGadget(game_gadget[i]);
16906 void MapGameButtons()
16910 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16911 MapGadget(game_gadget[i]);
16914 void UnmapGameButtons()
16918 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16919 UnmapGadget(game_gadget[i]);
16922 void RedrawGameButtons()
16926 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16927 RedrawGadget(game_gadget[i]);
16930 static void HandleGameButtonsExt(int id)
16932 boolean handle_game_buttons =
16933 (game_status == GAME_MODE_PLAYING ||
16934 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16936 if (!handle_game_buttons)
16941 case GAME_CTRL_ID_STOP:
16942 if (game_status == GAME_MODE_MAIN)
16948 RequestQuitGame(TRUE);
16951 case GAME_CTRL_ID_PAUSE:
16952 if (options.network && game_status == GAME_MODE_PLAYING)
16954 #if defined(NETWORK_AVALIABLE)
16956 SendToServer_ContinuePlaying();
16958 SendToServer_PausePlaying();
16962 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16965 case GAME_CTRL_ID_PLAY:
16966 if (game_status == GAME_MODE_MAIN)
16968 StartGameActions(options.network, setup.autorecord, level.random_seed);
16970 else if (tape.pausing)
16972 #if defined(NETWORK_AVALIABLE)
16973 if (options.network)
16974 SendToServer_ContinuePlaying();
16978 tape.pausing = FALSE;
16979 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16984 case SOUND_CTRL_ID_MUSIC:
16985 if (setup.sound_music)
16987 setup.sound_music = FALSE;
16991 else if (audio.music_available)
16993 setup.sound = setup.sound_music = TRUE;
16995 SetAudioMode(setup.sound);
17001 case SOUND_CTRL_ID_LOOPS:
17002 if (setup.sound_loops)
17003 setup.sound_loops = FALSE;
17004 else if (audio.loops_available)
17006 setup.sound = setup.sound_loops = TRUE;
17008 SetAudioMode(setup.sound);
17012 case SOUND_CTRL_ID_SIMPLE:
17013 if (setup.sound_simple)
17014 setup.sound_simple = FALSE;
17015 else if (audio.sound_available)
17017 setup.sound = setup.sound_simple = TRUE;
17019 SetAudioMode(setup.sound);
17023 case GAME_CTRL_ID_SAVE:
17027 case GAME_CTRL_ID_LOAD:
17036 static void HandleGameButtons(struct GadgetInfo *gi)
17038 HandleGameButtonsExt(gi->custom_id);
17041 void HandleSoundButtonKeys(Key key)
17044 if (key == setup.shortcut.sound_simple)
17045 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17046 else if (key == setup.shortcut.sound_loops)
17047 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17048 else if (key == setup.shortcut.sound_music)
17049 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17051 if (key == setup.shortcut.sound_simple)
17052 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17053 else if (key == setup.shortcut.sound_loops)
17054 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17055 else if (key == setup.shortcut.sound_music)
17056 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);