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
1018 #define NUM_GAME_BUTTONS 6
1021 /* forward declaration for internal use */
1023 static void CreateField(int, int, int);
1025 static void ResetGfxAnimation(int, int);
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1051 static void TestIfElementSmashesCustomElement(int, int, int);
1054 static void HandleElementChange(int, int, int);
1055 static void ExecuteCustomElementAction(int, int, int, int);
1056 static boolean ChangeElement(int, int, int, int);
1058 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1059 #define CheckTriggeredElementChange(x, y, e, ev) \
1060 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1061 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1062 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1063 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1064 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1065 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1066 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1068 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1069 #define CheckElementChange(x, y, e, te, ev) \
1070 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1071 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1072 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1073 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1074 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1076 static void PlayLevelSound(int, int, int);
1077 static void PlayLevelSoundNearest(int, int, int);
1078 static void PlayLevelSoundAction(int, int, int);
1079 static void PlayLevelSoundElementAction(int, int, int, int);
1080 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1081 static void PlayLevelSoundActionIfLoop(int, int, int);
1082 static void StopLevelSoundActionIfLoop(int, int, int);
1083 static void PlayLevelMusic();
1085 static void MapGameButtons();
1086 static void HandleGameButtons(struct GadgetInfo *);
1088 int AmoebeNachbarNr(int, int);
1089 void AmoebeUmwandeln(int, int);
1090 void ContinueMoving(int, int);
1091 void Bang(int, int);
1092 void InitMovDir(int, int);
1093 void InitAmoebaNr(int, int);
1094 int NewHiScore(void);
1096 void TestIfGoodThingHitsBadThing(int, int, int);
1097 void TestIfBadThingHitsGoodThing(int, int, int);
1098 void TestIfPlayerTouchesBadThing(int, int);
1099 void TestIfPlayerRunsIntoBadThing(int, int, int);
1100 void TestIfBadThingTouchesPlayer(int, int);
1101 void TestIfBadThingRunsIntoPlayer(int, int, int);
1102 void TestIfFriendTouchesBadThing(int, int);
1103 void TestIfBadThingTouchesFriend(int, int);
1104 void TestIfBadThingTouchesOtherBadThing(int, int);
1105 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1107 void KillPlayer(struct PlayerInfo *);
1108 void BuryPlayer(struct PlayerInfo *);
1109 void RemovePlayer(struct PlayerInfo *);
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1120 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1122 if (recursion_loop_detected) \
1125 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1127 recursion_loop_detected = TRUE; \
1128 recursion_loop_element = (e); \
1131 recursion_loop_depth++; \
1134 #define RECURSION_LOOP_DETECTION_END() \
1136 recursion_loop_depth--; \
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1143 static int map_player_action[MAX_PLAYERS];
1146 /* ------------------------------------------------------------------------- */
1147 /* definition of elements that automatically change to other elements after */
1148 /* a specified time, eventually calling a function when changing */
1149 /* ------------------------------------------------------------------------- */
1151 /* forward declaration for changer functions */
1152 static void InitBuggyBase(int, int);
1153 static void WarnBuggyBase(int, int);
1155 static void InitTrap(int, int);
1156 static void ActivateTrap(int, int);
1157 static void ChangeActiveTrap(int, int);
1159 static void InitRobotWheel(int, int);
1160 static void RunRobotWheel(int, int);
1161 static void StopRobotWheel(int, int);
1163 static void InitTimegateWheel(int, int);
1164 static void RunTimegateWheel(int, int);
1166 static void InitMagicBallDelay(int, int);
1167 static void ActivateMagicBall(int, int);
1169 struct ChangingElementInfo
1174 void (*pre_change_function)(int x, int y);
1175 void (*change_function)(int x, int y);
1176 void (*post_change_function)(int x, int y);
1179 static struct ChangingElementInfo change_delay_list[] =
1214 EL_STEEL_EXIT_OPENING,
1222 EL_STEEL_EXIT_CLOSING,
1223 EL_STEEL_EXIT_CLOSED,
1250 EL_EM_STEEL_EXIT_OPENING,
1251 EL_EM_STEEL_EXIT_OPEN,
1258 EL_EM_STEEL_EXIT_CLOSING,
1262 EL_EM_STEEL_EXIT_CLOSED,
1286 EL_SWITCHGATE_OPENING,
1294 EL_SWITCHGATE_CLOSING,
1295 EL_SWITCHGATE_CLOSED,
1302 EL_TIMEGATE_OPENING,
1310 EL_TIMEGATE_CLOSING,
1319 EL_ACID_SPLASH_LEFT,
1327 EL_ACID_SPLASH_RIGHT,
1336 EL_SP_BUGGY_BASE_ACTIVATING,
1343 EL_SP_BUGGY_BASE_ACTIVATING,
1344 EL_SP_BUGGY_BASE_ACTIVE,
1351 EL_SP_BUGGY_BASE_ACTIVE,
1375 EL_ROBOT_WHEEL_ACTIVE,
1383 EL_TIMEGATE_SWITCH_ACTIVE,
1391 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392 EL_DC_TIMEGATE_SWITCH,
1399 EL_EMC_MAGIC_BALL_ACTIVE,
1400 EL_EMC_MAGIC_BALL_ACTIVE,
1407 EL_EMC_SPRING_BUMPER_ACTIVE,
1408 EL_EMC_SPRING_BUMPER,
1415 EL_DIAGONAL_SHRINKING,
1423 EL_DIAGONAL_GROWING,
1444 int push_delay_fixed, push_delay_random;
1448 { EL_SPRING, 0, 0 },
1449 { EL_BALLOON, 0, 0 },
1451 { EL_SOKOBAN_OBJECT, 2, 0 },
1452 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1453 { EL_SATELLITE, 2, 0 },
1454 { EL_SP_DISK_YELLOW, 2, 0 },
1456 { EL_UNDEFINED, 0, 0 },
1464 move_stepsize_list[] =
1466 { EL_AMOEBA_DROP, 2 },
1467 { EL_AMOEBA_DROPPING, 2 },
1468 { EL_QUICKSAND_FILLING, 1 },
1469 { EL_QUICKSAND_EMPTYING, 1 },
1470 { EL_QUICKSAND_FAST_FILLING, 2 },
1471 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472 { EL_MAGIC_WALL_FILLING, 2 },
1473 { EL_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_BD_MAGIC_WALL_FILLING, 2 },
1475 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_DC_MAGIC_WALL_FILLING, 2 },
1477 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1479 { EL_UNDEFINED, 0 },
1487 collect_count_list[] =
1490 { EL_BD_DIAMOND, 1 },
1491 { EL_EMERALD_YELLOW, 1 },
1492 { EL_EMERALD_RED, 1 },
1493 { EL_EMERALD_PURPLE, 1 },
1495 { EL_SP_INFOTRON, 1 },
1499 { EL_UNDEFINED, 0 },
1507 access_direction_list[] =
1509 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1512 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1513 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1514 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1515 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1516 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1517 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1518 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1519 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1521 { EL_SP_PORT_LEFT, MV_RIGHT },
1522 { EL_SP_PORT_RIGHT, MV_LEFT },
1523 { EL_SP_PORT_UP, MV_DOWN },
1524 { EL_SP_PORT_DOWN, MV_UP },
1525 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1526 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1527 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1529 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1530 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1531 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1532 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1533 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1534 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1535 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1536 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1537 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1538 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1539 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1541 { EL_UNDEFINED, MV_NONE }
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1546 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1549 IS_JUST_CHANGING(x, y))
1551 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1553 /* static variables for playfield scan mode (scanning forward or backward) */
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1559 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1560 (y) >= 0 && (y) <= lev_fieldy - 1; \
1561 (y) += playfield_scan_delta_y) \
1562 for ((x) = playfield_scan_start_x; \
1563 (x) >= 0 && (x) <= lev_fieldx - 1; \
1564 (x) += playfield_scan_delta_x)
1567 void DEBUG_SetMaximumDynamite()
1571 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573 local_player->inventory_element[local_player->inventory_size++] =
1578 static void InitPlayfieldScanModeVars()
1580 if (game.use_reverse_scan_direction)
1582 playfield_scan_start_x = lev_fieldx - 1;
1583 playfield_scan_start_y = lev_fieldy - 1;
1585 playfield_scan_delta_x = -1;
1586 playfield_scan_delta_y = -1;
1590 playfield_scan_start_x = 0;
1591 playfield_scan_start_y = 0;
1593 playfield_scan_delta_x = 1;
1594 playfield_scan_delta_y = 1;
1598 static void InitPlayfieldScanMode(int mode)
1600 game.use_reverse_scan_direction =
1601 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1603 InitPlayfieldScanModeVars();
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1609 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1611 /* make sure that stepsize value is always a power of 2 */
1612 move_stepsize = (1 << log_2(move_stepsize));
1614 return TILEX / move_stepsize;
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1620 int player_nr = player->index_nr;
1621 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1624 /* do no immediately change move delay -- the player might just be moving */
1625 player->move_delay_value_next = move_delay;
1627 /* information if player can move must be set separately */
1628 player->cannot_move = cannot_move;
1632 player->move_delay = game.initial_move_delay[player_nr];
1633 player->move_delay_value = game.initial_move_delay_value[player_nr];
1635 player->move_delay_value_next = -1;
1637 player->move_delay_reset_counter = 0;
1641 void GetPlayerConfig()
1643 GameFrameDelay = setup.game_frame_delay;
1645 if (!audio.sound_available)
1646 setup.sound_simple = FALSE;
1648 if (!audio.loops_available)
1649 setup.sound_loops = FALSE;
1651 if (!audio.music_available)
1652 setup.sound_music = FALSE;
1654 if (!video.fullscreen_available)
1655 setup.fullscreen = FALSE;
1657 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1659 SetAudioMode(setup.sound);
1663 int GetElementFromGroupElement(int element)
1665 if (IS_GROUP_ELEMENT(element))
1667 struct ElementGroupInfo *group = element_info[element].group;
1668 int last_anim_random_frame = gfx.anim_random_frame;
1671 if (group->choice_mode == ANIM_RANDOM)
1672 gfx.anim_random_frame = RND(group->num_elements_resolved);
1674 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1675 group->choice_mode, 0,
1678 if (group->choice_mode == ANIM_RANDOM)
1679 gfx.anim_random_frame = last_anim_random_frame;
1681 group->choice_pos++;
1683 element = group->element_resolved[element_pos];
1689 static void InitPlayerField(int x, int y, int element, boolean init_game)
1691 if (element == EL_SP_MURPHY)
1695 if (stored_player[0].present)
1697 Feld[x][y] = EL_SP_MURPHY_CLONE;
1703 stored_player[0].initial_element = element;
1704 stored_player[0].use_murphy = TRUE;
1706 if (!level.use_artwork_element[0])
1707 stored_player[0].artwork_element = EL_SP_MURPHY;
1710 Feld[x][y] = EL_PLAYER_1;
1716 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1717 int jx = player->jx, jy = player->jy;
1719 player->present = TRUE;
1721 player->block_last_field = (element == EL_SP_MURPHY ?
1722 level.sp_block_last_field :
1723 level.block_last_field);
1725 /* ---------- initialize player's last field block delay --------------- */
1727 /* always start with reliable default value (no adjustment needed) */
1728 player->block_delay_adjustment = 0;
1730 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1731 if (player->block_last_field && element == EL_SP_MURPHY)
1732 player->block_delay_adjustment = 1;
1734 /* special case 2: in game engines before 3.1.1, blocking was different */
1735 if (game.use_block_last_field_bug)
1736 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1738 if (!options.network || player->connected)
1740 player->active = TRUE;
1742 /* remove potentially duplicate players */
1743 if (StorePlayer[jx][jy] == Feld[x][y])
1744 StorePlayer[jx][jy] = 0;
1746 StorePlayer[x][y] = Feld[x][y];
1748 #if DEBUG_INIT_PLAYER
1751 printf("- player element %d activated", player->element_nr);
1752 printf(" (local player is %d and currently %s)\n",
1753 local_player->element_nr,
1754 local_player->active ? "active" : "not active");
1759 Feld[x][y] = EL_EMPTY;
1761 player->jx = player->last_jx = x;
1762 player->jy = player->last_jy = y;
1765 #if USE_PLAYER_REANIMATION
1768 int player_nr = GET_PLAYER_NR(element);
1769 struct PlayerInfo *player = &stored_player[player_nr];
1771 if (player->active && player->killed)
1772 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1777 static void InitField(int x, int y, boolean init_game)
1779 int element = Feld[x][y];
1788 InitPlayerField(x, y, element, init_game);
1791 case EL_SOKOBAN_FIELD_PLAYER:
1792 element = Feld[x][y] = EL_PLAYER_1;
1793 InitField(x, y, init_game);
1795 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1796 InitField(x, y, init_game);
1799 case EL_SOKOBAN_FIELD_EMPTY:
1800 local_player->sokobanfields_still_needed++;
1804 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1806 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1807 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1808 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1809 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1810 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1811 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1812 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1813 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1822 case EL_SPACESHIP_RIGHT:
1823 case EL_SPACESHIP_UP:
1824 case EL_SPACESHIP_LEFT:
1825 case EL_SPACESHIP_DOWN:
1826 case EL_BD_BUTTERFLY:
1827 case EL_BD_BUTTERFLY_RIGHT:
1828 case EL_BD_BUTTERFLY_UP:
1829 case EL_BD_BUTTERFLY_LEFT:
1830 case EL_BD_BUTTERFLY_DOWN:
1832 case EL_BD_FIREFLY_RIGHT:
1833 case EL_BD_FIREFLY_UP:
1834 case EL_BD_FIREFLY_LEFT:
1835 case EL_BD_FIREFLY_DOWN:
1836 case EL_PACMAN_RIGHT:
1838 case EL_PACMAN_LEFT:
1839 case EL_PACMAN_DOWN:
1841 case EL_YAMYAM_LEFT:
1842 case EL_YAMYAM_RIGHT:
1844 case EL_YAMYAM_DOWN:
1845 case EL_DARK_YAMYAM:
1848 case EL_SP_SNIKSNAK:
1849 case EL_SP_ELECTRON:
1858 case EL_AMOEBA_FULL:
1863 case EL_AMOEBA_DROP:
1864 if (y == lev_fieldy - 1)
1866 Feld[x][y] = EL_AMOEBA_GROWING;
1867 Store[x][y] = EL_AMOEBA_WET;
1871 case EL_DYNAMITE_ACTIVE:
1872 case EL_SP_DISK_RED_ACTIVE:
1873 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1874 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1875 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1876 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1877 MovDelay[x][y] = 96;
1880 case EL_EM_DYNAMITE_ACTIVE:
1881 MovDelay[x][y] = 32;
1885 local_player->lights_still_needed++;
1889 local_player->friends_still_needed++;
1894 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1897 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1903 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1904 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1905 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1906 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1907 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1908 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1911 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1912 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1913 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1915 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1917 game.belt_dir[belt_nr] = belt_dir;
1918 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1920 else /* more than one switch -- set it like the first switch */
1922 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1927 #if !USE_BOTH_SWITCHGATE_SWITCHES
1928 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1930 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1933 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1935 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1939 case EL_LIGHT_SWITCH_ACTIVE:
1941 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1944 case EL_INVISIBLE_STEELWALL:
1945 case EL_INVISIBLE_WALL:
1946 case EL_INVISIBLE_SAND:
1947 if (game.light_time_left > 0 ||
1948 game.lenses_time_left > 0)
1949 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1952 case EL_EMC_MAGIC_BALL:
1953 if (game.ball_state)
1954 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1957 case EL_EMC_MAGIC_BALL_SWITCH:
1958 if (game.ball_state)
1959 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1962 case EL_TRIGGER_PLAYER:
1963 case EL_TRIGGER_ELEMENT:
1964 case EL_TRIGGER_CE_VALUE:
1965 case EL_TRIGGER_CE_SCORE:
1967 case EL_ANY_ELEMENT:
1968 case EL_CURRENT_CE_VALUE:
1969 case EL_CURRENT_CE_SCORE:
1986 /* reference elements should not be used on the playfield */
1987 Feld[x][y] = EL_EMPTY;
1991 if (IS_CUSTOM_ELEMENT(element))
1993 if (CAN_MOVE(element))
1996 #if USE_NEW_CUSTOM_VALUE
1997 if (!element_info[element].use_last_ce_value || init_game)
1998 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2001 else if (IS_GROUP_ELEMENT(element))
2003 Feld[x][y] = GetElementFromGroupElement(element);
2005 InitField(x, y, init_game);
2012 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2015 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2017 InitField(x, y, init_game);
2019 /* not needed to call InitMovDir() -- already done by InitField()! */
2020 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2021 CAN_MOVE(Feld[x][y]))
2025 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2027 int old_element = Feld[x][y];
2029 InitField(x, y, init_game);
2031 /* not needed to call InitMovDir() -- already done by InitField()! */
2032 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2033 CAN_MOVE(old_element) &&
2034 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2037 /* this case is in fact a combination of not less than three bugs:
2038 first, it calls InitMovDir() for elements that can move, although this is
2039 already done by InitField(); then, it checks the element that was at this
2040 field _before_ the call to InitField() (which can change it); lastly, it
2041 was not called for "mole with direction" elements, which were treated as
2042 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2046 static int get_key_element_from_nr(int key_nr)
2048 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2049 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2050 EL_EM_KEY_1 : EL_KEY_1);
2052 return key_base_element + key_nr;
2055 static int get_next_dropped_element(struct PlayerInfo *player)
2057 return (player->inventory_size > 0 ?
2058 player->inventory_element[player->inventory_size - 1] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left > 0 ?
2062 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2066 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2068 /* pos >= 0: get element from bottom of the stack;
2069 pos < 0: get element from top of the stack */
2073 int min_inventory_size = -pos;
2074 int inventory_pos = player->inventory_size - min_inventory_size;
2075 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2077 return (player->inventory_size >= min_inventory_size ?
2078 player->inventory_element[inventory_pos] :
2079 player->inventory_infinite_element != EL_UNDEFINED ?
2080 player->inventory_infinite_element :
2081 player->dynabombs_left >= min_dynabombs_left ?
2082 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2087 int min_dynabombs_left = pos + 1;
2088 int min_inventory_size = pos + 1 - player->dynabombs_left;
2089 int inventory_pos = pos - player->dynabombs_left;
2091 return (player->inventory_infinite_element != EL_UNDEFINED ?
2092 player->inventory_infinite_element :
2093 player->dynabombs_left >= min_dynabombs_left ?
2094 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2095 player->inventory_size >= min_inventory_size ?
2096 player->inventory_element[inventory_pos] :
2101 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2103 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2104 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2107 if (gpo1->sort_priority != gpo2->sort_priority)
2108 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2110 compare_result = gpo1->nr - gpo2->nr;
2112 return compare_result;
2115 void InitGameControlValues()
2119 for (i = 0; game_panel_controls[i].nr != -1; i++)
2121 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2122 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2123 struct TextPosInfo *pos = gpc->pos;
2125 int type = gpc->type;
2129 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2130 Error(ERR_EXIT, "this should not happen -- please debug");
2133 /* force update of game controls after initialization */
2134 gpc->value = gpc->last_value = -1;
2135 gpc->frame = gpc->last_frame = -1;
2136 gpc->gfx_frame = -1;
2138 /* determine panel value width for later calculation of alignment */
2139 if (type == TYPE_INTEGER || type == TYPE_STRING)
2141 pos->width = pos->size * getFontWidth(pos->font);
2142 pos->height = getFontHeight(pos->font);
2144 else if (type == TYPE_ELEMENT)
2146 pos->width = pos->size;
2147 pos->height = pos->size;
2150 /* fill structure for game panel draw order */
2152 gpo->sort_priority = pos->sort_priority;
2155 /* sort game panel controls according to sort_priority and control number */
2156 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2157 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2160 void UpdatePlayfieldElementCount()
2162 boolean use_element_count = FALSE;
2165 /* first check if it is needed at all to calculate playfield element count */
2166 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2167 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2168 use_element_count = TRUE;
2170 if (!use_element_count)
2173 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2174 element_info[i].element_count = 0;
2176 SCAN_PLAYFIELD(x, y)
2178 element_info[Feld[x][y]].element_count++;
2181 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2182 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2183 if (IS_IN_GROUP(j, i))
2184 element_info[EL_GROUP_START + i].element_count +=
2185 element_info[j].element_count;
2188 void UpdateGameControlValues()
2191 int time = (local_player->LevelSolved ?
2192 local_player->LevelSolved_CountingTime :
2193 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->time :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->time_played :
2197 game.no_time_limit ? TimePlayed : TimeLeft);
2198 int score = (local_player->LevelSolved ?
2199 local_player->LevelSolved_CountingScore :
2200 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201 level.native_em_level->lev->score :
2202 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203 level.native_sp_level->game_sp->score :
2204 local_player->score);
2205 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2206 level.native_em_level->lev->required :
2207 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2208 level.native_sp_level->game_sp->infotrons_still_needed :
2209 local_player->gems_still_needed);
2210 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2211 level.native_em_level->lev->required > 0 :
2212 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2213 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2214 local_player->gems_still_needed > 0 ||
2215 local_player->sokobanfields_still_needed > 0 ||
2216 local_player->lights_still_needed > 0);
2218 UpdatePlayfieldElementCount();
2220 /* update game panel control values */
2222 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2223 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2225 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2226 for (i = 0; i < MAX_NUM_KEYS; i++)
2227 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2228 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2229 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2231 if (game.centered_player_nr == -1)
2233 for (i = 0; i < MAX_PLAYERS; i++)
2235 /* only one player in Supaplex game engine */
2236 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2239 for (k = 0; k < MAX_NUM_KEYS; k++)
2241 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2243 if (level.native_em_level->ply[i]->keys & (1 << k))
2244 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2245 get_key_element_from_nr(k);
2247 else if (stored_player[i].key[k])
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2252 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2253 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2254 level.native_em_level->ply[i]->dynamite;
2255 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 level.native_sp_level->game_sp->red_disk_count;
2259 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2260 stored_player[i].inventory_size;
2262 if (stored_player[i].num_white_keys > 0)
2263 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2266 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2267 stored_player[i].num_white_keys;
2272 int player_nr = game.centered_player_nr;
2274 for (k = 0; k < MAX_NUM_KEYS; k++)
2276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2282 else if (stored_player[player_nr].key[k])
2283 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 get_key_element_from_nr(k);
2287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 level.native_em_level->ply[player_nr]->dynamite;
2290 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 level.native_sp_level->game_sp->red_disk_count;
2294 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 stored_player[player_nr].inventory_size;
2297 if (stored_player[player_nr].num_white_keys > 0)
2298 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2300 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301 stored_player[player_nr].num_white_keys;
2304 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2306 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2307 get_inventory_element_from_pos(local_player, i);
2308 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2309 get_inventory_element_from_pos(local_player, -i - 1);
2312 game_panel_controls[GAME_PANEL_SCORE].value = score;
2313 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2315 game_panel_controls[GAME_PANEL_TIME].value = time;
2317 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2318 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2319 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2321 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2323 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2326 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327 local_player->shield_normal_time_left;
2328 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2331 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332 local_player->shield_deadly_time_left;
2334 game_panel_controls[GAME_PANEL_EXIT].value =
2335 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341 EL_EMC_MAGIC_BALL_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346 game.light_time_left;
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351 game.timegate_time_left;
2353 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2356 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359 game.lenses_time_left;
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364 game.magnify_time_left;
2366 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2368 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2370 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2371 EL_BALLOON_SWITCH_NONE);
2373 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374 local_player->dynabomb_count;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376 local_player->dynabomb_size;
2377 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2380 game_panel_controls[GAME_PANEL_PENGUINS].value =
2381 local_player->friends_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384 local_player->sokobanfields_still_needed;
2385 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386 local_player->sokobanfields_still_needed;
2388 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2391 for (i = 0; i < NUM_BELTS; i++)
2393 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403 game.magic_wall_time_left;
2405 #if USE_PLAYER_GRAVITY
2406 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2407 local_player->gravity;
2409 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2412 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2413 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2415 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2416 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2417 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2418 game.panel.element[i].id : EL_UNDEFINED);
2420 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2421 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2422 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2423 element_info[game.panel.element_count[i].id].element_count : 0);
2425 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2426 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2427 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2428 element_info[game.panel.ce_score[i].id].collect_score : 0);
2430 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2431 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2432 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2433 element_info[game.panel.ce_score_element[i].id].collect_score :
2436 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2437 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2438 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2440 /* update game panel control frames */
2442 for (i = 0; game_panel_controls[i].nr != -1; i++)
2444 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2446 if (gpc->type == TYPE_ELEMENT)
2448 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2450 int last_anim_random_frame = gfx.anim_random_frame;
2451 int element = gpc->value;
2452 int graphic = el2panelimg(element);
2454 if (gpc->value != gpc->last_value)
2457 gpc->gfx_random = INIT_GFX_RANDOM();
2463 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2464 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2465 gpc->gfx_random = INIT_GFX_RANDOM();
2468 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2469 gfx.anim_random_frame = gpc->gfx_random;
2471 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2472 gpc->gfx_frame = element_info[element].collect_score;
2474 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2477 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478 gfx.anim_random_frame = last_anim_random_frame;
2484 void DisplayGameControlValues()
2486 boolean redraw_panel = FALSE;
2489 for (i = 0; game_panel_controls[i].nr != -1; i++)
2491 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2493 if (PANEL_DEACTIVATED(gpc->pos))
2496 if (gpc->value == gpc->last_value &&
2497 gpc->frame == gpc->last_frame)
2500 redraw_panel = TRUE;
2506 /* copy default game door content to main double buffer */
2508 /* !!! CHECK AGAIN !!! */
2509 SetPanelBackground();
2510 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2511 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2513 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2514 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2517 /* redraw game control buttons */
2519 RedrawGameButtons();
2525 game_status = GAME_MODE_PSEUDO_PANEL;
2528 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2530 for (i = 0; game_panel_controls[i].nr != -1; i++)
2534 int nr = game_panel_order[i].nr;
2535 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2537 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540 struct TextPosInfo *pos = gpc->pos;
2541 int type = gpc->type;
2542 int value = gpc->value;
2543 int frame = gpc->frame;
2545 int last_value = gpc->last_value;
2546 int last_frame = gpc->last_frame;
2548 int size = pos->size;
2549 int font = pos->font;
2550 boolean draw_masked = pos->draw_masked;
2551 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2553 if (PANEL_DEACTIVATED(pos))
2557 if (value == last_value && frame == last_frame)
2561 gpc->last_value = value;
2562 gpc->last_frame = frame;
2565 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 if (type == TYPE_INTEGER)
2570 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2571 nr == GAME_PANEL_TIME)
2573 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2575 if (use_dynamic_size) /* use dynamic number of digits */
2577 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2578 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2579 int size2 = size1 + 1;
2580 int font1 = pos->font;
2581 int font2 = pos->font_alt;
2583 size = (value < value_change ? size1 : size2);
2584 font = (value < value_change ? font1 : font2);
2587 /* clear background if value just changed its size (dynamic digits) */
2588 if ((last_value < value_change) != (value < value_change))
2590 int width1 = size1 * getFontWidth(font1);
2591 int width2 = size2 * getFontWidth(font2);
2592 int max_width = MAX(width1, width2);
2593 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2595 pos->width = max_width;
2597 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598 max_width, max_height);
2605 /* correct text size if "digits" is zero or less */
2607 size = strlen(int2str(value, size));
2609 /* dynamically correct text alignment */
2610 pos->width = size * getFontWidth(font);
2613 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2614 int2str(value, size), font, mask_mode);
2616 else if (type == TYPE_ELEMENT)
2618 int element, graphic;
2622 int dst_x = PANEL_XPOS(pos);
2623 int dst_y = PANEL_YPOS(pos);
2626 if (value != EL_UNDEFINED && value != EL_EMPTY)
2629 graphic = el2panelimg(value);
2631 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2638 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641 width = graphic_info[graphic].width * size / TILESIZE;
2642 height = graphic_info[graphic].height * size / TILESIZE;
2646 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2647 dst_x - src_x, dst_y - src_y);
2648 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2653 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2658 if (value == EL_UNDEFINED || value == EL_EMPTY)
2660 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2661 graphic = el2panelimg(element);
2663 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2664 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2665 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2670 graphic = el2panelimg(value);
2672 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675 width = graphic_info[graphic].width * size / TILESIZE;
2676 height = graphic_info[graphic].height * size / TILESIZE;
2678 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 else if (type == TYPE_STRING)
2683 boolean active = (value != 0);
2684 char *state_normal = "off";
2685 char *state_active = "on";
2686 char *state = (active ? state_active : state_normal);
2687 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2688 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2689 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2690 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2692 if (nr == GAME_PANEL_GRAVITY_STATE)
2694 int font1 = pos->font; /* (used for normal state) */
2695 int font2 = pos->font_alt; /* (used for active state) */
2697 int size1 = strlen(state_normal);
2698 int size2 = strlen(state_active);
2699 int width1 = size1 * getFontWidth(font1);
2700 int width2 = size2 * getFontWidth(font2);
2701 int max_width = MAX(width1, width2);
2702 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2704 pos->width = max_width;
2706 /* clear background for values that may have changed its size */
2707 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2708 max_width, max_height);
2711 font = (active ? font2 : font1);
2721 /* don't truncate output if "chars" is zero or less */
2724 /* dynamically correct text alignment */
2725 pos->width = size * getFontWidth(font);
2729 s_cut = getStringCopyN(s, size);
2731 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2732 s_cut, font, mask_mode);
2738 redraw_mask |= REDRAW_DOOR_1;
2741 game_status = GAME_MODE_PLAYING;
2744 void UpdateAndDisplayGameControlValues()
2746 if (tape.warp_forward)
2749 UpdateGameControlValues();
2750 DisplayGameControlValues();
2753 void DrawGameValue_Emeralds(int value)
2755 struct TextPosInfo *pos = &game.panel.gems;
2756 int font_nr = pos->font;
2757 int font_width = getFontWidth(font_nr);
2758 int chars = pos->size;
2761 return; /* !!! USE NEW STUFF !!! */
2764 if (PANEL_DEACTIVATED(pos))
2767 pos->width = chars * font_width;
2769 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2772 void DrawGameValue_Dynamite(int value)
2774 struct TextPosInfo *pos = &game.panel.inventory_count;
2775 int font_nr = pos->font;
2776 int font_width = getFontWidth(font_nr);
2777 int chars = pos->size;
2780 return; /* !!! USE NEW STUFF !!! */
2783 if (PANEL_DEACTIVATED(pos))
2786 pos->width = chars * font_width;
2788 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2791 void DrawGameValue_Score(int value)
2793 struct TextPosInfo *pos = &game.panel.score;
2794 int font_nr = pos->font;
2795 int font_width = getFontWidth(font_nr);
2796 int chars = pos->size;
2799 return; /* !!! USE NEW STUFF !!! */
2802 if (PANEL_DEACTIVATED(pos))
2805 pos->width = chars * font_width;
2807 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2810 void DrawGameValue_Time(int value)
2812 struct TextPosInfo *pos = &game.panel.time;
2813 static int last_value = -1;
2816 int chars = pos->size;
2817 int font1_nr = pos->font;
2818 int font2_nr = pos->font_alt;
2819 int font_nr = font1_nr;
2820 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2823 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2829 if (use_dynamic_chars) /* use dynamic number of chars */
2831 chars = (value < 1000 ? chars1 : chars2);
2832 font_nr = (value < 1000 ? font1_nr : font2_nr);
2835 /* clear background if value just changed its size (dynamic chars only) */
2836 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838 int width1 = chars1 * getFontWidth(font1_nr);
2839 int width2 = chars2 * getFontWidth(font2_nr);
2840 int max_width = MAX(width1, width2);
2841 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843 pos->width = max_width;
2845 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2846 max_width, max_height);
2849 pos->width = chars * getFontWidth(font_nr);
2851 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2856 void DrawGameValue_Level(int value)
2858 struct TextPosInfo *pos = &game.panel.level_number;
2861 int chars = pos->size;
2862 int font1_nr = pos->font;
2863 int font2_nr = pos->font_alt;
2864 int font_nr = font1_nr;
2865 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2868 return; /* !!! USE NEW STUFF !!! */
2871 if (PANEL_DEACTIVATED(pos))
2874 if (use_dynamic_chars) /* use dynamic number of chars */
2876 chars = (level_nr < 100 ? chars1 : chars2);
2877 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2880 pos->width = chars * getFontWidth(font_nr);
2882 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2885 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2890 return; /* !!! USE NEW STUFF !!! */
2893 for (i = 0; i < MAX_NUM_KEYS; i++)
2895 struct TextPosInfo *pos = &game.panel.key[i];
2896 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2897 int src_y = DOOR_GFX_PAGEY1 + 123;
2898 int dst_x = PANEL_XPOS(pos);
2899 int dst_y = PANEL_YPOS(pos);
2901 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2902 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2904 int graphic = el2edimg(element);
2906 if (PANEL_DEACTIVATED(pos))
2910 /* masked blit with tiles from half-size scaled bitmap does not work yet
2911 (no mask bitmap created for these sizes after loading and scaling) --
2912 solution: load without creating mask, scale, then create final mask */
2914 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2915 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2922 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2924 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2925 dst_x - src_x, dst_y - src_y);
2926 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2931 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2933 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2934 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2939 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2942 int key[MAX_NUM_KEYS];
2945 /* prevent EM engine from updating time/score values parallel to GameWon() */
2946 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2947 local_player->LevelSolved)
2950 for (i = 0; i < MAX_NUM_KEYS; i++)
2951 key[i] = key_bits & (1 << i);
2953 DrawGameValue_Level(level_nr);
2955 DrawGameValue_Emeralds(emeralds);
2956 DrawGameValue_Dynamite(dynamite);
2957 DrawGameValue_Score(score);
2958 DrawGameValue_Time(time);
2960 DrawGameValue_Keys(key);
2963 void UpdateGameDoorValues()
2965 UpdateGameControlValues();
2968 void DrawGameDoorValues()
2970 DisplayGameControlValues();
2973 void DrawGameDoorValues_OLD()
2975 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2976 int dynamite_value = 0;
2977 int score_value = (local_player->LevelSolved ? local_player->score_final :
2978 local_player->score);
2979 int gems_value = local_player->gems_still_needed;
2983 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2985 DrawGameDoorValues_EM();
2990 if (game.centered_player_nr == -1)
2992 for (i = 0; i < MAX_PLAYERS; i++)
2994 for (j = 0; j < MAX_NUM_KEYS; j++)
2995 if (stored_player[i].key[j])
2996 key_bits |= (1 << j);
2998 dynamite_value += stored_player[i].inventory_size;
3003 int player_nr = game.centered_player_nr;
3005 for (i = 0; i < MAX_NUM_KEYS; i++)
3006 if (stored_player[player_nr].key[i])
3007 key_bits |= (1 << i);
3009 dynamite_value = stored_player[player_nr].inventory_size;
3012 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3018 =============================================================================
3020 -----------------------------------------------------------------------------
3021 initialize game engine due to level / tape version number
3022 =============================================================================
3025 static void InitGameEngine()
3027 int i, j, k, l, x, y;
3029 /* set game engine from tape file when re-playing, else from level file */
3030 game.engine_version = (tape.playing ? tape.engine_version :
3031 level.game_version);
3033 /* set single or multi-player game mode (needed for re-playing tapes) */
3034 game.team_mode = setup.team_mode;
3038 int num_players = 0;
3040 for (i = 0; i < MAX_PLAYERS; i++)
3041 if (tape.player_participates[i])
3044 /* multi-player tapes contain input data for more than one player */
3045 game.team_mode = (num_players > 1);
3048 /* ---------------------------------------------------------------------- */
3049 /* set flags for bugs and changes according to active game engine version */
3050 /* ---------------------------------------------------------------------- */
3053 Summary of bugfix/change:
3054 Fixed handling for custom elements that change when pushed by the player.
3056 Fixed/changed in version:
3060 Before 3.1.0, custom elements that "change when pushing" changed directly
3061 after the player started pushing them (until then handled in "DigField()").
3062 Since 3.1.0, these custom elements are not changed until the "pushing"
3063 move of the element is finished (now handled in "ContinueMoving()").
3065 Affected levels/tapes:
3066 The first condition is generally needed for all levels/tapes before version
3067 3.1.0, which might use the old behaviour before it was changed; known tapes
3068 that are affected are some tapes from the level set "Walpurgis Gardens" by
3070 The second condition is an exception from the above case and is needed for
3071 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3072 above (including some development versions of 3.1.0), but before it was
3073 known that this change would break tapes like the above and was fixed in
3074 3.1.1, so that the changed behaviour was active although the engine version
3075 while recording maybe was before 3.1.0. There is at least one tape that is
3076 affected by this exception, which is the tape for the one-level set "Bug
3077 Machine" by Juergen Bonhagen.
3080 game.use_change_when_pushing_bug =
3081 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3083 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3084 tape.game_version < VERSION_IDENT(3,1,1,0)));
3087 Summary of bugfix/change:
3088 Fixed handling for blocking the field the player leaves when moving.
3090 Fixed/changed in version:
3094 Before 3.1.1, when "block last field when moving" was enabled, the field
3095 the player is leaving when moving was blocked for the time of the move,
3096 and was directly unblocked afterwards. This resulted in the last field
3097 being blocked for exactly one less than the number of frames of one player
3098 move. Additionally, even when blocking was disabled, the last field was
3099 blocked for exactly one frame.
3100 Since 3.1.1, due to changes in player movement handling, the last field
3101 is not blocked at all when blocking is disabled. When blocking is enabled,
3102 the last field is blocked for exactly the number of frames of one player
3103 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3104 last field is blocked for exactly one more than the number of frames of
3107 Affected levels/tapes:
3108 (!!! yet to be determined -- probably many !!!)
3111 game.use_block_last_field_bug =
3112 (game.engine_version < VERSION_IDENT(3,1,1,0));
3115 Summary of bugfix/change:
3116 Changed behaviour of CE changes with multiple changes per single frame.
3118 Fixed/changed in version:
3122 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3123 This resulted in race conditions where CEs seem to behave strange in some
3124 situations (where triggered CE changes were just skipped because there was
3125 already a CE change on that tile in the playfield in that engine frame).
3126 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3127 (The number of changes per frame must be limited in any case, because else
3128 it is easily possible to define CE changes that would result in an infinite
3129 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3130 should be set large enough so that it would only be reached in cases where
3131 the corresponding CE change conditions run into a loop. Therefore, it seems
3132 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3133 maximal number of change pages for custom elements.)
3135 Affected levels/tapes:
3139 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3140 game.max_num_changes_per_frame = 1;
3142 game.max_num_changes_per_frame =
3143 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3146 /* ---------------------------------------------------------------------- */
3148 /* default scan direction: scan playfield from top/left to bottom/right */
3149 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3151 /* dynamically adjust element properties according to game engine version */
3152 InitElementPropertiesEngine(game.engine_version);
3155 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3156 printf(" tape version == %06d [%s] [file: %06d]\n",
3157 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3159 printf(" => game.engine_version == %06d\n", game.engine_version);
3162 /* ---------- initialize player's initial move delay --------------------- */
3164 /* dynamically adjust player properties according to level information */
3165 for (i = 0; i < MAX_PLAYERS; i++)
3166 game.initial_move_delay_value[i] =
3167 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3169 /* dynamically adjust player properties according to game engine version */
3170 for (i = 0; i < MAX_PLAYERS; i++)
3171 game.initial_move_delay[i] =
3172 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3173 game.initial_move_delay_value[i] : 0);
3175 /* ---------- initialize player's initial push delay --------------------- */
3177 /* dynamically adjust player properties according to game engine version */
3178 game.initial_push_delay_value =
3179 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3181 /* ---------- initialize changing elements ------------------------------- */
3183 /* initialize changing elements information */
3184 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3186 struct ElementInfo *ei = &element_info[i];
3188 /* this pointer might have been changed in the level editor */
3189 ei->change = &ei->change_page[0];
3191 if (!IS_CUSTOM_ELEMENT(i))
3193 ei->change->target_element = EL_EMPTY_SPACE;
3194 ei->change->delay_fixed = 0;
3195 ei->change->delay_random = 0;
3196 ei->change->delay_frames = 1;
3199 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3201 ei->has_change_event[j] = FALSE;
3203 ei->event_page_nr[j] = 0;
3204 ei->event_page[j] = &ei->change_page[0];
3208 /* add changing elements from pre-defined list */
3209 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3211 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3212 struct ElementInfo *ei = &element_info[ch_delay->element];
3214 ei->change->target_element = ch_delay->target_element;
3215 ei->change->delay_fixed = ch_delay->change_delay;
3217 ei->change->pre_change_function = ch_delay->pre_change_function;
3218 ei->change->change_function = ch_delay->change_function;
3219 ei->change->post_change_function = ch_delay->post_change_function;
3221 ei->change->can_change = TRUE;
3222 ei->change->can_change_or_has_action = TRUE;
3224 ei->has_change_event[CE_DELAY] = TRUE;
3226 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3227 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3230 /* ---------- initialize internal run-time variables --------------------- */
3232 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3234 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3236 for (j = 0; j < ei->num_change_pages; j++)
3238 ei->change_page[j].can_change_or_has_action =
3239 (ei->change_page[j].can_change |
3240 ei->change_page[j].has_action);
3244 /* add change events from custom element configuration */
3245 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3247 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3249 for (j = 0; j < ei->num_change_pages; j++)
3251 if (!ei->change_page[j].can_change_or_has_action)
3254 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3256 /* only add event page for the first page found with this event */
3257 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3259 ei->has_change_event[k] = TRUE;
3261 ei->event_page_nr[k] = j;
3262 ei->event_page[k] = &ei->change_page[j];
3269 /* ---------- initialize reference elements in change conditions --------- */
3271 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3273 int element = EL_CUSTOM_START + i;
3274 struct ElementInfo *ei = &element_info[element];
3276 for (j = 0; j < ei->num_change_pages; j++)
3278 int trigger_element = ei->change_page[j].initial_trigger_element;
3280 if (trigger_element >= EL_PREV_CE_8 &&
3281 trigger_element <= EL_NEXT_CE_8)
3282 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3284 ei->change_page[j].trigger_element = trigger_element;
3289 /* ---------- initialize run-time trigger player and element ------------- */
3291 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3293 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3295 for (j = 0; j < ei->num_change_pages; j++)
3297 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3298 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3299 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3300 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3301 ei->change_page[j].actual_trigger_ce_value = 0;
3302 ei->change_page[j].actual_trigger_ce_score = 0;
3306 /* ---------- initialize trigger events ---------------------------------- */
3308 /* initialize trigger events information */
3309 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3310 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3311 trigger_events[i][j] = FALSE;
3313 /* add trigger events from element change event properties */
3314 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316 struct ElementInfo *ei = &element_info[i];
3318 for (j = 0; j < ei->num_change_pages; j++)
3320 if (!ei->change_page[j].can_change_or_has_action)
3323 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3325 int trigger_element = ei->change_page[j].trigger_element;
3327 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3329 if (ei->change_page[j].has_event[k])
3331 if (IS_GROUP_ELEMENT(trigger_element))
3333 struct ElementGroupInfo *group =
3334 element_info[trigger_element].group;
3336 for (l = 0; l < group->num_elements_resolved; l++)
3337 trigger_events[group->element_resolved[l]][k] = TRUE;
3339 else if (trigger_element == EL_ANY_ELEMENT)
3340 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3341 trigger_events[l][k] = TRUE;
3343 trigger_events[trigger_element][k] = TRUE;
3350 /* ---------- initialize push delay -------------------------------------- */
3352 /* initialize push delay values to default */
3353 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3355 if (!IS_CUSTOM_ELEMENT(i))
3357 /* set default push delay values (corrected since version 3.0.7-1) */
3358 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3360 element_info[i].push_delay_fixed = 2;
3361 element_info[i].push_delay_random = 8;
3365 element_info[i].push_delay_fixed = 8;
3366 element_info[i].push_delay_random = 8;
3371 /* set push delay value for certain elements from pre-defined list */
3372 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3374 int e = push_delay_list[i].element;
3376 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3377 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3380 /* set push delay value for Supaplex elements for newer engine versions */
3381 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3383 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3385 if (IS_SP_ELEMENT(i))
3387 /* set SP push delay to just enough to push under a falling zonk */
3388 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3390 element_info[i].push_delay_fixed = delay;
3391 element_info[i].push_delay_random = 0;
3396 /* ---------- initialize move stepsize ----------------------------------- */
3398 /* initialize move stepsize values to default */
3399 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400 if (!IS_CUSTOM_ELEMENT(i))
3401 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3403 /* set move stepsize value for certain elements from pre-defined list */
3404 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3406 int e = move_stepsize_list[i].element;
3408 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3411 /* ---------- initialize collect score ----------------------------------- */
3413 /* initialize collect score values for custom elements from initial value */
3414 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3415 if (IS_CUSTOM_ELEMENT(i))
3416 element_info[i].collect_score = element_info[i].collect_score_initial;
3418 /* ---------- initialize collect count ----------------------------------- */
3420 /* initialize collect count values for non-custom elements */
3421 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3422 if (!IS_CUSTOM_ELEMENT(i))
3423 element_info[i].collect_count_initial = 0;
3425 /* add collect count values for all elements from pre-defined list */
3426 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3427 element_info[collect_count_list[i].element].collect_count_initial =
3428 collect_count_list[i].count;
3430 /* ---------- initialize access direction -------------------------------- */
3432 /* initialize access direction values to default (access from every side) */
3433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3434 if (!IS_CUSTOM_ELEMENT(i))
3435 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3437 /* set access direction value for certain elements from pre-defined list */
3438 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3439 element_info[access_direction_list[i].element].access_direction =
3440 access_direction_list[i].direction;
3442 /* ---------- initialize explosion content ------------------------------- */
3443 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445 if (IS_CUSTOM_ELEMENT(i))
3448 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3450 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3452 element_info[i].content.e[x][y] =
3453 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3454 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3455 i == EL_PLAYER_3 ? EL_EMERALD :
3456 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3457 i == EL_MOLE ? EL_EMERALD_RED :
3458 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3459 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3460 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3461 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3462 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3463 i == EL_WALL_EMERALD ? EL_EMERALD :
3464 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3465 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3466 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3467 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3468 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3469 i == EL_WALL_PEARL ? EL_PEARL :
3470 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3475 /* ---------- initialize recursion detection ------------------------------ */
3476 recursion_loop_depth = 0;
3477 recursion_loop_detected = FALSE;
3478 recursion_loop_element = EL_UNDEFINED;
3480 /* ---------- initialize graphics engine ---------------------------------- */
3481 game.scroll_delay_value =
3482 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3483 setup.scroll_delay ? setup.scroll_delay_value : 0);
3484 game.scroll_delay_value =
3485 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3488 int get_num_special_action(int element, int action_first, int action_last)
3490 int num_special_action = 0;
3493 for (i = action_first; i <= action_last; i++)
3495 boolean found = FALSE;
3497 for (j = 0; j < NUM_DIRECTIONS; j++)
3498 if (el_act_dir2img(element, i, j) !=
3499 el_act_dir2img(element, ACTION_DEFAULT, j))
3503 num_special_action++;
3508 return num_special_action;
3513 =============================================================================
3515 -----------------------------------------------------------------------------
3516 initialize and start new game
3517 =============================================================================
3522 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3523 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3524 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3526 boolean do_fading = (game_status == GAME_MODE_MAIN);
3529 int initial_move_dir = MV_DOWN;
3531 int initial_move_dir = MV_NONE;
3535 game_status = GAME_MODE_PLAYING;
3538 /* needed if different viewport properties defined for playing */
3539 ChangeViewportPropertiesIfNeeded();
3543 DrawCompleteVideoDisplay();
3547 InitGameControlValues();
3549 /* don't play tapes over network */
3550 network_playing = (options.network && !tape.playing);
3552 for (i = 0; i < MAX_PLAYERS; i++)
3554 struct PlayerInfo *player = &stored_player[i];
3556 player->index_nr = i;
3557 player->index_bit = (1 << i);
3558 player->element_nr = EL_PLAYER_1 + i;
3560 player->present = FALSE;
3561 player->active = FALSE;
3562 player->mapped = FALSE;
3564 player->killed = FALSE;
3565 player->reanimated = FALSE;
3568 player->effective_action = 0;
3569 player->programmed_action = 0;
3572 player->score_final = 0;
3574 player->gems_still_needed = level.gems_needed;
3575 player->sokobanfields_still_needed = 0;
3576 player->lights_still_needed = 0;
3577 player->friends_still_needed = 0;
3579 for (j = 0; j < MAX_NUM_KEYS; j++)
3580 player->key[j] = FALSE;
3582 player->num_white_keys = 0;
3584 player->dynabomb_count = 0;
3585 player->dynabomb_size = 1;
3586 player->dynabombs_left = 0;
3587 player->dynabomb_xl = FALSE;
3589 player->MovDir = initial_move_dir;
3592 player->GfxDir = initial_move_dir;
3593 player->GfxAction = ACTION_DEFAULT;
3595 player->StepFrame = 0;
3597 player->initial_element = player->element_nr;
3598 player->artwork_element =
3599 (level.use_artwork_element[i] ? level.artwork_element[i] :
3600 player->element_nr);
3601 player->use_murphy = FALSE;
3603 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3604 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3606 player->gravity = level.initial_player_gravity[i];
3608 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3610 player->actual_frame_counter = 0;
3612 player->step_counter = 0;
3614 player->last_move_dir = initial_move_dir;
3616 player->is_active = FALSE;
3618 player->is_waiting = FALSE;
3619 player->is_moving = FALSE;
3620 player->is_auto_moving = FALSE;
3621 player->is_digging = FALSE;
3622 player->is_snapping = FALSE;
3623 player->is_collecting = FALSE;
3624 player->is_pushing = FALSE;
3625 player->is_switching = FALSE;
3626 player->is_dropping = FALSE;
3627 player->is_dropping_pressed = FALSE;
3629 player->is_bored = FALSE;
3630 player->is_sleeping = FALSE;
3632 player->frame_counter_bored = -1;
3633 player->frame_counter_sleeping = -1;
3635 player->anim_delay_counter = 0;
3636 player->post_delay_counter = 0;
3638 player->dir_waiting = initial_move_dir;
3639 player->action_waiting = ACTION_DEFAULT;
3640 player->last_action_waiting = ACTION_DEFAULT;
3641 player->special_action_bored = ACTION_DEFAULT;
3642 player->special_action_sleeping = ACTION_DEFAULT;
3644 player->switch_x = -1;
3645 player->switch_y = -1;
3647 player->drop_x = -1;
3648 player->drop_y = -1;
3650 player->show_envelope = 0;
3652 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3654 player->push_delay = -1; /* initialized when pushing starts */
3655 player->push_delay_value = game.initial_push_delay_value;
3657 player->drop_delay = 0;
3658 player->drop_pressed_delay = 0;
3660 player->last_jx = -1;
3661 player->last_jy = -1;
3665 player->shield_normal_time_left = 0;
3666 player->shield_deadly_time_left = 0;
3668 player->inventory_infinite_element = EL_UNDEFINED;
3669 player->inventory_size = 0;
3671 if (level.use_initial_inventory[i])
3673 for (j = 0; j < level.initial_inventory_size[i]; j++)
3675 int element = level.initial_inventory_content[i][j];
3676 int collect_count = element_info[element].collect_count_initial;
3679 if (!IS_CUSTOM_ELEMENT(element))
3682 if (collect_count == 0)
3683 player->inventory_infinite_element = element;
3685 for (k = 0; k < collect_count; k++)
3686 if (player->inventory_size < MAX_INVENTORY_SIZE)
3687 player->inventory_element[player->inventory_size++] = element;
3691 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3692 SnapField(player, 0, 0);
3694 player->LevelSolved = FALSE;
3695 player->GameOver = FALSE;
3697 player->LevelSolved_GameWon = FALSE;
3698 player->LevelSolved_GameEnd = FALSE;
3699 player->LevelSolved_PanelOff = FALSE;
3700 player->LevelSolved_SaveTape = FALSE;
3701 player->LevelSolved_SaveScore = FALSE;
3702 player->LevelSolved_CountingTime = 0;
3703 player->LevelSolved_CountingScore = 0;
3705 map_player_action[i] = i;
3708 network_player_action_received = FALSE;
3710 #if defined(NETWORK_AVALIABLE)
3711 /* initial null action */
3712 if (network_playing)
3713 SendToServer_MovePlayer(MV_NONE);
3722 TimeLeft = level.time;
3725 ScreenMovDir = MV_NONE;
3729 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3731 AllPlayersGone = FALSE;
3733 game.no_time_limit = (level.time == 0);
3735 game.yamyam_content_nr = 0;
3736 game.robot_wheel_active = FALSE;
3737 game.magic_wall_active = FALSE;
3738 game.magic_wall_time_left = 0;
3739 game.light_time_left = 0;
3740 game.timegate_time_left = 0;
3741 game.switchgate_pos = 0;
3742 game.wind_direction = level.wind_direction_initial;
3744 #if !USE_PLAYER_GRAVITY
3745 game.gravity = FALSE;
3746 game.explosions_delayed = TRUE;
3749 game.lenses_time_left = 0;
3750 game.magnify_time_left = 0;
3752 game.ball_state = level.ball_state_initial;
3753 game.ball_content_nr = 0;
3755 game.envelope_active = FALSE;
3757 /* set focus to local player for network games, else to all players */
3758 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3759 game.centered_player_nr_next = game.centered_player_nr;
3760 game.set_centered_player = FALSE;
3762 if (network_playing && tape.recording)
3764 /* store client dependent player focus when recording network games */
3765 tape.centered_player_nr_next = game.centered_player_nr_next;
3766 tape.set_centered_player = TRUE;
3769 for (i = 0; i < NUM_BELTS; i++)
3771 game.belt_dir[i] = MV_NONE;
3772 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3775 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3776 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3778 #if DEBUG_INIT_PLAYER
3781 printf("Player status at level initialization:\n");
3785 SCAN_PLAYFIELD(x, y)
3787 Feld[x][y] = level.field[x][y];
3788 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3789 ChangeDelay[x][y] = 0;
3790 ChangePage[x][y] = -1;
3791 #if USE_NEW_CUSTOM_VALUE
3792 CustomValue[x][y] = 0; /* initialized in InitField() */
3794 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3796 WasJustMoving[x][y] = 0;
3797 WasJustFalling[x][y] = 0;
3798 CheckCollision[x][y] = 0;
3799 CheckImpact[x][y] = 0;
3801 Pushed[x][y] = FALSE;
3803 ChangeCount[x][y] = 0;
3804 ChangeEvent[x][y] = -1;
3806 ExplodePhase[x][y] = 0;
3807 ExplodeDelay[x][y] = 0;
3808 ExplodeField[x][y] = EX_TYPE_NONE;
3810 RunnerVisit[x][y] = 0;
3811 PlayerVisit[x][y] = 0;
3814 GfxRandom[x][y] = INIT_GFX_RANDOM();
3815 GfxElement[x][y] = EL_UNDEFINED;
3816 GfxAction[x][y] = ACTION_DEFAULT;
3817 GfxDir[x][y] = MV_NONE;
3818 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3821 SCAN_PLAYFIELD(x, y)
3823 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3825 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3827 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3830 InitField(x, y, TRUE);
3832 ResetGfxAnimation(x, y);
3837 for (i = 0; i < MAX_PLAYERS; i++)
3839 struct PlayerInfo *player = &stored_player[i];
3841 /* set number of special actions for bored and sleeping animation */
3842 player->num_special_action_bored =
3843 get_num_special_action(player->artwork_element,
3844 ACTION_BORING_1, ACTION_BORING_LAST);
3845 player->num_special_action_sleeping =
3846 get_num_special_action(player->artwork_element,
3847 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3850 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3851 emulate_sb ? EMU_SOKOBAN :
3852 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3854 #if USE_NEW_ALL_SLIPPERY
3855 /* initialize type of slippery elements */
3856 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3858 if (!IS_CUSTOM_ELEMENT(i))
3860 /* default: elements slip down either to the left or right randomly */
3861 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3863 /* SP style elements prefer to slip down on the left side */
3864 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3865 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3867 /* BD style elements prefer to slip down on the left side */
3868 if (game.emulation == EMU_BOULDERDASH)
3869 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3874 /* initialize explosion and ignition delay */
3875 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3877 if (!IS_CUSTOM_ELEMENT(i))
3880 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3881 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3882 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3883 int last_phase = (num_phase + 1) * delay;
3884 int half_phase = (num_phase / 2) * delay;
3886 element_info[i].explosion_delay = last_phase - 1;
3887 element_info[i].ignition_delay = half_phase;
3889 if (i == EL_BLACK_ORB)
3890 element_info[i].ignition_delay = 1;
3894 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3895 element_info[i].explosion_delay = 1;
3897 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3898 element_info[i].ignition_delay = 1;
3902 /* correct non-moving belts to start moving left */
3903 for (i = 0; i < NUM_BELTS; i++)
3904 if (game.belt_dir[i] == MV_NONE)
3905 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3907 #if USE_NEW_PLAYER_ASSIGNMENTS
3908 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3909 /* choose default local player */
3910 local_player = &stored_player[0];
3912 for (i = 0; i < MAX_PLAYERS; i++)
3913 stored_player[i].connected = FALSE;
3915 local_player->connected = TRUE;
3916 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3919 printf("::: TEAM MODE: %d\n", game.team_mode);
3925 for (i = 0; i < MAX_PLAYERS; i++)
3926 stored_player[i].connected = tape.player_participates[i];
3928 /* try to guess locally connected team mode players (needed for correct
3929 assignment of player figures from level to locally playing players) */
3931 for (i = 0; i < MAX_PLAYERS; i++)
3932 if (tape.player_participates[i])
3933 stored_player[i].connected = TRUE;
3936 else if (game.team_mode && !options.network)
3938 /* try to guess locally connected team mode players (needed for correct
3939 assignment of player figures from level to locally playing players) */
3941 for (i = 0; i < MAX_PLAYERS; i++)
3942 if (setup.input[i].use_joystick ||
3943 setup.input[i].key.left != KSYM_UNDEFINED)
3944 stored_player[i].connected = TRUE;
3947 #if DEBUG_INIT_PLAYER
3950 printf("Player status after level initialization:\n");
3952 for (i = 0; i < MAX_PLAYERS; i++)
3954 struct PlayerInfo *player = &stored_player[i];
3956 printf("- player %d: present == %d, connected == %d, active == %d",
3962 if (local_player == player)
3963 printf(" (local player)");
3970 #if DEBUG_INIT_PLAYER
3972 printf("Reassigning players ...\n");
3975 /* check if any connected player was not found in playfield */
3976 for (i = 0; i < MAX_PLAYERS; i++)
3978 struct PlayerInfo *player = &stored_player[i];
3980 if (player->connected && !player->present)
3982 struct PlayerInfo *field_player = NULL;
3984 #if DEBUG_INIT_PLAYER
3986 printf("- looking for field player for player %d ...\n", i + 1);
3989 /* assign first free player found that is present in the playfield */
3992 /* first try: look for unmapped playfield player that is not connected */
3993 for (j = 0; j < MAX_PLAYERS; j++)
3994 if (field_player == NULL &&
3995 stored_player[j].present &&
3996 !stored_player[j].mapped &&
3997 !stored_player[j].connected)
3998 field_player = &stored_player[j];
4000 /* second try: look for *any* unmapped playfield player */
4001 for (j = 0; j < MAX_PLAYERS; j++)
4002 if (field_player == NULL &&
4003 stored_player[j].present &&
4004 !stored_player[j].mapped)
4005 field_player = &stored_player[j];
4007 /* first try: look for unmapped playfield player that is not connected */
4008 if (field_player == NULL)
4009 for (j = 0; j < MAX_PLAYERS; j++)
4010 if (stored_player[j].present &&
4011 !stored_player[j].mapped &&
4012 !stored_player[j].connected)
4013 field_player = &stored_player[j];
4015 /* second try: look for *any* unmapped playfield player */
4016 if (field_player == NULL)
4017 for (j = 0; j < MAX_PLAYERS; j++)
4018 if (stored_player[j].present &&
4019 !stored_player[j].mapped)
4020 field_player = &stored_player[j];
4023 if (field_player != NULL)
4025 int jx = field_player->jx, jy = field_player->jy;
4027 #if DEBUG_INIT_PLAYER
4029 printf("- found player %d\n", field_player->index_nr + 1);
4032 player->present = FALSE;
4033 player->active = FALSE;
4035 field_player->present = TRUE;
4036 field_player->active = TRUE;
4039 player->initial_element = field_player->initial_element;
4040 player->artwork_element = field_player->artwork_element;
4042 player->block_last_field = field_player->block_last_field;
4043 player->block_delay_adjustment = field_player->block_delay_adjustment;
4046 StorePlayer[jx][jy] = field_player->element_nr;
4048 field_player->jx = field_player->last_jx = jx;
4049 field_player->jy = field_player->last_jy = jy;
4051 if (local_player == player)
4052 local_player = field_player;
4054 map_player_action[field_player->index_nr] = i;
4056 field_player->mapped = TRUE;
4058 #if DEBUG_INIT_PLAYER
4060 printf("- map_player_action[%d] == %d\n",
4061 field_player->index_nr + 1, i + 1);
4066 if (player->connected && player->present)
4067 player->mapped = TRUE;
4070 #if DEBUG_INIT_PLAYER
4073 printf("Player status after player assignment (first stage):\n");
4075 for (i = 0; i < MAX_PLAYERS; i++)
4077 struct PlayerInfo *player = &stored_player[i];
4079 printf("- player %d: present == %d, connected == %d, active == %d",
4085 if (local_player == player)
4086 printf(" (local player)");
4095 /* check if any connected player was not found in playfield */
4096 for (i = 0; i < MAX_PLAYERS; i++)
4098 struct PlayerInfo *player = &stored_player[i];
4100 if (player->connected && !player->present)
4102 for (j = 0; j < MAX_PLAYERS; j++)
4104 struct PlayerInfo *field_player = &stored_player[j];
4105 int jx = field_player->jx, jy = field_player->jy;
4107 /* assign first free player found that is present in the playfield */
4108 if (field_player->present && !field_player->connected)
4110 player->present = TRUE;
4111 player->active = TRUE;
4113 field_player->present = FALSE;
4114 field_player->active = FALSE;
4116 player->initial_element = field_player->initial_element;
4117 player->artwork_element = field_player->artwork_element;
4119 player->block_last_field = field_player->block_last_field;
4120 player->block_delay_adjustment = field_player->block_delay_adjustment;
4122 StorePlayer[jx][jy] = player->element_nr;
4124 player->jx = player->last_jx = jx;
4125 player->jy = player->last_jy = jy;
4135 printf("::: local_player->present == %d\n", local_player->present);
4140 /* when playing a tape, eliminate all players who do not participate */
4142 #if USE_NEW_PLAYER_ASSIGNMENTS
4145 if (!game.team_mode)
4148 for (i = 0; i < MAX_PLAYERS; i++)
4150 if (stored_player[i].active &&
4151 !tape.player_participates[map_player_action[i]])
4153 struct PlayerInfo *player = &stored_player[i];
4154 int jx = player->jx, jy = player->jy;
4156 #if DEBUG_INIT_PLAYER
4158 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4161 player->active = FALSE;
4162 StorePlayer[jx][jy] = 0;
4163 Feld[jx][jy] = EL_EMPTY;
4169 for (i = 0; i < MAX_PLAYERS; i++)
4171 if (stored_player[i].active &&
4172 !tape.player_participates[i])
4174 struct PlayerInfo *player = &stored_player[i];
4175 int jx = player->jx, jy = player->jy;
4177 player->active = FALSE;
4178 StorePlayer[jx][jy] = 0;
4179 Feld[jx][jy] = EL_EMPTY;
4184 else if (!options.network && !game.team_mode) /* && !tape.playing */
4186 /* when in single player mode, eliminate all but the first active player */
4188 for (i = 0; i < MAX_PLAYERS; i++)
4190 if (stored_player[i].active)
4192 for (j = i + 1; j < MAX_PLAYERS; j++)
4194 if (stored_player[j].active)
4196 struct PlayerInfo *player = &stored_player[j];
4197 int jx = player->jx, jy = player->jy;
4199 player->active = FALSE;
4200 player->present = FALSE;
4202 StorePlayer[jx][jy] = 0;
4203 Feld[jx][jy] = EL_EMPTY;
4210 /* when recording the game, store which players take part in the game */
4213 #if USE_NEW_PLAYER_ASSIGNMENTS
4214 for (i = 0; i < MAX_PLAYERS; i++)
4215 if (stored_player[i].connected)
4216 tape.player_participates[i] = TRUE;
4218 for (i = 0; i < MAX_PLAYERS; i++)
4219 if (stored_player[i].active)
4220 tape.player_participates[i] = TRUE;
4224 #if DEBUG_INIT_PLAYER
4227 printf("Player status after player assignment (final stage):\n");
4229 for (i = 0; i < MAX_PLAYERS; i++)
4231 struct PlayerInfo *player = &stored_player[i];
4233 printf("- player %d: present == %d, connected == %d, active == %d",
4239 if (local_player == player)
4240 printf(" (local player)");
4247 if (BorderElement == EL_EMPTY)
4250 SBX_Right = lev_fieldx - SCR_FIELDX;
4252 SBY_Lower = lev_fieldy - SCR_FIELDY;
4257 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4259 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4264 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4265 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4267 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4268 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4270 if (EVEN(SCR_FIELDX))
4272 if (EVEN(SCR_FIELDY))
4277 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4278 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4280 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4281 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4284 /* if local player not found, look for custom element that might create
4285 the player (make some assumptions about the right custom element) */
4286 if (!local_player->present)
4288 int start_x = 0, start_y = 0;
4289 int found_rating = 0;
4290 int found_element = EL_UNDEFINED;
4291 int player_nr = local_player->index_nr;
4293 SCAN_PLAYFIELD(x, y)
4295 int element = Feld[x][y];
4300 if (level.use_start_element[player_nr] &&
4301 level.start_element[player_nr] == element &&
4308 found_element = element;
4311 if (!IS_CUSTOM_ELEMENT(element))
4314 if (CAN_CHANGE(element))
4316 for (i = 0; i < element_info[element].num_change_pages; i++)
4318 /* check for player created from custom element as single target */
4319 content = element_info[element].change_page[i].target_element;
4320 is_player = ELEM_IS_PLAYER(content);
4322 if (is_player && (found_rating < 3 ||
4323 (found_rating == 3 && element < found_element)))
4329 found_element = element;
4334 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4336 /* check for player created from custom element as explosion content */
4337 content = element_info[element].content.e[xx][yy];
4338 is_player = ELEM_IS_PLAYER(content);
4340 if (is_player && (found_rating < 2 ||
4341 (found_rating == 2 && element < found_element)))
4343 start_x = x + xx - 1;
4344 start_y = y + yy - 1;
4347 found_element = element;
4350 if (!CAN_CHANGE(element))
4353 for (i = 0; i < element_info[element].num_change_pages; i++)
4355 /* check for player created from custom element as extended target */
4357 element_info[element].change_page[i].target_content.e[xx][yy];
4359 is_player = ELEM_IS_PLAYER(content);
4361 if (is_player && (found_rating < 1 ||
4362 (found_rating == 1 && element < found_element)))
4364 start_x = x + xx - 1;
4365 start_y = y + yy - 1;
4368 found_element = element;
4374 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4375 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4378 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4379 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4384 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4385 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4386 local_player->jx - MIDPOSX);
4388 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4389 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4390 local_player->jy - MIDPOSY);
4394 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4398 /* do not use PLAYING mask for fading out from main screen */
4399 game_status = GAME_MODE_MAIN;
4404 if (!game.restart_level)
4405 CloseDoor(DOOR_CLOSE_1);
4408 if (level_editor_test_game)
4409 FadeSkipNextFadeIn();
4411 FadeSetEnterScreen();
4413 if (level_editor_test_game)
4414 fading = fading_none;
4416 fading = menu.destination;
4420 FadeOut(REDRAW_FIELD);
4423 FadeOut(REDRAW_FIELD);
4427 game_status = GAME_MODE_PLAYING;
4430 /* !!! FIX THIS (START) !!! */
4431 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4433 InitGameEngine_EM();
4435 /* blit playfield from scroll buffer to normal back buffer for fading in */
4436 BlitScreenToBitmap_EM(backbuffer);
4438 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4440 InitGameEngine_SP();
4442 /* blit playfield from scroll buffer to normal back buffer for fading in */
4443 BlitScreenToBitmap_SP(backbuffer);
4450 /* after drawing the level, correct some elements */
4451 if (game.timegate_time_left == 0)
4452 CloseAllOpenTimegates();
4455 BlitScreenToBitmap(backbuffer);
4457 /* blit playfield from scroll buffer to normal back buffer for fading in */
4458 if (setup.soft_scrolling)
4459 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4462 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4464 /* !!! FIX THIS (END) !!! */
4467 FadeIn(REDRAW_FIELD);
4470 FadeIn(REDRAW_FIELD);
4475 if (!game.restart_level)
4477 /* copy default game door content to main double buffer */
4480 /* !!! CHECK AGAIN !!! */
4481 SetPanelBackground();
4482 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4483 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4485 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4487 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4488 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4489 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4490 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4493 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4494 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4498 SetPanelBackground();
4499 SetDrawBackgroundMask(REDRAW_DOOR_1);
4502 UpdateAndDisplayGameControlValues();
4504 UpdateGameDoorValues();
4505 DrawGameDoorValues();
4508 if (!game.restart_level)
4512 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4513 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4514 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4518 /* copy actual game door content to door double buffer for OpenDoor() */
4519 BlitBitmap(drawto, bitmap_db_door,
4520 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4522 OpenDoor(DOOR_OPEN_ALL);
4524 PlaySound(SND_GAME_STARTING);
4526 if (setup.sound_music)
4529 KeyboardAutoRepeatOffUnlessAutoplay();
4531 #if DEBUG_INIT_PLAYER
4534 printf("Player status (final):\n");
4536 for (i = 0; i < MAX_PLAYERS; i++)
4538 struct PlayerInfo *player = &stored_player[i];
4540 printf("- player %d: present == %d, connected == %d, active == %d",
4546 if (local_player == player)
4547 printf(" (local player)");
4562 if (!game.restart_level && !tape.playing)
4564 LevelStats_incPlayed(level_nr);
4566 SaveLevelSetup_SeriesInfo();
4569 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4573 game.restart_level = FALSE;
4576 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4578 /* this is used for non-R'n'D game engines to update certain engine values */
4580 /* needed to determine if sounds are played within the visible screen area */
4581 scroll_x = actual_scroll_x;
4582 scroll_y = actual_scroll_y;
4585 void InitMovDir(int x, int y)
4587 int i, element = Feld[x][y];
4588 static int xy[4][2] =
4595 static int direction[3][4] =
4597 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4598 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4599 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4608 Feld[x][y] = EL_BUG;
4609 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4612 case EL_SPACESHIP_RIGHT:
4613 case EL_SPACESHIP_UP:
4614 case EL_SPACESHIP_LEFT:
4615 case EL_SPACESHIP_DOWN:
4616 Feld[x][y] = EL_SPACESHIP;
4617 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4620 case EL_BD_BUTTERFLY_RIGHT:
4621 case EL_BD_BUTTERFLY_UP:
4622 case EL_BD_BUTTERFLY_LEFT:
4623 case EL_BD_BUTTERFLY_DOWN:
4624 Feld[x][y] = EL_BD_BUTTERFLY;
4625 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4628 case EL_BD_FIREFLY_RIGHT:
4629 case EL_BD_FIREFLY_UP:
4630 case EL_BD_FIREFLY_LEFT:
4631 case EL_BD_FIREFLY_DOWN:
4632 Feld[x][y] = EL_BD_FIREFLY;
4633 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4636 case EL_PACMAN_RIGHT:
4638 case EL_PACMAN_LEFT:
4639 case EL_PACMAN_DOWN:
4640 Feld[x][y] = EL_PACMAN;
4641 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4644 case EL_YAMYAM_LEFT:
4645 case EL_YAMYAM_RIGHT:
4647 case EL_YAMYAM_DOWN:
4648 Feld[x][y] = EL_YAMYAM;
4649 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4652 case EL_SP_SNIKSNAK:
4653 MovDir[x][y] = MV_UP;
4656 case EL_SP_ELECTRON:
4657 MovDir[x][y] = MV_LEFT;
4664 Feld[x][y] = EL_MOLE;
4665 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4669 if (IS_CUSTOM_ELEMENT(element))
4671 struct ElementInfo *ei = &element_info[element];
4672 int move_direction_initial = ei->move_direction_initial;
4673 int move_pattern = ei->move_pattern;
4675 if (move_direction_initial == MV_START_PREVIOUS)
4677 if (MovDir[x][y] != MV_NONE)
4680 move_direction_initial = MV_START_AUTOMATIC;
4683 if (move_direction_initial == MV_START_RANDOM)
4684 MovDir[x][y] = 1 << RND(4);
4685 else if (move_direction_initial & MV_ANY_DIRECTION)
4686 MovDir[x][y] = move_direction_initial;
4687 else if (move_pattern == MV_ALL_DIRECTIONS ||
4688 move_pattern == MV_TURNING_LEFT ||
4689 move_pattern == MV_TURNING_RIGHT ||
4690 move_pattern == MV_TURNING_LEFT_RIGHT ||
4691 move_pattern == MV_TURNING_RIGHT_LEFT ||
4692 move_pattern == MV_TURNING_RANDOM)
4693 MovDir[x][y] = 1 << RND(4);
4694 else if (move_pattern == MV_HORIZONTAL)
4695 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4696 else if (move_pattern == MV_VERTICAL)
4697 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4698 else if (move_pattern & MV_ANY_DIRECTION)
4699 MovDir[x][y] = element_info[element].move_pattern;
4700 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4701 move_pattern == MV_ALONG_RIGHT_SIDE)
4703 /* use random direction as default start direction */
4704 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4705 MovDir[x][y] = 1 << RND(4);
4707 for (i = 0; i < NUM_DIRECTIONS; i++)
4709 int x1 = x + xy[i][0];
4710 int y1 = y + xy[i][1];
4712 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4714 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4715 MovDir[x][y] = direction[0][i];
4717 MovDir[x][y] = direction[1][i];
4726 MovDir[x][y] = 1 << RND(4);
4728 if (element != EL_BUG &&
4729 element != EL_SPACESHIP &&
4730 element != EL_BD_BUTTERFLY &&
4731 element != EL_BD_FIREFLY)
4734 for (i = 0; i < NUM_DIRECTIONS; i++)
4736 int x1 = x + xy[i][0];
4737 int y1 = y + xy[i][1];
4739 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4741 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4743 MovDir[x][y] = direction[0][i];
4746 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4747 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4749 MovDir[x][y] = direction[1][i];
4758 GfxDir[x][y] = MovDir[x][y];
4761 void InitAmoebaNr(int x, int y)
4764 int group_nr = AmoebeNachbarNr(x, y);
4768 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4770 if (AmoebaCnt[i] == 0)
4778 AmoebaNr[x][y] = group_nr;
4779 AmoebaCnt[group_nr]++;
4780 AmoebaCnt2[group_nr]++;
4783 static void PlayerWins(struct PlayerInfo *player)
4785 player->LevelSolved = TRUE;
4786 player->GameOver = TRUE;
4788 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4789 level.native_em_level->lev->score : player->score);
4791 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4793 player->LevelSolved_CountingScore = player->score_final;
4798 static int time, time_final;
4799 static int score, score_final;
4800 static int game_over_delay_1 = 0;
4801 static int game_over_delay_2 = 0;
4802 int game_over_delay_value_1 = 50;
4803 int game_over_delay_value_2 = 50;
4805 if (!local_player->LevelSolved_GameWon)
4809 /* do not start end game actions before the player stops moving (to exit) */
4810 if (local_player->MovPos)
4813 local_player->LevelSolved_GameWon = TRUE;
4814 local_player->LevelSolved_SaveTape = tape.recording;
4815 local_player->LevelSolved_SaveScore = !tape.playing;
4819 LevelStats_incSolved(level_nr);
4821 SaveLevelSetup_SeriesInfo();
4824 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4828 if (tape.auto_play) /* tape might already be stopped here */
4829 tape.auto_play_level_solved = TRUE;
4835 game_over_delay_1 = game_over_delay_value_1;
4836 game_over_delay_2 = game_over_delay_value_2;
4838 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4839 score = score_final = local_player->score_final;
4844 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4846 else if (game.no_time_limit && TimePlayed < 999)
4849 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4852 local_player->score_final = score_final;
4854 if (level_editor_test_game)
4857 score = score_final;
4860 local_player->LevelSolved_CountingTime = time;
4861 local_player->LevelSolved_CountingScore = score;
4863 game_panel_controls[GAME_PANEL_TIME].value = time;
4864 game_panel_controls[GAME_PANEL_SCORE].value = score;
4866 DisplayGameControlValues();
4868 DrawGameValue_Time(time);
4869 DrawGameValue_Score(score);
4873 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4875 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4877 /* close exit door after last player */
4878 if ((AllPlayersGone &&
4879 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4880 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4881 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4882 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4883 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4885 int element = Feld[ExitX][ExitY];
4888 if (element == EL_EM_EXIT_OPEN ||
4889 element == EL_EM_STEEL_EXIT_OPEN)
4896 Feld[ExitX][ExitY] =
4897 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4898 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4899 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4900 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4901 EL_EM_STEEL_EXIT_CLOSING);
4903 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4907 /* player disappears */
4908 DrawLevelField(ExitX, ExitY);
4911 for (i = 0; i < MAX_PLAYERS; i++)
4913 struct PlayerInfo *player = &stored_player[i];
4915 if (player->present)
4917 RemovePlayer(player);
4919 /* player disappears */
4920 DrawLevelField(player->jx, player->jy);
4925 PlaySound(SND_GAME_WINNING);
4928 if (game_over_delay_1 > 0)
4930 game_over_delay_1--;
4935 if (time != time_final)
4937 int time_to_go = ABS(time_final - time);
4938 int time_count_dir = (time < time_final ? +1 : -1);
4939 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4941 time += time_count_steps * time_count_dir;
4942 score += time_count_steps * level.score[SC_TIME_BONUS];
4945 local_player->LevelSolved_CountingTime = time;
4946 local_player->LevelSolved_CountingScore = score;
4948 game_panel_controls[GAME_PANEL_TIME].value = time;
4949 game_panel_controls[GAME_PANEL_SCORE].value = score;
4951 DisplayGameControlValues();
4953 DrawGameValue_Time(time);
4954 DrawGameValue_Score(score);
4957 if (time == time_final)
4958 StopSound(SND_GAME_LEVELTIME_BONUS);
4959 else if (setup.sound_loops)
4960 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4962 PlaySound(SND_GAME_LEVELTIME_BONUS);
4967 local_player->LevelSolved_PanelOff = TRUE;
4969 if (game_over_delay_2 > 0)
4971 game_over_delay_2--;
4984 boolean raise_level = FALSE;
4986 local_player->LevelSolved_GameEnd = TRUE;
4988 CloseDoor(DOOR_CLOSE_1);
4990 if (local_player->LevelSolved_SaveTape)
4997 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4999 SaveTape(tape.level_nr); /* ask to save tape */
5003 if (level_editor_test_game)
5005 game_status = GAME_MODE_MAIN;
5008 DrawAndFadeInMainMenu(REDRAW_FIELD);
5016 if (!local_player->LevelSolved_SaveScore)
5019 FadeOut(REDRAW_FIELD);
5022 game_status = GAME_MODE_MAIN;
5024 DrawAndFadeInMainMenu(REDRAW_FIELD);
5029 if (level_nr == leveldir_current->handicap_level)
5031 leveldir_current->handicap_level++;
5033 SaveLevelSetup_SeriesInfo();
5036 if (level_nr < leveldir_current->last_level)
5037 raise_level = TRUE; /* advance to next level */
5039 if ((hi_pos = NewHiScore()) >= 0)
5041 game_status = GAME_MODE_SCORES;
5043 DrawHallOfFame(hi_pos);
5054 FadeOut(REDRAW_FIELD);
5057 game_status = GAME_MODE_MAIN;
5065 DrawAndFadeInMainMenu(REDRAW_FIELD);
5074 LoadScore(level_nr);
5076 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5077 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5080 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5082 if (local_player->score_final > highscore[k].Score)
5084 /* player has made it to the hall of fame */
5086 if (k < MAX_SCORE_ENTRIES - 1)
5088 int m = MAX_SCORE_ENTRIES - 1;
5091 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5092 if (strEqual(setup.player_name, highscore[l].Name))
5094 if (m == k) /* player's new highscore overwrites his old one */
5098 for (l = m; l > k; l--)
5100 strcpy(highscore[l].Name, highscore[l - 1].Name);
5101 highscore[l].Score = highscore[l - 1].Score;
5108 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5109 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5110 highscore[k].Score = local_player->score_final;
5116 else if (!strncmp(setup.player_name, highscore[k].Name,
5117 MAX_PLAYER_NAME_LEN))
5118 break; /* player already there with a higher score */
5124 SaveScore(level_nr);
5129 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5131 int element = Feld[x][y];
5132 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5133 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5134 int horiz_move = (dx != 0);
5135 int sign = (horiz_move ? dx : dy);
5136 int step = sign * element_info[element].move_stepsize;
5138 /* special values for move stepsize for spring and things on conveyor belt */
5141 if (CAN_FALL(element) &&
5142 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5143 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5144 else if (element == EL_SPRING)
5145 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5151 inline static int getElementMoveStepsize(int x, int y)
5153 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5156 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5158 if (player->GfxAction != action || player->GfxDir != dir)
5161 printf("Player frame reset! (%d => %d, %d => %d)\n",
5162 player->GfxAction, action, player->GfxDir, dir);
5165 player->GfxAction = action;
5166 player->GfxDir = dir;
5168 player->StepFrame = 0;
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173 static void ResetGfxFrame(int x, int y, boolean redraw)
5175 int element = Feld[x][y];
5176 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5177 int last_gfx_frame = GfxFrame[x][y];
5179 if (graphic_info[graphic].anim_global_sync)
5180 GfxFrame[x][y] = FrameCounter;
5181 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5182 GfxFrame[x][y] = CustomValue[x][y];
5183 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5184 GfxFrame[x][y] = element_info[element].collect_score;
5185 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5186 GfxFrame[x][y] = ChangeDelay[x][y];
5188 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5189 DrawLevelGraphicAnimation(x, y, graphic);
5193 static void ResetGfxAnimation(int x, int y)
5195 GfxAction[x][y] = ACTION_DEFAULT;
5196 GfxDir[x][y] = MovDir[x][y];
5199 #if USE_GFX_RESET_GFX_ANIMATION
5200 ResetGfxFrame(x, y, FALSE);
5204 static void ResetRandomAnimationValue(int x, int y)
5206 GfxRandom[x][y] = INIT_GFX_RANDOM();
5209 void InitMovingField(int x, int y, int direction)
5211 int element = Feld[x][y];
5212 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5213 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5216 boolean is_moving_before, is_moving_after;
5218 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5221 /* check if element was/is moving or being moved before/after mode change */
5224 is_moving_before = (WasJustMoving[x][y] != 0);
5226 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5227 is_moving_before = WasJustMoving[x][y];
5230 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5232 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5234 /* reset animation only for moving elements which change direction of moving
5235 or which just started or stopped moving
5236 (else CEs with property "can move" / "not moving" are reset each frame) */
5237 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5239 if (is_moving_before != is_moving_after ||
5240 direction != MovDir[x][y])
5241 ResetGfxAnimation(x, y);
5243 if ((is_moving_before || is_moving_after) && !continues_moving)
5244 ResetGfxAnimation(x, y);
5247 if (!continues_moving)
5248 ResetGfxAnimation(x, y);
5251 MovDir[x][y] = direction;
5252 GfxDir[x][y] = direction;
5254 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5255 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5256 direction == MV_DOWN && CAN_FALL(element) ?
5257 ACTION_FALLING : ACTION_MOVING);
5259 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5260 ACTION_FALLING : ACTION_MOVING);
5263 /* this is needed for CEs with property "can move" / "not moving" */
5265 if (is_moving_after)
5267 if (Feld[newx][newy] == EL_EMPTY)
5268 Feld[newx][newy] = EL_BLOCKED;
5270 MovDir[newx][newy] = MovDir[x][y];
5272 #if USE_NEW_CUSTOM_VALUE
5273 CustomValue[newx][newy] = CustomValue[x][y];
5276 GfxFrame[newx][newy] = GfxFrame[x][y];
5277 GfxRandom[newx][newy] = GfxRandom[x][y];
5278 GfxAction[newx][newy] = GfxAction[x][y];
5279 GfxDir[newx][newy] = GfxDir[x][y];
5283 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5285 int direction = MovDir[x][y];
5286 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5287 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5293 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5295 int oldx = x, oldy = y;
5296 int direction = MovDir[x][y];
5298 if (direction == MV_LEFT)
5300 else if (direction == MV_RIGHT)
5302 else if (direction == MV_UP)
5304 else if (direction == MV_DOWN)
5307 *comes_from_x = oldx;
5308 *comes_from_y = oldy;
5311 int MovingOrBlocked2Element(int x, int y)
5313 int element = Feld[x][y];
5315 if (element == EL_BLOCKED)
5319 Blocked2Moving(x, y, &oldx, &oldy);
5320 return Feld[oldx][oldy];
5326 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5328 /* like MovingOrBlocked2Element(), but if element is moving
5329 and (x,y) is the field the moving element is just leaving,
5330 return EL_BLOCKED instead of the element value */
5331 int element = Feld[x][y];
5333 if (IS_MOVING(x, y))
5335 if (element == EL_BLOCKED)
5339 Blocked2Moving(x, y, &oldx, &oldy);
5340 return Feld[oldx][oldy];
5349 static void RemoveField(int x, int y)
5351 Feld[x][y] = EL_EMPTY;
5357 #if USE_NEW_CUSTOM_VALUE
5358 CustomValue[x][y] = 0;
5362 ChangeDelay[x][y] = 0;
5363 ChangePage[x][y] = -1;
5364 Pushed[x][y] = FALSE;
5367 ExplodeField[x][y] = EX_TYPE_NONE;
5370 GfxElement[x][y] = EL_UNDEFINED;
5371 GfxAction[x][y] = ACTION_DEFAULT;
5372 GfxDir[x][y] = MV_NONE;
5374 /* !!! this would prevent the removed tile from being redrawn !!! */
5375 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5379 void RemoveMovingField(int x, int y)
5381 int oldx = x, oldy = y, newx = x, newy = y;
5382 int element = Feld[x][y];
5383 int next_element = EL_UNDEFINED;
5385 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5388 if (IS_MOVING(x, y))
5390 Moving2Blocked(x, y, &newx, &newy);
5392 if (Feld[newx][newy] != EL_BLOCKED)
5394 /* element is moving, but target field is not free (blocked), but
5395 already occupied by something different (example: acid pool);
5396 in this case, only remove the moving field, but not the target */
5398 RemoveField(oldx, oldy);
5400 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5402 TEST_DrawLevelField(oldx, oldy);
5407 else if (element == EL_BLOCKED)
5409 Blocked2Moving(x, y, &oldx, &oldy);
5410 if (!IS_MOVING(oldx, oldy))
5414 if (element == EL_BLOCKED &&
5415 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5416 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5417 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5418 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5419 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5420 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5421 next_element = get_next_element(Feld[oldx][oldy]);
5423 RemoveField(oldx, oldy);
5424 RemoveField(newx, newy);
5426 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5428 if (next_element != EL_UNDEFINED)
5429 Feld[oldx][oldy] = next_element;
5431 TEST_DrawLevelField(oldx, oldy);
5432 TEST_DrawLevelField(newx, newy);
5435 void DrawDynamite(int x, int y)
5437 int sx = SCREENX(x), sy = SCREENY(y);
5438 int graphic = el2img(Feld[x][y]);
5441 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5444 if (IS_WALKABLE_INSIDE(Back[x][y]))
5448 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5449 else if (Store[x][y])
5450 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5452 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5454 if (Back[x][y] || Store[x][y])
5455 DrawGraphicThruMask(sx, sy, graphic, frame);
5457 DrawGraphic(sx, sy, graphic, frame);
5460 void CheckDynamite(int x, int y)
5462 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5466 if (MovDelay[x][y] != 0)
5469 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5475 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5480 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5482 boolean num_checked_players = 0;
5485 for (i = 0; i < MAX_PLAYERS; i++)
5487 if (stored_player[i].active)
5489 int sx = stored_player[i].jx;
5490 int sy = stored_player[i].jy;
5492 if (num_checked_players == 0)
5499 *sx1 = MIN(*sx1, sx);
5500 *sy1 = MIN(*sy1, sy);
5501 *sx2 = MAX(*sx2, sx);
5502 *sy2 = MAX(*sy2, sy);
5505 num_checked_players++;
5510 static boolean checkIfAllPlayersFitToScreen_RND()
5512 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5514 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5516 return (sx2 - sx1 < SCR_FIELDX &&
5517 sy2 - sy1 < SCR_FIELDY);
5520 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5522 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5524 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5526 *sx = (sx1 + sx2) / 2;
5527 *sy = (sy1 + sy2) / 2;
5530 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5531 boolean center_screen, boolean quick_relocation)
5533 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5534 boolean no_delay = (tape.warp_forward);
5535 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5536 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5538 if (quick_relocation)
5540 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5542 if (!level.shifted_relocation || center_screen)
5544 /* quick relocation (without scrolling), with centering of screen */
5546 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5547 x > SBX_Right + MIDPOSX ? SBX_Right :
5550 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5551 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5556 /* quick relocation (without scrolling), but do not center screen */
5558 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5559 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5562 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5566 int offset_x = x + (scroll_x - center_scroll_x);
5567 int offset_y = y + (scroll_y - center_scroll_y);
5569 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5570 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5571 offset_x - MIDPOSX);
5573 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5574 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5575 offset_y - MIDPOSY);
5581 if (!level.shifted_relocation || center_screen)
5583 /* quick relocation (without scrolling), with centering of screen */
5585 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5586 x > SBX_Right + MIDPOSX ? SBX_Right :
5589 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5590 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5595 /* quick relocation (without scrolling), but do not center screen */
5597 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5598 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5601 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5602 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5605 int offset_x = x + (scroll_x - center_scroll_x);
5606 int offset_y = y + (scroll_y - center_scroll_y);
5608 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5609 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5610 offset_x - MIDPOSX);
5612 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5613 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5614 offset_y - MIDPOSY);
5617 /* quick relocation (without scrolling), inside visible screen area */
5619 int offset = game.scroll_delay_value;
5621 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5622 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5623 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5625 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5626 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5627 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5629 /* don't scroll over playfield boundaries */
5630 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5631 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5633 /* don't scroll over playfield boundaries */
5634 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5635 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5639 RedrawPlayfield(TRUE, 0,0,0,0);
5644 int scroll_xx, scroll_yy;
5646 if (!level.shifted_relocation || center_screen)
5648 /* visible relocation (with scrolling), with centering of screen */
5650 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5651 x > SBX_Right + MIDPOSX ? SBX_Right :
5654 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5655 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5660 /* visible relocation (with scrolling), but do not center screen */
5662 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5663 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5666 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5667 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5670 int offset_x = x + (scroll_x - center_scroll_x);
5671 int offset_y = y + (scroll_y - center_scroll_y);
5673 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5674 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5675 offset_x - MIDPOSX);
5677 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5678 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5679 offset_y - MIDPOSY);
5684 /* visible relocation (with scrolling), with centering of screen */
5686 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5687 x > SBX_Right + MIDPOSX ? SBX_Right :
5690 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5691 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5695 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5697 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5700 int fx = FX, fy = FY;
5702 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5703 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5705 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5711 fx += dx * TILEX / 2;
5712 fy += dy * TILEY / 2;
5714 ScrollLevel(dx, dy);
5717 /* scroll in two steps of half tile size to make things smoother */
5718 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5720 Delay(wait_delay_value);
5722 /* scroll second step to align at full tile size */
5724 Delay(wait_delay_value);
5729 Delay(wait_delay_value);
5733 void RelocatePlayer(int jx, int jy, int el_player_raw)
5735 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5736 int player_nr = GET_PLAYER_NR(el_player);
5737 struct PlayerInfo *player = &stored_player[player_nr];
5738 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5739 boolean no_delay = (tape.warp_forward);
5740 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5741 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5742 int old_jx = player->jx;
5743 int old_jy = player->jy;
5744 int old_element = Feld[old_jx][old_jy];
5745 int element = Feld[jx][jy];
5746 boolean player_relocated = (old_jx != jx || old_jy != jy);
5748 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5749 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5750 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5751 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5752 int leave_side_horiz = move_dir_horiz;
5753 int leave_side_vert = move_dir_vert;
5754 int enter_side = enter_side_horiz | enter_side_vert;
5755 int leave_side = leave_side_horiz | leave_side_vert;
5757 if (player->GameOver) /* do not reanimate dead player */
5760 if (!player_relocated) /* no need to relocate the player */
5763 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5765 RemoveField(jx, jy); /* temporarily remove newly placed player */
5766 DrawLevelField(jx, jy);
5769 if (player->present)
5771 while (player->MovPos)
5773 ScrollPlayer(player, SCROLL_GO_ON);
5774 ScrollScreen(NULL, SCROLL_GO_ON);
5776 AdvanceFrameAndPlayerCounters(player->index_nr);
5781 Delay(wait_delay_value);
5784 DrawPlayer(player); /* needed here only to cleanup last field */
5785 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5787 player->is_moving = FALSE;
5790 if (IS_CUSTOM_ELEMENT(old_element))
5791 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5793 player->index_bit, leave_side);
5795 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5797 player->index_bit, leave_side);
5799 Feld[jx][jy] = el_player;
5800 InitPlayerField(jx, jy, el_player, TRUE);
5802 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5803 possible that the relocation target field did not contain a player element,
5804 but a walkable element, to which the new player was relocated -- in this
5805 case, restore that (already initialized!) element on the player field */
5806 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5808 Feld[jx][jy] = element; /* restore previously existing element */
5810 /* !!! do not initialize already initialized element a second time !!! */
5811 /* (this causes at least problems with "element creation" CE trigger for
5812 already existing elements, and existing Sokoban fields counted twice) */
5813 InitField(jx, jy, FALSE);
5817 /* only visually relocate centered player */
5818 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5819 FALSE, level.instant_relocation);
5821 TestIfPlayerTouchesBadThing(jx, jy);
5822 TestIfPlayerTouchesCustomElement(jx, jy);
5824 if (IS_CUSTOM_ELEMENT(element))
5825 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5826 player->index_bit, enter_side);
5828 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5829 player->index_bit, enter_side);
5832 if (player->is_switching)
5834 /* ensure that relocation while still switching an element does not cause
5835 a new element to be treated as also switched directly after relocation
5836 (this is important for teleporter switches that teleport the player to
5837 a place where another teleporter switch is in the same direction, which
5838 would then incorrectly be treated as immediately switched before the
5839 direction key that caused the switch was released) */
5841 player->switch_x += jx - old_jx;
5842 player->switch_y += jy - old_jy;
5847 void Explode(int ex, int ey, int phase, int mode)
5853 /* !!! eliminate this variable !!! */
5854 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5856 if (game.explosions_delayed)
5858 ExplodeField[ex][ey] = mode;
5862 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5864 int center_element = Feld[ex][ey];
5865 int artwork_element, explosion_element; /* set these values later */
5868 /* --- This is only really needed (and now handled) in "Impact()". --- */
5869 /* do not explode moving elements that left the explode field in time */
5870 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5871 center_element == EL_EMPTY &&
5872 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5877 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5878 if (mode == EX_TYPE_NORMAL ||
5879 mode == EX_TYPE_CENTER ||
5880 mode == EX_TYPE_CROSS)
5881 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5884 /* remove things displayed in background while burning dynamite */
5885 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5888 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5890 /* put moving element to center field (and let it explode there) */
5891 center_element = MovingOrBlocked2Element(ex, ey);
5892 RemoveMovingField(ex, ey);
5893 Feld[ex][ey] = center_element;
5896 /* now "center_element" is finally determined -- set related values now */
5897 artwork_element = center_element; /* for custom player artwork */
5898 explosion_element = center_element; /* for custom player artwork */
5900 if (IS_PLAYER(ex, ey))
5902 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5904 artwork_element = stored_player[player_nr].artwork_element;
5906 if (level.use_explosion_element[player_nr])
5908 explosion_element = level.explosion_element[player_nr];
5909 artwork_element = explosion_element;
5914 if (mode == EX_TYPE_NORMAL ||
5915 mode == EX_TYPE_CENTER ||
5916 mode == EX_TYPE_CROSS)
5917 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5920 last_phase = element_info[explosion_element].explosion_delay + 1;
5922 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5924 int xx = x - ex + 1;
5925 int yy = y - ey + 1;
5928 if (!IN_LEV_FIELD(x, y) ||
5929 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5930 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5933 element = Feld[x][y];
5935 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5937 element = MovingOrBlocked2Element(x, y);
5939 if (!IS_EXPLOSION_PROOF(element))
5940 RemoveMovingField(x, y);
5943 /* indestructible elements can only explode in center (but not flames) */
5944 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5945 mode == EX_TYPE_BORDER)) ||
5946 element == EL_FLAMES)
5949 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5950 behaviour, for example when touching a yamyam that explodes to rocks
5951 with active deadly shield, a rock is created under the player !!! */
5952 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5954 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5955 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5956 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5958 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5961 if (IS_ACTIVE_BOMB(element))
5963 /* re-activate things under the bomb like gate or penguin */
5964 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5971 /* save walkable background elements while explosion on same tile */
5972 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5973 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5974 Back[x][y] = element;
5976 /* ignite explodable elements reached by other explosion */
5977 if (element == EL_EXPLOSION)
5978 element = Store2[x][y];
5980 if (AmoebaNr[x][y] &&
5981 (element == EL_AMOEBA_FULL ||
5982 element == EL_BD_AMOEBA ||
5983 element == EL_AMOEBA_GROWING))
5985 AmoebaCnt[AmoebaNr[x][y]]--;
5986 AmoebaCnt2[AmoebaNr[x][y]]--;
5991 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5993 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5995 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5997 if (PLAYERINFO(ex, ey)->use_murphy)
5998 Store[x][y] = EL_EMPTY;
6001 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6002 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6003 else if (ELEM_IS_PLAYER(center_element))
6004 Store[x][y] = EL_EMPTY;
6005 else if (center_element == EL_YAMYAM)
6006 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6007 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6008 Store[x][y] = element_info[center_element].content.e[xx][yy];
6010 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6011 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6012 otherwise) -- FIX THIS !!! */
6013 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6014 Store[x][y] = element_info[element].content.e[1][1];
6016 else if (!CAN_EXPLODE(element))
6017 Store[x][y] = element_info[element].content.e[1][1];
6020 Store[x][y] = EL_EMPTY;
6022 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6023 center_element == EL_AMOEBA_TO_DIAMOND)
6024 Store2[x][y] = element;
6026 Feld[x][y] = EL_EXPLOSION;
6027 GfxElement[x][y] = artwork_element;
6029 ExplodePhase[x][y] = 1;
6030 ExplodeDelay[x][y] = last_phase;
6035 if (center_element == EL_YAMYAM)
6036 game.yamyam_content_nr =
6037 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6049 GfxFrame[x][y] = 0; /* restart explosion animation */
6051 last_phase = ExplodeDelay[x][y];
6053 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6057 /* activate this even in non-DEBUG version until cause for crash in
6058 getGraphicAnimationFrame() (see below) is found and eliminated */
6064 /* this can happen if the player leaves an explosion just in time */
6065 if (GfxElement[x][y] == EL_UNDEFINED)
6066 GfxElement[x][y] = EL_EMPTY;
6068 if (GfxElement[x][y] == EL_UNDEFINED)
6071 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6072 printf("Explode(): This should never happen!\n");
6075 GfxElement[x][y] = EL_EMPTY;
6081 border_element = Store2[x][y];
6082 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6083 border_element = StorePlayer[x][y];
6085 if (phase == element_info[border_element].ignition_delay ||
6086 phase == last_phase)
6088 boolean border_explosion = FALSE;
6090 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6091 !PLAYER_EXPLOSION_PROTECTED(x, y))
6093 KillPlayerUnlessExplosionProtected(x, y);
6094 border_explosion = TRUE;
6096 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6098 Feld[x][y] = Store2[x][y];
6101 border_explosion = TRUE;
6103 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6105 AmoebeUmwandeln(x, y);
6107 border_explosion = TRUE;
6110 /* if an element just explodes due to another explosion (chain-reaction),
6111 do not immediately end the new explosion when it was the last frame of
6112 the explosion (as it would be done in the following "if"-statement!) */
6113 if (border_explosion && phase == last_phase)
6117 if (phase == last_phase)
6121 element = Feld[x][y] = Store[x][y];
6122 Store[x][y] = Store2[x][y] = 0;
6123 GfxElement[x][y] = EL_UNDEFINED;
6125 /* player can escape from explosions and might therefore be still alive */
6126 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6127 element <= EL_PLAYER_IS_EXPLODING_4)
6129 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6130 int explosion_element = EL_PLAYER_1 + player_nr;
6131 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6132 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6134 if (level.use_explosion_element[player_nr])
6135 explosion_element = level.explosion_element[player_nr];
6137 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6138 element_info[explosion_element].content.e[xx][yy]);
6141 /* restore probably existing indestructible background element */
6142 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6143 element = Feld[x][y] = Back[x][y];
6146 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6147 GfxDir[x][y] = MV_NONE;
6148 ChangeDelay[x][y] = 0;
6149 ChangePage[x][y] = -1;
6151 #if USE_NEW_CUSTOM_VALUE
6152 CustomValue[x][y] = 0;
6155 InitField_WithBug2(x, y, FALSE);
6157 TEST_DrawLevelField(x, y);
6159 TestIfElementTouchesCustomElement(x, y);
6161 if (GFX_CRUMBLED(element))
6162 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6164 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6165 StorePlayer[x][y] = 0;
6167 if (ELEM_IS_PLAYER(element))
6168 RelocatePlayer(x, y, element);
6170 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6172 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6173 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6176 TEST_DrawLevelFieldCrumbled(x, y);
6178 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6180 DrawLevelElement(x, y, Back[x][y]);
6181 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6183 else if (IS_WALKABLE_UNDER(Back[x][y]))
6185 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6186 DrawLevelElementThruMask(x, y, Back[x][y]);
6188 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6189 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6193 void DynaExplode(int ex, int ey)
6196 int dynabomb_element = Feld[ex][ey];
6197 int dynabomb_size = 1;
6198 boolean dynabomb_xl = FALSE;
6199 struct PlayerInfo *player;
6200 static int xy[4][2] =
6208 if (IS_ACTIVE_BOMB(dynabomb_element))
6210 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6211 dynabomb_size = player->dynabomb_size;
6212 dynabomb_xl = player->dynabomb_xl;
6213 player->dynabombs_left++;
6216 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6218 for (i = 0; i < NUM_DIRECTIONS; i++)
6220 for (j = 1; j <= dynabomb_size; j++)
6222 int x = ex + j * xy[i][0];
6223 int y = ey + j * xy[i][1];
6226 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6229 element = Feld[x][y];
6231 /* do not restart explosions of fields with active bombs */
6232 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6235 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6237 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6238 !IS_DIGGABLE(element) && !dynabomb_xl)
6244 void Bang(int x, int y)
6246 int element = MovingOrBlocked2Element(x, y);
6247 int explosion_type = EX_TYPE_NORMAL;
6249 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6251 struct PlayerInfo *player = PLAYERINFO(x, y);
6253 #if USE_FIX_CE_ACTION_WITH_PLAYER
6254 element = Feld[x][y] = player->initial_element;
6256 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6257 player->element_nr);
6260 if (level.use_explosion_element[player->index_nr])
6262 int explosion_element = level.explosion_element[player->index_nr];
6264 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6265 explosion_type = EX_TYPE_CROSS;
6266 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6267 explosion_type = EX_TYPE_CENTER;
6275 case EL_BD_BUTTERFLY:
6278 case EL_DARK_YAMYAM:
6282 RaiseScoreElement(element);
6285 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6286 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6287 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6288 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6289 case EL_DYNABOMB_INCREASE_NUMBER:
6290 case EL_DYNABOMB_INCREASE_SIZE:
6291 case EL_DYNABOMB_INCREASE_POWER:
6292 explosion_type = EX_TYPE_DYNA;
6295 case EL_DC_LANDMINE:
6297 case EL_EM_EXIT_OPEN:
6298 case EL_EM_STEEL_EXIT_OPEN:
6300 explosion_type = EX_TYPE_CENTER;
6305 case EL_LAMP_ACTIVE:
6306 case EL_AMOEBA_TO_DIAMOND:
6307 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6308 explosion_type = EX_TYPE_CENTER;
6312 if (element_info[element].explosion_type == EXPLODES_CROSS)
6313 explosion_type = EX_TYPE_CROSS;
6314 else if (element_info[element].explosion_type == EXPLODES_1X1)
6315 explosion_type = EX_TYPE_CENTER;
6319 if (explosion_type == EX_TYPE_DYNA)
6322 Explode(x, y, EX_PHASE_START, explosion_type);
6324 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6327 void SplashAcid(int x, int y)
6329 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6330 (!IN_LEV_FIELD(x - 1, y - 2) ||
6331 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6332 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
6339 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6342 static void InitBeltMovement()
6344 static int belt_base_element[4] =
6346 EL_CONVEYOR_BELT_1_LEFT,
6347 EL_CONVEYOR_BELT_2_LEFT,
6348 EL_CONVEYOR_BELT_3_LEFT,
6349 EL_CONVEYOR_BELT_4_LEFT
6351 static int belt_base_active_element[4] =
6353 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6354 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6355 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6356 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6361 /* set frame order for belt animation graphic according to belt direction */
6362 for (i = 0; i < NUM_BELTS; i++)
6366 for (j = 0; j < NUM_BELT_PARTS; j++)
6368 int element = belt_base_active_element[belt_nr] + j;
6369 int graphic_1 = el2img(element);
6370 int graphic_2 = el2panelimg(element);
6372 if (game.belt_dir[i] == MV_LEFT)
6374 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6375 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6379 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6380 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6385 SCAN_PLAYFIELD(x, y)
6387 int element = Feld[x][y];
6389 for (i = 0; i < NUM_BELTS; i++)
6391 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6393 int e_belt_nr = getBeltNrFromBeltElement(element);
6396 if (e_belt_nr == belt_nr)
6398 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6400 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6407 static void ToggleBeltSwitch(int x, int y)
6409 static int belt_base_element[4] =
6411 EL_CONVEYOR_BELT_1_LEFT,
6412 EL_CONVEYOR_BELT_2_LEFT,
6413 EL_CONVEYOR_BELT_3_LEFT,
6414 EL_CONVEYOR_BELT_4_LEFT
6416 static int belt_base_active_element[4] =
6418 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6419 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6420 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6421 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6423 static int belt_base_switch_element[4] =
6425 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6426 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6427 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6428 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6430 static int belt_move_dir[4] =
6438 int element = Feld[x][y];
6439 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6440 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6441 int belt_dir = belt_move_dir[belt_dir_nr];
6444 if (!IS_BELT_SWITCH(element))
6447 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6448 game.belt_dir[belt_nr] = belt_dir;
6450 if (belt_dir_nr == 3)
6453 /* set frame order for belt animation graphic according to belt direction */
6454 for (i = 0; i < NUM_BELT_PARTS; i++)
6456 int element = belt_base_active_element[belt_nr] + i;
6457 int graphic_1 = el2img(element);
6458 int graphic_2 = el2panelimg(element);
6460 if (belt_dir == MV_LEFT)
6462 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6463 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6467 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6468 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6472 SCAN_PLAYFIELD(xx, yy)
6474 int element = Feld[xx][yy];
6476 if (IS_BELT_SWITCH(element))
6478 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6480 if (e_belt_nr == belt_nr)
6482 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6483 TEST_DrawLevelField(xx, yy);
6486 else if (IS_BELT(element) && belt_dir != MV_NONE)
6488 int e_belt_nr = getBeltNrFromBeltElement(element);
6490 if (e_belt_nr == belt_nr)
6492 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6494 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6495 TEST_DrawLevelField(xx, yy);
6498 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6500 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6502 if (e_belt_nr == belt_nr)
6504 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6506 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6507 TEST_DrawLevelField(xx, yy);
6513 static void ToggleSwitchgateSwitch(int x, int y)
6517 game.switchgate_pos = !game.switchgate_pos;
6519 SCAN_PLAYFIELD(xx, yy)
6521 int element = Feld[xx][yy];
6523 #if !USE_BOTH_SWITCHGATE_SWITCHES
6524 if (element == EL_SWITCHGATE_SWITCH_UP ||
6525 element == EL_SWITCHGATE_SWITCH_DOWN)
6527 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6528 TEST_DrawLevelField(xx, yy);
6530 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6531 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6533 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6534 TEST_DrawLevelField(xx, yy);
6537 if (element == EL_SWITCHGATE_SWITCH_UP)
6539 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6540 TEST_DrawLevelField(xx, yy);
6542 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6544 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6545 TEST_DrawLevelField(xx, yy);
6547 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6549 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6550 TEST_DrawLevelField(xx, yy);
6552 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6554 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6555 TEST_DrawLevelField(xx, yy);
6558 else if (element == EL_SWITCHGATE_OPEN ||
6559 element == EL_SWITCHGATE_OPENING)
6561 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6563 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6565 else if (element == EL_SWITCHGATE_CLOSED ||
6566 element == EL_SWITCHGATE_CLOSING)
6568 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6570 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6575 static int getInvisibleActiveFromInvisibleElement(int element)
6577 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6578 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6579 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6583 static int getInvisibleFromInvisibleActiveElement(int element)
6585 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6586 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6587 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6591 static void RedrawAllLightSwitchesAndInvisibleElements()
6595 SCAN_PLAYFIELD(x, y)
6597 int element = Feld[x][y];
6599 if (element == EL_LIGHT_SWITCH &&
6600 game.light_time_left > 0)
6602 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6603 TEST_DrawLevelField(x, y);
6605 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6606 game.light_time_left == 0)
6608 Feld[x][y] = EL_LIGHT_SWITCH;
6609 TEST_DrawLevelField(x, y);
6611 else if (element == EL_EMC_DRIPPER &&
6612 game.light_time_left > 0)
6614 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6615 TEST_DrawLevelField(x, y);
6617 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6618 game.light_time_left == 0)
6620 Feld[x][y] = EL_EMC_DRIPPER;
6621 TEST_DrawLevelField(x, y);
6623 else if (element == EL_INVISIBLE_STEELWALL ||
6624 element == EL_INVISIBLE_WALL ||
6625 element == EL_INVISIBLE_SAND)
6627 if (game.light_time_left > 0)
6628 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6630 TEST_DrawLevelField(x, y);
6632 /* uncrumble neighbour fields, if needed */
6633 if (element == EL_INVISIBLE_SAND)
6634 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6636 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6637 element == EL_INVISIBLE_WALL_ACTIVE ||
6638 element == EL_INVISIBLE_SAND_ACTIVE)
6640 if (game.light_time_left == 0)
6641 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6643 TEST_DrawLevelField(x, y);
6645 /* re-crumble neighbour fields, if needed */
6646 if (element == EL_INVISIBLE_SAND)
6647 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6652 static void RedrawAllInvisibleElementsForLenses()
6656 SCAN_PLAYFIELD(x, y)
6658 int element = Feld[x][y];
6660 if (element == EL_EMC_DRIPPER &&
6661 game.lenses_time_left > 0)
6663 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6664 TEST_DrawLevelField(x, y);
6666 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6667 game.lenses_time_left == 0)
6669 Feld[x][y] = EL_EMC_DRIPPER;
6670 TEST_DrawLevelField(x, y);
6672 else if (element == EL_INVISIBLE_STEELWALL ||
6673 element == EL_INVISIBLE_WALL ||
6674 element == EL_INVISIBLE_SAND)
6676 if (game.lenses_time_left > 0)
6677 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6679 TEST_DrawLevelField(x, y);
6681 /* uncrumble neighbour fields, if needed */
6682 if (element == EL_INVISIBLE_SAND)
6683 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6685 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6686 element == EL_INVISIBLE_WALL_ACTIVE ||
6687 element == EL_INVISIBLE_SAND_ACTIVE)
6689 if (game.lenses_time_left == 0)
6690 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6692 TEST_DrawLevelField(x, y);
6694 /* re-crumble neighbour fields, if needed */
6695 if (element == EL_INVISIBLE_SAND)
6696 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6701 static void RedrawAllInvisibleElementsForMagnifier()
6705 SCAN_PLAYFIELD(x, y)
6707 int element = Feld[x][y];
6709 if (element == EL_EMC_FAKE_GRASS &&
6710 game.magnify_time_left > 0)
6712 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6713 TEST_DrawLevelField(x, y);
6715 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6716 game.magnify_time_left == 0)
6718 Feld[x][y] = EL_EMC_FAKE_GRASS;
6719 TEST_DrawLevelField(x, y);
6721 else if (IS_GATE_GRAY(element) &&
6722 game.magnify_time_left > 0)
6724 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6725 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6726 IS_EM_GATE_GRAY(element) ?
6727 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6728 IS_EMC_GATE_GRAY(element) ?
6729 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6730 IS_DC_GATE_GRAY(element) ?
6731 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6733 TEST_DrawLevelField(x, y);
6735 else if (IS_GATE_GRAY_ACTIVE(element) &&
6736 game.magnify_time_left == 0)
6738 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6739 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6740 IS_EM_GATE_GRAY_ACTIVE(element) ?
6741 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6742 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6743 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6744 IS_DC_GATE_GRAY_ACTIVE(element) ?
6745 EL_DC_GATE_WHITE_GRAY :
6747 TEST_DrawLevelField(x, y);
6752 static void ToggleLightSwitch(int x, int y)
6754 int element = Feld[x][y];
6756 game.light_time_left =
6757 (element == EL_LIGHT_SWITCH ?
6758 level.time_light * FRAMES_PER_SECOND : 0);
6760 RedrawAllLightSwitchesAndInvisibleElements();
6763 static void ActivateTimegateSwitch(int x, int y)
6767 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6769 SCAN_PLAYFIELD(xx, yy)
6771 int element = Feld[xx][yy];
6773 if (element == EL_TIMEGATE_CLOSED ||
6774 element == EL_TIMEGATE_CLOSING)
6776 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6777 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6781 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6783 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6784 TEST_DrawLevelField(xx, yy);
6791 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6792 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6794 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6798 void Impact(int x, int y)
6800 boolean last_line = (y == lev_fieldy - 1);
6801 boolean object_hit = FALSE;
6802 boolean impact = (last_line || object_hit);
6803 int element = Feld[x][y];
6804 int smashed = EL_STEELWALL;
6806 if (!last_line) /* check if element below was hit */
6808 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6811 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6812 MovDir[x][y + 1] != MV_DOWN ||
6813 MovPos[x][y + 1] <= TILEY / 2));
6815 /* do not smash moving elements that left the smashed field in time */
6816 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6817 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6820 #if USE_QUICKSAND_IMPACT_BUGFIX
6821 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6823 RemoveMovingField(x, y + 1);
6824 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6825 Feld[x][y + 2] = EL_ROCK;
6826 TEST_DrawLevelField(x, y + 2);
6831 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6833 RemoveMovingField(x, y + 1);
6834 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6835 Feld[x][y + 2] = EL_ROCK;
6836 TEST_DrawLevelField(x, y + 2);
6843 smashed = MovingOrBlocked2Element(x, y + 1);
6845 impact = (last_line || object_hit);
6848 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6850 SplashAcid(x, y + 1);
6854 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6855 /* only reset graphic animation if graphic really changes after impact */
6857 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6859 ResetGfxAnimation(x, y);
6860 TEST_DrawLevelField(x, y);
6863 if (impact && CAN_EXPLODE_IMPACT(element))
6868 else if (impact && element == EL_PEARL &&
6869 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6871 ResetGfxAnimation(x, y);
6873 Feld[x][y] = EL_PEARL_BREAKING;
6874 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6877 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6879 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6884 if (impact && element == EL_AMOEBA_DROP)
6886 if (object_hit && IS_PLAYER(x, y + 1))
6887 KillPlayerUnlessEnemyProtected(x, y + 1);
6888 else if (object_hit && smashed == EL_PENGUIN)
6892 Feld[x][y] = EL_AMOEBA_GROWING;
6893 Store[x][y] = EL_AMOEBA_WET;
6895 ResetRandomAnimationValue(x, y);
6900 if (object_hit) /* check which object was hit */
6902 if ((CAN_PASS_MAGIC_WALL(element) &&
6903 (smashed == EL_MAGIC_WALL ||
6904 smashed == EL_BD_MAGIC_WALL)) ||
6905 (CAN_PASS_DC_MAGIC_WALL(element) &&
6906 smashed == EL_DC_MAGIC_WALL))
6909 int activated_magic_wall =
6910 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6911 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6912 EL_DC_MAGIC_WALL_ACTIVE);
6914 /* activate magic wall / mill */
6915 SCAN_PLAYFIELD(xx, yy)
6917 if (Feld[xx][yy] == smashed)
6918 Feld[xx][yy] = activated_magic_wall;
6921 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6922 game.magic_wall_active = TRUE;
6924 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6925 SND_MAGIC_WALL_ACTIVATING :
6926 smashed == EL_BD_MAGIC_WALL ?
6927 SND_BD_MAGIC_WALL_ACTIVATING :
6928 SND_DC_MAGIC_WALL_ACTIVATING));
6931 if (IS_PLAYER(x, y + 1))
6933 if (CAN_SMASH_PLAYER(element))
6935 KillPlayerUnlessEnemyProtected(x, y + 1);
6939 else if (smashed == EL_PENGUIN)
6941 if (CAN_SMASH_PLAYER(element))
6947 else if (element == EL_BD_DIAMOND)
6949 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6955 else if (((element == EL_SP_INFOTRON ||
6956 element == EL_SP_ZONK) &&
6957 (smashed == EL_SP_SNIKSNAK ||
6958 smashed == EL_SP_ELECTRON ||
6959 smashed == EL_SP_DISK_ORANGE)) ||
6960 (element == EL_SP_INFOTRON &&
6961 smashed == EL_SP_DISK_YELLOW))
6966 else if (CAN_SMASH_EVERYTHING(element))
6968 if (IS_CLASSIC_ENEMY(smashed) ||
6969 CAN_EXPLODE_SMASHED(smashed))
6974 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6976 if (smashed == EL_LAMP ||
6977 smashed == EL_LAMP_ACTIVE)
6982 else if (smashed == EL_NUT)
6984 Feld[x][y + 1] = EL_NUT_BREAKING;
6985 PlayLevelSound(x, y, SND_NUT_BREAKING);
6986 RaiseScoreElement(EL_NUT);
6989 else if (smashed == EL_PEARL)
6991 ResetGfxAnimation(x, y);
6993 Feld[x][y + 1] = EL_PEARL_BREAKING;
6994 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6997 else if (smashed == EL_DIAMOND)
6999 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7000 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7003 else if (IS_BELT_SWITCH(smashed))
7005 ToggleBeltSwitch(x, y + 1);
7007 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7008 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7009 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7010 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7012 ToggleSwitchgateSwitch(x, y + 1);
7014 else if (smashed == EL_LIGHT_SWITCH ||
7015 smashed == EL_LIGHT_SWITCH_ACTIVE)
7017 ToggleLightSwitch(x, y + 1);
7022 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7025 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7027 CheckElementChangeBySide(x, y + 1, smashed, element,
7028 CE_SWITCHED, CH_SIDE_TOP);
7029 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7035 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7040 /* play sound of magic wall / mill */
7042 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7043 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7044 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7046 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7047 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7048 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7049 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7050 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7051 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7056 /* play sound of object that hits the ground */
7057 if (last_line || object_hit)
7058 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7061 inline static void TurnRoundExt(int x, int y)
7073 { 0, 0 }, { 0, 0 }, { 0, 0 },
7078 int left, right, back;
7082 { MV_DOWN, MV_UP, MV_RIGHT },
7083 { MV_UP, MV_DOWN, MV_LEFT },
7085 { MV_LEFT, MV_RIGHT, MV_DOWN },
7089 { MV_RIGHT, MV_LEFT, MV_UP }
7092 int element = Feld[x][y];
7093 int move_pattern = element_info[element].move_pattern;
7095 int old_move_dir = MovDir[x][y];
7096 int left_dir = turn[old_move_dir].left;
7097 int right_dir = turn[old_move_dir].right;
7098 int back_dir = turn[old_move_dir].back;
7100 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7101 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7102 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7103 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7105 int left_x = x + left_dx, left_y = y + left_dy;
7106 int right_x = x + right_dx, right_y = y + right_dy;
7107 int move_x = x + move_dx, move_y = y + move_dy;
7111 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7113 TestIfBadThingTouchesOtherBadThing(x, y);
7115 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7116 MovDir[x][y] = right_dir;
7117 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7118 MovDir[x][y] = left_dir;
7120 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7122 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7125 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7127 TestIfBadThingTouchesOtherBadThing(x, y);
7129 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7130 MovDir[x][y] = left_dir;
7131 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7132 MovDir[x][y] = right_dir;
7134 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7136 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7139 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7141 TestIfBadThingTouchesOtherBadThing(x, y);
7143 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7144 MovDir[x][y] = left_dir;
7145 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7146 MovDir[x][y] = right_dir;
7148 if (MovDir[x][y] != old_move_dir)
7151 else if (element == EL_YAMYAM)
7153 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7154 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7156 if (can_turn_left && can_turn_right)
7157 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7158 else if (can_turn_left)
7159 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7160 else if (can_turn_right)
7161 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7163 MovDir[x][y] = back_dir;
7165 MovDelay[x][y] = 16 + 16 * RND(3);
7167 else if (element == EL_DARK_YAMYAM)
7169 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7171 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7174 if (can_turn_left && can_turn_right)
7175 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7176 else if (can_turn_left)
7177 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7178 else if (can_turn_right)
7179 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7181 MovDir[x][y] = back_dir;
7183 MovDelay[x][y] = 16 + 16 * RND(3);
7185 else if (element == EL_PACMAN)
7187 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7188 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7190 if (can_turn_left && can_turn_right)
7191 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7192 else if (can_turn_left)
7193 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7194 else if (can_turn_right)
7195 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7197 MovDir[x][y] = back_dir;
7199 MovDelay[x][y] = 6 + RND(40);
7201 else if (element == EL_PIG)
7203 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7204 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7205 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7206 boolean should_turn_left, should_turn_right, should_move_on;
7208 int rnd = RND(rnd_value);
7210 should_turn_left = (can_turn_left &&
7212 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7213 y + back_dy + left_dy)));
7214 should_turn_right = (can_turn_right &&
7216 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7217 y + back_dy + right_dy)));
7218 should_move_on = (can_move_on &&
7221 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7222 y + move_dy + left_dy) ||
7223 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7224 y + move_dy + right_dy)));
7226 if (should_turn_left || should_turn_right || should_move_on)
7228 if (should_turn_left && should_turn_right && should_move_on)
7229 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7230 rnd < 2 * rnd_value / 3 ? right_dir :
7232 else if (should_turn_left && should_turn_right)
7233 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7234 else if (should_turn_left && should_move_on)
7235 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7236 else if (should_turn_right && should_move_on)
7237 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7238 else if (should_turn_left)
7239 MovDir[x][y] = left_dir;
7240 else if (should_turn_right)
7241 MovDir[x][y] = right_dir;
7242 else if (should_move_on)
7243 MovDir[x][y] = old_move_dir;
7245 else if (can_move_on && rnd > rnd_value / 8)
7246 MovDir[x][y] = old_move_dir;
7247 else if (can_turn_left && can_turn_right)
7248 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7249 else if (can_turn_left && rnd > rnd_value / 8)
7250 MovDir[x][y] = left_dir;
7251 else if (can_turn_right && rnd > rnd_value/8)
7252 MovDir[x][y] = right_dir;
7254 MovDir[x][y] = back_dir;
7256 xx = x + move_xy[MovDir[x][y]].dx;
7257 yy = y + move_xy[MovDir[x][y]].dy;
7259 if (!IN_LEV_FIELD(xx, yy) ||
7260 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7261 MovDir[x][y] = old_move_dir;
7265 else if (element == EL_DRAGON)
7267 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7268 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7269 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7271 int rnd = RND(rnd_value);
7273 if (can_move_on && rnd > rnd_value / 8)
7274 MovDir[x][y] = old_move_dir;
7275 else if (can_turn_left && can_turn_right)
7276 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7277 else if (can_turn_left && rnd > rnd_value / 8)
7278 MovDir[x][y] = left_dir;
7279 else if (can_turn_right && rnd > rnd_value / 8)
7280 MovDir[x][y] = right_dir;
7282 MovDir[x][y] = back_dir;
7284 xx = x + move_xy[MovDir[x][y]].dx;
7285 yy = y + move_xy[MovDir[x][y]].dy;
7287 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7288 MovDir[x][y] = old_move_dir;
7292 else if (element == EL_MOLE)
7294 boolean can_move_on =
7295 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7296 IS_AMOEBOID(Feld[move_x][move_y]) ||
7297 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7300 boolean can_turn_left =
7301 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7302 IS_AMOEBOID(Feld[left_x][left_y])));
7304 boolean can_turn_right =
7305 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7306 IS_AMOEBOID(Feld[right_x][right_y])));
7308 if (can_turn_left && can_turn_right)
7309 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7310 else if (can_turn_left)
7311 MovDir[x][y] = left_dir;
7313 MovDir[x][y] = right_dir;
7316 if (MovDir[x][y] != old_move_dir)
7319 else if (element == EL_BALLOON)
7321 MovDir[x][y] = game.wind_direction;
7324 else if (element == EL_SPRING)
7326 #if USE_NEW_SPRING_BUMPER
7327 if (MovDir[x][y] & MV_HORIZONTAL)
7329 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7330 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7332 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7333 ResetGfxAnimation(move_x, move_y);
7334 TEST_DrawLevelField(move_x, move_y);
7336 MovDir[x][y] = back_dir;
7338 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7339 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7340 MovDir[x][y] = MV_NONE;
7343 if (MovDir[x][y] & MV_HORIZONTAL &&
7344 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7345 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7346 MovDir[x][y] = MV_NONE;
7351 else if (element == EL_ROBOT ||
7352 element == EL_SATELLITE ||
7353 element == EL_PENGUIN ||
7354 element == EL_EMC_ANDROID)
7356 int attr_x = -1, attr_y = -1;
7367 for (i = 0; i < MAX_PLAYERS; i++)
7369 struct PlayerInfo *player = &stored_player[i];
7370 int jx = player->jx, jy = player->jy;
7372 if (!player->active)
7376 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7384 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7385 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7386 game.engine_version < VERSION_IDENT(3,1,0,0)))
7392 if (element == EL_PENGUIN)
7395 static int xy[4][2] =
7403 for (i = 0; i < NUM_DIRECTIONS; i++)
7405 int ex = x + xy[i][0];
7406 int ey = y + xy[i][1];
7408 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7409 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7410 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7411 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7420 MovDir[x][y] = MV_NONE;
7422 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7423 else if (attr_x > x)
7424 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7426 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7427 else if (attr_y > y)
7428 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7430 if (element == EL_ROBOT)
7434 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7435 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7436 Moving2Blocked(x, y, &newx, &newy);
7438 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7439 MovDelay[x][y] = 8 + 8 * !RND(3);
7441 MovDelay[x][y] = 16;
7443 else if (element == EL_PENGUIN)
7449 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7451 boolean first_horiz = RND(2);
7452 int new_move_dir = MovDir[x][y];
7455 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456 Moving2Blocked(x, y, &newx, &newy);
7458 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7462 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7463 Moving2Blocked(x, y, &newx, &newy);
7465 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7468 MovDir[x][y] = old_move_dir;
7472 else if (element == EL_SATELLITE)
7478 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7480 boolean first_horiz = RND(2);
7481 int new_move_dir = MovDir[x][y];
7484 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485 Moving2Blocked(x, y, &newx, &newy);
7487 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7491 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7492 Moving2Blocked(x, y, &newx, &newy);
7494 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7497 MovDir[x][y] = old_move_dir;
7501 else if (element == EL_EMC_ANDROID)
7503 static int check_pos[16] =
7505 -1, /* 0 => (invalid) */
7506 7, /* 1 => MV_LEFT */
7507 3, /* 2 => MV_RIGHT */
7508 -1, /* 3 => (invalid) */
7510 0, /* 5 => MV_LEFT | MV_UP */
7511 2, /* 6 => MV_RIGHT | MV_UP */
7512 -1, /* 7 => (invalid) */
7513 5, /* 8 => MV_DOWN */
7514 6, /* 9 => MV_LEFT | MV_DOWN */
7515 4, /* 10 => MV_RIGHT | MV_DOWN */
7516 -1, /* 11 => (invalid) */
7517 -1, /* 12 => (invalid) */
7518 -1, /* 13 => (invalid) */
7519 -1, /* 14 => (invalid) */
7520 -1, /* 15 => (invalid) */
7528 { -1, -1, MV_LEFT | MV_UP },
7530 { +1, -1, MV_RIGHT | MV_UP },
7531 { +1, 0, MV_RIGHT },
7532 { +1, +1, MV_RIGHT | MV_DOWN },
7534 { -1, +1, MV_LEFT | MV_DOWN },
7537 int start_pos, check_order;
7538 boolean can_clone = FALSE;
7541 /* check if there is any free field around current position */
7542 for (i = 0; i < 8; i++)
7544 int newx = x + check_xy[i].dx;
7545 int newy = y + check_xy[i].dy;
7547 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7555 if (can_clone) /* randomly find an element to clone */
7559 start_pos = check_pos[RND(8)];
7560 check_order = (RND(2) ? -1 : +1);
7562 for (i = 0; i < 8; i++)
7564 int pos_raw = start_pos + i * check_order;
7565 int pos = (pos_raw + 8) % 8;
7566 int newx = x + check_xy[pos].dx;
7567 int newy = y + check_xy[pos].dy;
7569 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7571 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7572 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7574 Store[x][y] = Feld[newx][newy];
7583 if (can_clone) /* randomly find a direction to move */
7587 start_pos = check_pos[RND(8)];
7588 check_order = (RND(2) ? -1 : +1);
7590 for (i = 0; i < 8; i++)
7592 int pos_raw = start_pos + i * check_order;
7593 int pos = (pos_raw + 8) % 8;
7594 int newx = x + check_xy[pos].dx;
7595 int newy = y + check_xy[pos].dy;
7596 int new_move_dir = check_xy[pos].dir;
7598 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7600 MovDir[x][y] = new_move_dir;
7601 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7610 if (can_clone) /* cloning and moving successful */
7613 /* cannot clone -- try to move towards player */
7615 start_pos = check_pos[MovDir[x][y] & 0x0f];
7616 check_order = (RND(2) ? -1 : +1);
7618 for (i = 0; i < 3; i++)
7620 /* first check start_pos, then previous/next or (next/previous) pos */
7621 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7622 int pos = (pos_raw + 8) % 8;
7623 int newx = x + check_xy[pos].dx;
7624 int newy = y + check_xy[pos].dy;
7625 int new_move_dir = check_xy[pos].dir;
7627 if (IS_PLAYER(newx, newy))
7630 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7632 MovDir[x][y] = new_move_dir;
7633 MovDelay[x][y] = level.android_move_time * 8 + 1;
7640 else if (move_pattern == MV_TURNING_LEFT ||
7641 move_pattern == MV_TURNING_RIGHT ||
7642 move_pattern == MV_TURNING_LEFT_RIGHT ||
7643 move_pattern == MV_TURNING_RIGHT_LEFT ||
7644 move_pattern == MV_TURNING_RANDOM ||
7645 move_pattern == MV_ALL_DIRECTIONS)
7647 boolean can_turn_left =
7648 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7649 boolean can_turn_right =
7650 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7652 if (element_info[element].move_stepsize == 0) /* "not moving" */
7655 if (move_pattern == MV_TURNING_LEFT)
7656 MovDir[x][y] = left_dir;
7657 else if (move_pattern == MV_TURNING_RIGHT)
7658 MovDir[x][y] = right_dir;
7659 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7660 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7661 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7662 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7663 else if (move_pattern == MV_TURNING_RANDOM)
7664 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7665 can_turn_right && !can_turn_left ? right_dir :
7666 RND(2) ? left_dir : right_dir);
7667 else if (can_turn_left && can_turn_right)
7668 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7669 else if (can_turn_left)
7670 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7671 else if (can_turn_right)
7672 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7674 MovDir[x][y] = back_dir;
7676 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7678 else if (move_pattern == MV_HORIZONTAL ||
7679 move_pattern == MV_VERTICAL)
7681 if (move_pattern & old_move_dir)
7682 MovDir[x][y] = back_dir;
7683 else if (move_pattern == MV_HORIZONTAL)
7684 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7685 else if (move_pattern == MV_VERTICAL)
7686 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7688 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7690 else if (move_pattern & MV_ANY_DIRECTION)
7692 MovDir[x][y] = move_pattern;
7693 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7695 else if (move_pattern & MV_WIND_DIRECTION)
7697 MovDir[x][y] = game.wind_direction;
7698 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7700 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7702 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7703 MovDir[x][y] = left_dir;
7704 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7705 MovDir[x][y] = right_dir;
7707 if (MovDir[x][y] != old_move_dir)
7708 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7710 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7712 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7713 MovDir[x][y] = right_dir;
7714 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7715 MovDir[x][y] = left_dir;
7717 if (MovDir[x][y] != old_move_dir)
7718 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7720 else if (move_pattern == MV_TOWARDS_PLAYER ||
7721 move_pattern == MV_AWAY_FROM_PLAYER)
7723 int attr_x = -1, attr_y = -1;
7725 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7736 for (i = 0; i < MAX_PLAYERS; i++)
7738 struct PlayerInfo *player = &stored_player[i];
7739 int jx = player->jx, jy = player->jy;
7741 if (!player->active)
7745 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7753 MovDir[x][y] = MV_NONE;
7755 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7756 else if (attr_x > x)
7757 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7759 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7760 else if (attr_y > y)
7761 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7763 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7765 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7767 boolean first_horiz = RND(2);
7768 int new_move_dir = MovDir[x][y];
7770 if (element_info[element].move_stepsize == 0) /* "not moving" */
7772 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7773 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7779 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7780 Moving2Blocked(x, y, &newx, &newy);
7782 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7786 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7787 Moving2Blocked(x, y, &newx, &newy);
7789 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7792 MovDir[x][y] = old_move_dir;
7795 else if (move_pattern == MV_WHEN_PUSHED ||
7796 move_pattern == MV_WHEN_DROPPED)
7798 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7799 MovDir[x][y] = MV_NONE;
7803 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7805 static int test_xy[7][2] =
7815 static int test_dir[7] =
7825 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7826 int move_preference = -1000000; /* start with very low preference */
7827 int new_move_dir = MV_NONE;
7828 int start_test = RND(4);
7831 for (i = 0; i < NUM_DIRECTIONS; i++)
7833 int move_dir = test_dir[start_test + i];
7834 int move_dir_preference;
7836 xx = x + test_xy[start_test + i][0];
7837 yy = y + test_xy[start_test + i][1];
7839 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7840 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7842 new_move_dir = move_dir;
7847 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7850 move_dir_preference = -1 * RunnerVisit[xx][yy];
7851 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7852 move_dir_preference = PlayerVisit[xx][yy];
7854 if (move_dir_preference > move_preference)
7856 /* prefer field that has not been visited for the longest time */
7857 move_preference = move_dir_preference;
7858 new_move_dir = move_dir;
7860 else if (move_dir_preference == move_preference &&
7861 move_dir == old_move_dir)
7863 /* prefer last direction when all directions are preferred equally */
7864 move_preference = move_dir_preference;
7865 new_move_dir = move_dir;
7869 MovDir[x][y] = new_move_dir;
7870 if (old_move_dir != new_move_dir)
7871 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7875 static void TurnRound(int x, int y)
7877 int direction = MovDir[x][y];
7881 GfxDir[x][y] = MovDir[x][y];
7883 if (direction != MovDir[x][y])
7887 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7889 ResetGfxFrame(x, y, FALSE);
7892 static boolean JustBeingPushed(int x, int y)
7896 for (i = 0; i < MAX_PLAYERS; i++)
7898 struct PlayerInfo *player = &stored_player[i];
7900 if (player->active && player->is_pushing && player->MovPos)
7902 int next_jx = player->jx + (player->jx - player->last_jx);
7903 int next_jy = player->jy + (player->jy - player->last_jy);
7905 if (x == next_jx && y == next_jy)
7913 void StartMoving(int x, int y)
7915 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7916 int element = Feld[x][y];
7921 if (MovDelay[x][y] == 0)
7922 GfxAction[x][y] = ACTION_DEFAULT;
7924 if (CAN_FALL(element) && y < lev_fieldy - 1)
7926 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7927 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7928 if (JustBeingPushed(x, y))
7931 if (element == EL_QUICKSAND_FULL)
7933 if (IS_FREE(x, y + 1))
7935 InitMovingField(x, y, MV_DOWN);
7936 started_moving = TRUE;
7938 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7939 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7940 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7941 Store[x][y] = EL_ROCK;
7943 Store[x][y] = EL_ROCK;
7946 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7948 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7950 if (!MovDelay[x][y])
7952 MovDelay[x][y] = TILEY + 1;
7954 ResetGfxAnimation(x, y);
7955 ResetGfxAnimation(x, y + 1);
7960 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7961 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7968 Feld[x][y] = EL_QUICKSAND_EMPTY;
7969 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7970 Store[x][y + 1] = Store[x][y];
7973 PlayLevelSoundAction(x, y, ACTION_FILLING);
7975 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7977 if (!MovDelay[x][y])
7979 MovDelay[x][y] = TILEY + 1;
7981 ResetGfxAnimation(x, y);
7982 ResetGfxAnimation(x, y + 1);
7987 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7988 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7995 Feld[x][y] = EL_QUICKSAND_EMPTY;
7996 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7997 Store[x][y + 1] = Store[x][y];
8000 PlayLevelSoundAction(x, y, ACTION_FILLING);
8003 else if (element == EL_QUICKSAND_FAST_FULL)
8005 if (IS_FREE(x, y + 1))
8007 InitMovingField(x, y, MV_DOWN);
8008 started_moving = TRUE;
8010 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8011 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8012 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8013 Store[x][y] = EL_ROCK;
8015 Store[x][y] = EL_ROCK;
8018 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8020 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8022 if (!MovDelay[x][y])
8024 MovDelay[x][y] = TILEY + 1;
8026 ResetGfxAnimation(x, y);
8027 ResetGfxAnimation(x, y + 1);
8032 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8033 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8040 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8041 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8042 Store[x][y + 1] = Store[x][y];
8045 PlayLevelSoundAction(x, y, ACTION_FILLING);
8047 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8049 if (!MovDelay[x][y])
8051 MovDelay[x][y] = TILEY + 1;
8053 ResetGfxAnimation(x, y);
8054 ResetGfxAnimation(x, y + 1);
8059 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8060 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8067 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8068 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8069 Store[x][y + 1] = Store[x][y];
8072 PlayLevelSoundAction(x, y, ACTION_FILLING);
8075 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8076 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8078 InitMovingField(x, y, MV_DOWN);
8079 started_moving = TRUE;
8081 Feld[x][y] = EL_QUICKSAND_FILLING;
8082 Store[x][y] = element;
8084 PlayLevelSoundAction(x, y, ACTION_FILLING);
8086 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8087 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8089 InitMovingField(x, y, MV_DOWN);
8090 started_moving = TRUE;
8092 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8093 Store[x][y] = element;
8095 PlayLevelSoundAction(x, y, ACTION_FILLING);
8097 else if (element == EL_MAGIC_WALL_FULL)
8099 if (IS_FREE(x, y + 1))
8101 InitMovingField(x, y, MV_DOWN);
8102 started_moving = TRUE;
8104 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8105 Store[x][y] = EL_CHANGED(Store[x][y]);
8107 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8109 if (!MovDelay[x][y])
8110 MovDelay[x][y] = TILEY / 4 + 1;
8119 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8120 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8121 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8125 else if (element == EL_BD_MAGIC_WALL_FULL)
8127 if (IS_FREE(x, y + 1))
8129 InitMovingField(x, y, MV_DOWN);
8130 started_moving = TRUE;
8132 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8133 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8135 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8137 if (!MovDelay[x][y])
8138 MovDelay[x][y] = TILEY / 4 + 1;
8147 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8148 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8149 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8153 else if (element == EL_DC_MAGIC_WALL_FULL)
8155 if (IS_FREE(x, y + 1))
8157 InitMovingField(x, y, MV_DOWN);
8158 started_moving = TRUE;
8160 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8161 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8163 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8165 if (!MovDelay[x][y])
8166 MovDelay[x][y] = TILEY / 4 + 1;
8175 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8176 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8177 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8181 else if ((CAN_PASS_MAGIC_WALL(element) &&
8182 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8183 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8184 (CAN_PASS_DC_MAGIC_WALL(element) &&
8185 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8188 InitMovingField(x, y, MV_DOWN);
8189 started_moving = TRUE;
8192 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8193 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8194 EL_DC_MAGIC_WALL_FILLING);
8195 Store[x][y] = element;
8197 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8199 SplashAcid(x, y + 1);
8201 InitMovingField(x, y, MV_DOWN);
8202 started_moving = TRUE;
8204 Store[x][y] = EL_ACID;
8207 #if USE_FIX_IMPACT_COLLISION
8208 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8209 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8211 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8212 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8214 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8215 CAN_FALL(element) && WasJustFalling[x][y] &&
8216 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8218 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8219 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8220 (Feld[x][y + 1] == EL_BLOCKED)))
8222 /* this is needed for a special case not covered by calling "Impact()"
8223 from "ContinueMoving()": if an element moves to a tile directly below
8224 another element which was just falling on that tile (which was empty
8225 in the previous frame), the falling element above would just stop
8226 instead of smashing the element below (in previous version, the above
8227 element was just checked for "moving" instead of "falling", resulting
8228 in incorrect smashes caused by horizontal movement of the above
8229 element; also, the case of the player being the element to smash was
8230 simply not covered here... :-/ ) */
8232 CheckCollision[x][y] = 0;
8233 CheckImpact[x][y] = 0;
8237 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8239 if (MovDir[x][y] == MV_NONE)
8241 InitMovingField(x, y, MV_DOWN);
8242 started_moving = TRUE;
8245 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8247 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8248 MovDir[x][y] = MV_DOWN;
8250 InitMovingField(x, y, MV_DOWN);
8251 started_moving = TRUE;
8253 else if (element == EL_AMOEBA_DROP)
8255 Feld[x][y] = EL_AMOEBA_GROWING;
8256 Store[x][y] = EL_AMOEBA_WET;
8258 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8259 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8260 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8261 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8263 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8264 (IS_FREE(x - 1, y + 1) ||
8265 Feld[x - 1][y + 1] == EL_ACID));
8266 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8267 (IS_FREE(x + 1, y + 1) ||
8268 Feld[x + 1][y + 1] == EL_ACID));
8269 boolean can_fall_any = (can_fall_left || can_fall_right);
8270 boolean can_fall_both = (can_fall_left && can_fall_right);
8271 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8273 #if USE_NEW_ALL_SLIPPERY
8274 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8276 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8277 can_fall_right = FALSE;
8278 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8279 can_fall_left = FALSE;
8280 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8281 can_fall_right = FALSE;
8282 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8283 can_fall_left = FALSE;
8285 can_fall_any = (can_fall_left || can_fall_right);
8286 can_fall_both = FALSE;
8289 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8291 if (slippery_type == SLIPPERY_ONLY_LEFT)
8292 can_fall_right = FALSE;
8293 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8294 can_fall_left = FALSE;
8295 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8296 can_fall_right = FALSE;
8297 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8298 can_fall_left = FALSE;
8300 can_fall_any = (can_fall_left || can_fall_right);
8301 can_fall_both = (can_fall_left && can_fall_right);
8305 #if USE_NEW_ALL_SLIPPERY
8307 #if USE_NEW_SP_SLIPPERY
8308 /* !!! better use the same properties as for custom elements here !!! */
8309 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8310 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8312 can_fall_right = FALSE; /* slip down on left side */
8313 can_fall_both = FALSE;
8318 #if USE_NEW_ALL_SLIPPERY
8321 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8322 can_fall_right = FALSE; /* slip down on left side */
8324 can_fall_left = !(can_fall_right = RND(2));
8326 can_fall_both = FALSE;
8331 if (game.emulation == EMU_BOULDERDASH ||
8332 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8333 can_fall_right = FALSE; /* slip down on left side */
8335 can_fall_left = !(can_fall_right = RND(2));
8337 can_fall_both = FALSE;
8343 /* if not determined otherwise, prefer left side for slipping down */
8344 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8345 started_moving = TRUE;
8349 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8351 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8354 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8355 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8356 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8357 int belt_dir = game.belt_dir[belt_nr];
8359 if ((belt_dir == MV_LEFT && left_is_free) ||
8360 (belt_dir == MV_RIGHT && right_is_free))
8362 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8364 InitMovingField(x, y, belt_dir);
8365 started_moving = TRUE;
8367 Pushed[x][y] = TRUE;
8368 Pushed[nextx][y] = TRUE;
8370 GfxAction[x][y] = ACTION_DEFAULT;
8374 MovDir[x][y] = 0; /* if element was moving, stop it */
8379 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8381 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8383 if (CAN_MOVE(element) && !started_moving)
8386 int move_pattern = element_info[element].move_pattern;
8391 if (MovDir[x][y] == MV_NONE)
8393 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8394 x, y, element, element_info[element].token_name);
8395 printf("StartMoving(): This should never happen!\n");
8400 Moving2Blocked(x, y, &newx, &newy);
8402 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8405 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8406 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8408 WasJustMoving[x][y] = 0;
8409 CheckCollision[x][y] = 0;
8411 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8413 if (Feld[x][y] != element) /* element has changed */
8417 if (!MovDelay[x][y]) /* start new movement phase */
8419 /* all objects that can change their move direction after each step
8420 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8422 if (element != EL_YAMYAM &&
8423 element != EL_DARK_YAMYAM &&
8424 element != EL_PACMAN &&
8425 !(move_pattern & MV_ANY_DIRECTION) &&
8426 move_pattern != MV_TURNING_LEFT &&
8427 move_pattern != MV_TURNING_RIGHT &&
8428 move_pattern != MV_TURNING_LEFT_RIGHT &&
8429 move_pattern != MV_TURNING_RIGHT_LEFT &&
8430 move_pattern != MV_TURNING_RANDOM)
8434 if (MovDelay[x][y] && (element == EL_BUG ||
8435 element == EL_SPACESHIP ||
8436 element == EL_SP_SNIKSNAK ||
8437 element == EL_SP_ELECTRON ||
8438 element == EL_MOLE))
8439 TEST_DrawLevelField(x, y);
8443 if (MovDelay[x][y]) /* wait some time before next movement */
8447 if (element == EL_ROBOT ||
8448 element == EL_YAMYAM ||
8449 element == EL_DARK_YAMYAM)
8451 DrawLevelElementAnimationIfNeeded(x, y, element);
8452 PlayLevelSoundAction(x, y, ACTION_WAITING);
8454 else if (element == EL_SP_ELECTRON)
8455 DrawLevelElementAnimationIfNeeded(x, y, element);
8456 else if (element == EL_DRAGON)
8459 int dir = MovDir[x][y];
8460 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8461 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8462 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8463 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8464 dir == MV_UP ? IMG_FLAMES_1_UP :
8465 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8466 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8468 GfxAction[x][y] = ACTION_ATTACKING;
8470 if (IS_PLAYER(x, y))
8471 DrawPlayerField(x, y);
8473 TEST_DrawLevelField(x, y);
8475 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8477 for (i = 1; i <= 3; i++)
8479 int xx = x + i * dx;
8480 int yy = y + i * dy;
8481 int sx = SCREENX(xx);
8482 int sy = SCREENY(yy);
8483 int flame_graphic = graphic + (i - 1);
8485 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8490 int flamed = MovingOrBlocked2Element(xx, yy);
8494 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8496 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8497 RemoveMovingField(xx, yy);
8499 RemoveField(xx, yy);
8501 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8504 RemoveMovingField(xx, yy);
8507 ChangeDelay[xx][yy] = 0;
8509 Feld[xx][yy] = EL_FLAMES;
8511 if (IN_SCR_FIELD(sx, sy))
8513 TEST_DrawLevelFieldCrumbled(xx, yy);
8514 DrawGraphic(sx, sy, flame_graphic, frame);
8519 if (Feld[xx][yy] == EL_FLAMES)
8520 Feld[xx][yy] = EL_EMPTY;
8521 TEST_DrawLevelField(xx, yy);
8526 if (MovDelay[x][y]) /* element still has to wait some time */
8528 PlayLevelSoundAction(x, y, ACTION_WAITING);
8534 /* now make next step */
8536 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8538 if (DONT_COLLIDE_WITH(element) &&
8539 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8540 !PLAYER_ENEMY_PROTECTED(newx, newy))
8542 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8547 else if (CAN_MOVE_INTO_ACID(element) &&
8548 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8549 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8550 (MovDir[x][y] == MV_DOWN ||
8551 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8553 SplashAcid(newx, newy);
8554 Store[x][y] = EL_ACID;
8556 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8558 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8559 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8560 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8561 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8564 TEST_DrawLevelField(x, y);
8566 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8567 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8568 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8570 local_player->friends_still_needed--;
8571 if (!local_player->friends_still_needed &&
8572 !local_player->GameOver && AllPlayersGone)
8573 PlayerWins(local_player);
8577 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8579 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8580 TEST_DrawLevelField(newx, newy);
8582 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8584 else if (!IS_FREE(newx, newy))
8586 GfxAction[x][y] = ACTION_WAITING;
8588 if (IS_PLAYER(x, y))
8589 DrawPlayerField(x, y);
8591 TEST_DrawLevelField(x, y);
8596 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8598 if (IS_FOOD_PIG(Feld[newx][newy]))
8600 if (IS_MOVING(newx, newy))
8601 RemoveMovingField(newx, newy);
8604 Feld[newx][newy] = EL_EMPTY;
8605 TEST_DrawLevelField(newx, newy);
8608 PlayLevelSound(x, y, SND_PIG_DIGGING);
8610 else if (!IS_FREE(newx, newy))
8612 if (IS_PLAYER(x, y))
8613 DrawPlayerField(x, y);
8615 TEST_DrawLevelField(x, y);
8620 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8622 if (Store[x][y] != EL_EMPTY)
8624 boolean can_clone = FALSE;
8627 /* check if element to clone is still there */
8628 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8630 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8638 /* cannot clone or target field not free anymore -- do not clone */
8639 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8640 Store[x][y] = EL_EMPTY;
8643 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8645 if (IS_MV_DIAGONAL(MovDir[x][y]))
8647 int diagonal_move_dir = MovDir[x][y];
8648 int stored = Store[x][y];
8649 int change_delay = 8;
8652 /* android is moving diagonally */
8654 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8656 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8657 GfxElement[x][y] = EL_EMC_ANDROID;
8658 GfxAction[x][y] = ACTION_SHRINKING;
8659 GfxDir[x][y] = diagonal_move_dir;
8660 ChangeDelay[x][y] = change_delay;
8662 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8665 DrawLevelGraphicAnimation(x, y, graphic);
8666 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8668 if (Feld[newx][newy] == EL_ACID)
8670 SplashAcid(newx, newy);
8675 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8677 Store[newx][newy] = EL_EMC_ANDROID;
8678 GfxElement[newx][newy] = EL_EMC_ANDROID;
8679 GfxAction[newx][newy] = ACTION_GROWING;
8680 GfxDir[newx][newy] = diagonal_move_dir;
8681 ChangeDelay[newx][newy] = change_delay;
8683 graphic = el_act_dir2img(GfxElement[newx][newy],
8684 GfxAction[newx][newy], GfxDir[newx][newy]);
8686 DrawLevelGraphicAnimation(newx, newy, graphic);
8687 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8693 Feld[newx][newy] = EL_EMPTY;
8694 TEST_DrawLevelField(newx, newy);
8696 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8699 else if (!IS_FREE(newx, newy))
8702 if (IS_PLAYER(x, y))
8703 DrawPlayerField(x, y);
8705 TEST_DrawLevelField(x, y);
8711 else if (IS_CUSTOM_ELEMENT(element) &&
8712 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8715 if (!DigFieldByCE(newx, newy, element))
8718 int new_element = Feld[newx][newy];
8720 if (!IS_FREE(newx, newy))
8722 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8723 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8726 /* no element can dig solid indestructible elements */
8727 if (IS_INDESTRUCTIBLE(new_element) &&
8728 !IS_DIGGABLE(new_element) &&
8729 !IS_COLLECTIBLE(new_element))
8732 if (AmoebaNr[newx][newy] &&
8733 (new_element == EL_AMOEBA_FULL ||
8734 new_element == EL_BD_AMOEBA ||
8735 new_element == EL_AMOEBA_GROWING))
8737 AmoebaCnt[AmoebaNr[newx][newy]]--;
8738 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8741 if (IS_MOVING(newx, newy))
8742 RemoveMovingField(newx, newy);
8745 RemoveField(newx, newy);
8746 TEST_DrawLevelField(newx, newy);
8749 /* if digged element was about to explode, prevent the explosion */
8750 ExplodeField[newx][newy] = EX_TYPE_NONE;
8752 PlayLevelSoundAction(x, y, action);
8755 Store[newx][newy] = EL_EMPTY;
8758 /* this makes it possible to leave the removed element again */
8759 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8760 Store[newx][newy] = new_element;
8762 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8764 int move_leave_element = element_info[element].move_leave_element;
8766 /* this makes it possible to leave the removed element again */
8767 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8768 new_element : move_leave_element);
8774 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8776 RunnerVisit[x][y] = FrameCounter;
8777 PlayerVisit[x][y] /= 8; /* expire player visit path */
8780 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8782 if (!IS_FREE(newx, newy))
8784 if (IS_PLAYER(x, y))
8785 DrawPlayerField(x, y);
8787 TEST_DrawLevelField(x, y);
8793 boolean wanna_flame = !RND(10);
8794 int dx = newx - x, dy = newy - y;
8795 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8796 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8797 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8798 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8799 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8800 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8803 IS_CLASSIC_ENEMY(element1) ||
8804 IS_CLASSIC_ENEMY(element2)) &&
8805 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8806 element1 != EL_FLAMES && element2 != EL_FLAMES)
8808 ResetGfxAnimation(x, y);
8809 GfxAction[x][y] = ACTION_ATTACKING;
8811 if (IS_PLAYER(x, y))
8812 DrawPlayerField(x, y);
8814 TEST_DrawLevelField(x, y);
8816 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8818 MovDelay[x][y] = 50;
8822 RemoveField(newx, newy);
8824 Feld[newx][newy] = EL_FLAMES;
8825 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8828 RemoveField(newx1, newy1);
8830 Feld[newx1][newy1] = EL_FLAMES;
8832 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8835 RemoveField(newx2, newy2);
8837 Feld[newx2][newy2] = EL_FLAMES;
8844 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8845 Feld[newx][newy] == EL_DIAMOND)
8847 if (IS_MOVING(newx, newy))
8848 RemoveMovingField(newx, newy);
8851 Feld[newx][newy] = EL_EMPTY;
8852 TEST_DrawLevelField(newx, newy);
8855 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8857 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8858 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8860 if (AmoebaNr[newx][newy])
8862 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8863 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8864 Feld[newx][newy] == EL_BD_AMOEBA)
8865 AmoebaCnt[AmoebaNr[newx][newy]]--;
8870 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8872 RemoveMovingField(newx, newy);
8875 if (IS_MOVING(newx, newy))
8877 RemoveMovingField(newx, newy);
8882 Feld[newx][newy] = EL_EMPTY;
8883 TEST_DrawLevelField(newx, newy);
8886 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8888 else if ((element == EL_PACMAN || element == EL_MOLE)
8889 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8891 if (AmoebaNr[newx][newy])
8893 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8894 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8895 Feld[newx][newy] == EL_BD_AMOEBA)
8896 AmoebaCnt[AmoebaNr[newx][newy]]--;
8899 if (element == EL_MOLE)
8901 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8902 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8904 ResetGfxAnimation(x, y);
8905 GfxAction[x][y] = ACTION_DIGGING;
8906 TEST_DrawLevelField(x, y);
8908 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8910 return; /* wait for shrinking amoeba */
8912 else /* element == EL_PACMAN */
8914 Feld[newx][newy] = EL_EMPTY;
8915 TEST_DrawLevelField(newx, newy);
8916 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8919 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8920 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8921 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8923 /* wait for shrinking amoeba to completely disappear */
8926 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8928 /* object was running against a wall */
8933 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8934 if (move_pattern & MV_ANY_DIRECTION &&
8935 move_pattern == MovDir[x][y])
8937 int blocking_element =
8938 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8940 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8943 element = Feld[x][y]; /* element might have changed */
8947 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8948 DrawLevelElementAnimation(x, y, element);
8950 if (DONT_TOUCH(element))
8951 TestIfBadThingTouchesPlayer(x, y);
8956 InitMovingField(x, y, MovDir[x][y]);
8958 PlayLevelSoundAction(x, y, ACTION_MOVING);
8962 ContinueMoving(x, y);
8965 void ContinueMoving(int x, int y)
8967 int element = Feld[x][y];
8968 struct ElementInfo *ei = &element_info[element];
8969 int direction = MovDir[x][y];
8970 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8971 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8972 int newx = x + dx, newy = y + dy;
8973 int stored = Store[x][y];
8974 int stored_new = Store[newx][newy];
8975 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8976 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8977 boolean last_line = (newy == lev_fieldy - 1);
8979 MovPos[x][y] += getElementMoveStepsize(x, y);
8981 if (pushed_by_player) /* special case: moving object pushed by player */
8982 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8984 if (ABS(MovPos[x][y]) < TILEX)
8987 int ee = Feld[x][y];
8988 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8989 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8991 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8992 x, y, ABS(MovPos[x][y]),
8994 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8997 TEST_DrawLevelField(x, y);
8999 return; /* element is still moving */
9002 /* element reached destination field */
9004 Feld[x][y] = EL_EMPTY;
9005 Feld[newx][newy] = element;
9006 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9008 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9010 element = Feld[newx][newy] = EL_ACID;
9012 else if (element == EL_MOLE)
9014 Feld[x][y] = EL_SAND;
9016 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9018 else if (element == EL_QUICKSAND_FILLING)
9020 element = Feld[newx][newy] = get_next_element(element);
9021 Store[newx][newy] = Store[x][y];
9023 else if (element == EL_QUICKSAND_EMPTYING)
9025 Feld[x][y] = get_next_element(element);
9026 element = Feld[newx][newy] = Store[x][y];
9028 else if (element == EL_QUICKSAND_FAST_FILLING)
9030 element = Feld[newx][newy] = get_next_element(element);
9031 Store[newx][newy] = Store[x][y];
9033 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9035 Feld[x][y] = get_next_element(element);
9036 element = Feld[newx][newy] = Store[x][y];
9038 else if (element == EL_MAGIC_WALL_FILLING)
9040 element = Feld[newx][newy] = get_next_element(element);
9041 if (!game.magic_wall_active)
9042 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9043 Store[newx][newy] = Store[x][y];
9045 else if (element == EL_MAGIC_WALL_EMPTYING)
9047 Feld[x][y] = get_next_element(element);
9048 if (!game.magic_wall_active)
9049 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9050 element = Feld[newx][newy] = Store[x][y];
9052 #if USE_NEW_CUSTOM_VALUE
9053 InitField(newx, newy, FALSE);
9056 else if (element == EL_BD_MAGIC_WALL_FILLING)
9058 element = Feld[newx][newy] = get_next_element(element);
9059 if (!game.magic_wall_active)
9060 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9061 Store[newx][newy] = Store[x][y];
9063 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9065 Feld[x][y] = get_next_element(element);
9066 if (!game.magic_wall_active)
9067 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9068 element = Feld[newx][newy] = Store[x][y];
9070 #if USE_NEW_CUSTOM_VALUE
9071 InitField(newx, newy, FALSE);
9074 else if (element == EL_DC_MAGIC_WALL_FILLING)
9076 element = Feld[newx][newy] = get_next_element(element);
9077 if (!game.magic_wall_active)
9078 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9079 Store[newx][newy] = Store[x][y];
9081 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9083 Feld[x][y] = get_next_element(element);
9084 if (!game.magic_wall_active)
9085 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9086 element = Feld[newx][newy] = Store[x][y];
9088 #if USE_NEW_CUSTOM_VALUE
9089 InitField(newx, newy, FALSE);
9092 else if (element == EL_AMOEBA_DROPPING)
9094 Feld[x][y] = get_next_element(element);
9095 element = Feld[newx][newy] = Store[x][y];
9097 else if (element == EL_SOKOBAN_OBJECT)
9100 Feld[x][y] = Back[x][y];
9102 if (Back[newx][newy])
9103 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9105 Back[x][y] = Back[newx][newy] = 0;
9108 Store[x][y] = EL_EMPTY;
9113 MovDelay[newx][newy] = 0;
9115 if (CAN_CHANGE_OR_HAS_ACTION(element))
9117 /* copy element change control values to new field */
9118 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9119 ChangePage[newx][newy] = ChangePage[x][y];
9120 ChangeCount[newx][newy] = ChangeCount[x][y];
9121 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9124 #if USE_NEW_CUSTOM_VALUE
9125 CustomValue[newx][newy] = CustomValue[x][y];
9128 ChangeDelay[x][y] = 0;
9129 ChangePage[x][y] = -1;
9130 ChangeCount[x][y] = 0;
9131 ChangeEvent[x][y] = -1;
9133 #if USE_NEW_CUSTOM_VALUE
9134 CustomValue[x][y] = 0;
9137 /* copy animation control values to new field */
9138 GfxFrame[newx][newy] = GfxFrame[x][y];
9139 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9140 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9141 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9143 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9145 /* some elements can leave other elements behind after moving */
9147 if (ei->move_leave_element != EL_EMPTY &&
9148 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9149 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9151 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9152 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9153 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9156 int move_leave_element = ei->move_leave_element;
9160 /* this makes it possible to leave the removed element again */
9161 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9162 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9164 /* this makes it possible to leave the removed element again */
9165 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9166 move_leave_element = stored;
9169 /* this makes it possible to leave the removed element again */
9170 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9171 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9172 move_leave_element = stored;
9175 Feld[x][y] = move_leave_element;
9177 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9178 MovDir[x][y] = direction;
9180 InitField(x, y, FALSE);
9182 if (GFX_CRUMBLED(Feld[x][y]))
9183 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9185 if (ELEM_IS_PLAYER(move_leave_element))
9186 RelocatePlayer(x, y, move_leave_element);
9189 /* do this after checking for left-behind element */
9190 ResetGfxAnimation(x, y); /* reset animation values for old field */
9192 if (!CAN_MOVE(element) ||
9193 (CAN_FALL(element) && direction == MV_DOWN &&
9194 (element == EL_SPRING ||
9195 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9196 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9197 GfxDir[x][y] = MovDir[newx][newy] = 0;
9199 TEST_DrawLevelField(x, y);
9200 TEST_DrawLevelField(newx, newy);
9202 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9204 /* prevent pushed element from moving on in pushed direction */
9205 if (pushed_by_player && CAN_MOVE(element) &&
9206 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9207 !(element_info[element].move_pattern & direction))
9208 TurnRound(newx, newy);
9210 /* prevent elements on conveyor belt from moving on in last direction */
9211 if (pushed_by_conveyor && CAN_FALL(element) &&
9212 direction & MV_HORIZONTAL)
9213 MovDir[newx][newy] = 0;
9215 if (!pushed_by_player)
9217 int nextx = newx + dx, nexty = newy + dy;
9218 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9220 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9222 if (CAN_FALL(element) && direction == MV_DOWN)
9223 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9225 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9226 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9228 #if USE_FIX_IMPACT_COLLISION
9229 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9230 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9234 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9236 TestIfBadThingTouchesPlayer(newx, newy);
9237 TestIfBadThingTouchesFriend(newx, newy);
9239 if (!IS_CUSTOM_ELEMENT(element))
9240 TestIfBadThingTouchesOtherBadThing(newx, newy);
9242 else if (element == EL_PENGUIN)
9243 TestIfFriendTouchesBadThing(newx, newy);
9245 if (DONT_GET_HIT_BY(element))
9247 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9250 /* give the player one last chance (one more frame) to move away */
9251 if (CAN_FALL(element) && direction == MV_DOWN &&
9252 (last_line || (!IS_FREE(x, newy + 1) &&
9253 (!IS_PLAYER(x, newy + 1) ||
9254 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9257 if (pushed_by_player && !game.use_change_when_pushing_bug)
9259 int push_side = MV_DIR_OPPOSITE(direction);
9260 struct PlayerInfo *player = PLAYERINFO(x, y);
9262 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9263 player->index_bit, push_side);
9264 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9265 player->index_bit, push_side);
9268 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9269 MovDelay[newx][newy] = 1;
9271 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9273 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9276 if (ChangePage[newx][newy] != -1) /* delayed change */
9278 int page = ChangePage[newx][newy];
9279 struct ElementChangeInfo *change = &ei->change_page[page];
9281 ChangePage[newx][newy] = -1;
9283 if (change->can_change)
9285 if (ChangeElement(newx, newy, element, page))
9287 if (change->post_change_function)
9288 change->post_change_function(newx, newy);
9292 if (change->has_action)
9293 ExecuteCustomElementAction(newx, newy, element, page);
9297 TestIfElementHitsCustomElement(newx, newy, direction);
9298 TestIfPlayerTouchesCustomElement(newx, newy);
9299 TestIfElementTouchesCustomElement(newx, newy);
9301 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9302 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9303 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9304 MV_DIR_OPPOSITE(direction));
9307 int AmoebeNachbarNr(int ax, int ay)
9310 int element = Feld[ax][ay];
9312 static int xy[4][2] =
9320 for (i = 0; i < NUM_DIRECTIONS; i++)
9322 int x = ax + xy[i][0];
9323 int y = ay + xy[i][1];
9325 if (!IN_LEV_FIELD(x, y))
9328 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9329 group_nr = AmoebaNr[x][y];
9335 void AmoebenVereinigen(int ax, int ay)
9337 int i, x, y, xx, yy;
9338 int new_group_nr = AmoebaNr[ax][ay];
9339 static int xy[4][2] =
9347 if (new_group_nr == 0)
9350 for (i = 0; i < NUM_DIRECTIONS; i++)
9355 if (!IN_LEV_FIELD(x, y))
9358 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9359 Feld[x][y] == EL_BD_AMOEBA ||
9360 Feld[x][y] == EL_AMOEBA_DEAD) &&
9361 AmoebaNr[x][y] != new_group_nr)
9363 int old_group_nr = AmoebaNr[x][y];
9365 if (old_group_nr == 0)
9368 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9369 AmoebaCnt[old_group_nr] = 0;
9370 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9371 AmoebaCnt2[old_group_nr] = 0;
9373 SCAN_PLAYFIELD(xx, yy)
9375 if (AmoebaNr[xx][yy] == old_group_nr)
9376 AmoebaNr[xx][yy] = new_group_nr;
9382 void AmoebeUmwandeln(int ax, int ay)
9386 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9388 int group_nr = AmoebaNr[ax][ay];
9393 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9394 printf("AmoebeUmwandeln(): This should never happen!\n");
9399 SCAN_PLAYFIELD(x, y)
9401 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9404 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9408 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9409 SND_AMOEBA_TURNING_TO_GEM :
9410 SND_AMOEBA_TURNING_TO_ROCK));
9415 static int xy[4][2] =
9423 for (i = 0; i < NUM_DIRECTIONS; i++)
9428 if (!IN_LEV_FIELD(x, y))
9431 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9433 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9434 SND_AMOEBA_TURNING_TO_GEM :
9435 SND_AMOEBA_TURNING_TO_ROCK));
9442 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9445 int group_nr = AmoebaNr[ax][ay];
9446 boolean done = FALSE;
9451 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9452 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9457 SCAN_PLAYFIELD(x, y)
9459 if (AmoebaNr[x][y] == group_nr &&
9460 (Feld[x][y] == EL_AMOEBA_DEAD ||
9461 Feld[x][y] == EL_BD_AMOEBA ||
9462 Feld[x][y] == EL_AMOEBA_GROWING))
9465 Feld[x][y] = new_element;
9466 InitField(x, y, FALSE);
9467 TEST_DrawLevelField(x, y);
9473 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9474 SND_BD_AMOEBA_TURNING_TO_ROCK :
9475 SND_BD_AMOEBA_TURNING_TO_GEM));
9478 void AmoebeWaechst(int x, int y)
9480 static unsigned int sound_delay = 0;
9481 static unsigned int sound_delay_value = 0;
9483 if (!MovDelay[x][y]) /* start new growing cycle */
9487 if (DelayReached(&sound_delay, sound_delay_value))
9489 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9490 sound_delay_value = 30;
9494 if (MovDelay[x][y]) /* wait some time before growing bigger */
9497 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9499 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9500 6 - MovDelay[x][y]);
9502 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9505 if (!MovDelay[x][y])
9507 Feld[x][y] = Store[x][y];
9509 TEST_DrawLevelField(x, y);
9514 void AmoebaDisappearing(int x, int y)
9516 static unsigned int sound_delay = 0;
9517 static unsigned int sound_delay_value = 0;
9519 if (!MovDelay[x][y]) /* start new shrinking cycle */
9523 if (DelayReached(&sound_delay, sound_delay_value))
9524 sound_delay_value = 30;
9527 if (MovDelay[x][y]) /* wait some time before shrinking */
9530 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9532 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9533 6 - MovDelay[x][y]);
9535 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9538 if (!MovDelay[x][y])
9540 Feld[x][y] = EL_EMPTY;
9541 TEST_DrawLevelField(x, y);
9543 /* don't let mole enter this field in this cycle;
9544 (give priority to objects falling to this field from above) */
9550 void AmoebeAbleger(int ax, int ay)
9553 int element = Feld[ax][ay];
9554 int graphic = el2img(element);
9555 int newax = ax, neway = ay;
9556 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9557 static int xy[4][2] =
9565 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9567 Feld[ax][ay] = EL_AMOEBA_DEAD;
9568 TEST_DrawLevelField(ax, ay);
9572 if (IS_ANIMATED(graphic))
9573 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9575 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9576 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9578 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9581 if (MovDelay[ax][ay])
9585 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9588 int x = ax + xy[start][0];
9589 int y = ay + xy[start][1];
9591 if (!IN_LEV_FIELD(x, y))
9594 if (IS_FREE(x, y) ||
9595 CAN_GROW_INTO(Feld[x][y]) ||
9596 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9597 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9603 if (newax == ax && neway == ay)
9606 else /* normal or "filled" (BD style) amoeba */
9609 boolean waiting_for_player = FALSE;
9611 for (i = 0; i < NUM_DIRECTIONS; i++)
9613 int j = (start + i) % 4;
9614 int x = ax + xy[j][0];
9615 int y = ay + xy[j][1];
9617 if (!IN_LEV_FIELD(x, y))
9620 if (IS_FREE(x, y) ||
9621 CAN_GROW_INTO(Feld[x][y]) ||
9622 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9623 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9629 else if (IS_PLAYER(x, y))
9630 waiting_for_player = TRUE;
9633 if (newax == ax && neway == ay) /* amoeba cannot grow */
9635 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9637 Feld[ax][ay] = EL_AMOEBA_DEAD;
9638 TEST_DrawLevelField(ax, ay);
9639 AmoebaCnt[AmoebaNr[ax][ay]]--;
9641 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9643 if (element == EL_AMOEBA_FULL)
9644 AmoebeUmwandeln(ax, ay);
9645 else if (element == EL_BD_AMOEBA)
9646 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9651 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9653 /* amoeba gets larger by growing in some direction */
9655 int new_group_nr = AmoebaNr[ax][ay];
9658 if (new_group_nr == 0)
9660 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9661 printf("AmoebeAbleger(): This should never happen!\n");
9666 AmoebaNr[newax][neway] = new_group_nr;
9667 AmoebaCnt[new_group_nr]++;
9668 AmoebaCnt2[new_group_nr]++;
9670 /* if amoeba touches other amoeba(s) after growing, unify them */
9671 AmoebenVereinigen(newax, neway);
9673 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9675 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9681 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9682 (neway == lev_fieldy - 1 && newax != ax))
9684 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9685 Store[newax][neway] = element;
9687 else if (neway == ay || element == EL_EMC_DRIPPER)
9689 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9691 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9695 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9696 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9697 Store[ax][ay] = EL_AMOEBA_DROP;
9698 ContinueMoving(ax, ay);
9702 TEST_DrawLevelField(newax, neway);
9705 void Life(int ax, int ay)
9709 int element = Feld[ax][ay];
9710 int graphic = el2img(element);
9711 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9713 boolean changed = FALSE;
9715 if (IS_ANIMATED(graphic))
9716 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9721 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9722 MovDelay[ax][ay] = life_time;
9724 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9727 if (MovDelay[ax][ay])
9731 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9733 int xx = ax+x1, yy = ay+y1;
9736 if (!IN_LEV_FIELD(xx, yy))
9739 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9741 int x = xx+x2, y = yy+y2;
9743 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9746 if (((Feld[x][y] == element ||
9747 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9749 (IS_FREE(x, y) && Stop[x][y]))
9753 if (xx == ax && yy == ay) /* field in the middle */
9755 if (nachbarn < life_parameter[0] ||
9756 nachbarn > life_parameter[1])
9758 Feld[xx][yy] = EL_EMPTY;
9760 TEST_DrawLevelField(xx, yy);
9761 Stop[xx][yy] = TRUE;
9765 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9766 { /* free border field */
9767 if (nachbarn >= life_parameter[2] &&
9768 nachbarn <= life_parameter[3])
9770 Feld[xx][yy] = element;
9771 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9773 TEST_DrawLevelField(xx, yy);
9774 Stop[xx][yy] = TRUE;
9781 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9782 SND_GAME_OF_LIFE_GROWING);
9785 static void InitRobotWheel(int x, int y)
9787 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9790 static void RunRobotWheel(int x, int y)
9792 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9795 static void StopRobotWheel(int x, int y)
9797 if (ZX == x && ZY == y)
9801 game.robot_wheel_active = FALSE;
9805 static void InitTimegateWheel(int x, int y)
9807 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9810 static void RunTimegateWheel(int x, int y)
9812 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9815 static void InitMagicBallDelay(int x, int y)
9818 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9820 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9824 static void ActivateMagicBall(int bx, int by)
9828 if (level.ball_random)
9830 int pos_border = RND(8); /* select one of the eight border elements */
9831 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9832 int xx = pos_content % 3;
9833 int yy = pos_content / 3;
9838 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9839 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9843 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9845 int xx = x - bx + 1;
9846 int yy = y - by + 1;
9848 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9849 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9853 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9856 void CheckExit(int x, int y)
9858 if (local_player->gems_still_needed > 0 ||
9859 local_player->sokobanfields_still_needed > 0 ||
9860 local_player->lights_still_needed > 0)
9862 int element = Feld[x][y];
9863 int graphic = el2img(element);
9865 if (IS_ANIMATED(graphic))
9866 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9871 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9874 Feld[x][y] = EL_EXIT_OPENING;
9876 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9879 void CheckExitEM(int x, int y)
9881 if (local_player->gems_still_needed > 0 ||
9882 local_player->sokobanfields_still_needed > 0 ||
9883 local_player->lights_still_needed > 0)
9885 int element = Feld[x][y];
9886 int graphic = el2img(element);
9888 if (IS_ANIMATED(graphic))
9889 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9894 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9897 Feld[x][y] = EL_EM_EXIT_OPENING;
9899 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9902 void CheckExitSteel(int x, int y)
9904 if (local_player->gems_still_needed > 0 ||
9905 local_player->sokobanfields_still_needed > 0 ||
9906 local_player->lights_still_needed > 0)
9908 int element = Feld[x][y];
9909 int graphic = el2img(element);
9911 if (IS_ANIMATED(graphic))
9912 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9917 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9920 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9922 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9925 void CheckExitSteelEM(int x, int y)
9927 if (local_player->gems_still_needed > 0 ||
9928 local_player->sokobanfields_still_needed > 0 ||
9929 local_player->lights_still_needed > 0)
9931 int element = Feld[x][y];
9932 int graphic = el2img(element);
9934 if (IS_ANIMATED(graphic))
9935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9940 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9943 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9945 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9948 void CheckExitSP(int x, int y)
9950 if (local_player->gems_still_needed > 0)
9952 int element = Feld[x][y];
9953 int graphic = el2img(element);
9955 if (IS_ANIMATED(graphic))
9956 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9961 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9964 Feld[x][y] = EL_SP_EXIT_OPENING;
9966 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9969 static void CloseAllOpenTimegates()
9973 SCAN_PLAYFIELD(x, y)
9975 int element = Feld[x][y];
9977 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9979 Feld[x][y] = EL_TIMEGATE_CLOSING;
9981 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9986 void DrawTwinkleOnField(int x, int y)
9988 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9991 if (Feld[x][y] == EL_BD_DIAMOND)
9994 if (MovDelay[x][y] == 0) /* next animation frame */
9995 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9997 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10001 DrawLevelElementAnimation(x, y, Feld[x][y]);
10003 if (MovDelay[x][y] != 0)
10005 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10006 10 - MovDelay[x][y]);
10008 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10013 void MauerWaechst(int x, int y)
10017 if (!MovDelay[x][y]) /* next animation frame */
10018 MovDelay[x][y] = 3 * delay;
10020 if (MovDelay[x][y]) /* wait some time before next frame */
10024 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10026 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10027 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10029 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10032 if (!MovDelay[x][y])
10034 if (MovDir[x][y] == MV_LEFT)
10036 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10037 TEST_DrawLevelField(x - 1, y);
10039 else if (MovDir[x][y] == MV_RIGHT)
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_UP)
10046 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10047 TEST_DrawLevelField(x, y - 1);
10051 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10052 TEST_DrawLevelField(x, y + 1);
10055 Feld[x][y] = Store[x][y];
10057 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10058 TEST_DrawLevelField(x, y);
10063 void MauerAbleger(int ax, int ay)
10065 int element = Feld[ax][ay];
10066 int graphic = el2img(element);
10067 boolean oben_frei = FALSE, unten_frei = FALSE;
10068 boolean links_frei = FALSE, rechts_frei = FALSE;
10069 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10070 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10071 boolean new_wall = FALSE;
10073 if (IS_ANIMATED(graphic))
10074 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10076 if (!MovDelay[ax][ay]) /* start building new wall */
10077 MovDelay[ax][ay] = 6;
10079 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10081 MovDelay[ax][ay]--;
10082 if (MovDelay[ax][ay])
10086 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10088 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10090 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10092 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10093 rechts_frei = TRUE;
10095 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10096 element == EL_EXPANDABLE_WALL_ANY)
10100 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10101 Store[ax][ay-1] = element;
10102 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10103 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10104 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10105 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10110 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10111 Store[ax][ay+1] = element;
10112 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10113 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10114 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10115 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10120 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10121 element == EL_EXPANDABLE_WALL_ANY ||
10122 element == EL_EXPANDABLE_WALL ||
10123 element == EL_BD_EXPANDABLE_WALL)
10127 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10128 Store[ax-1][ay] = element;
10129 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10130 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10131 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10132 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10138 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10139 Store[ax+1][ay] = element;
10140 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10141 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10142 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10143 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10148 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10149 TEST_DrawLevelField(ax, ay);
10151 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10152 oben_massiv = TRUE;
10153 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10154 unten_massiv = TRUE;
10155 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10156 links_massiv = TRUE;
10157 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10158 rechts_massiv = TRUE;
10160 if (((oben_massiv && unten_massiv) ||
10161 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10162 element == EL_EXPANDABLE_WALL) &&
10163 ((links_massiv && rechts_massiv) ||
10164 element == EL_EXPANDABLE_WALL_VERTICAL))
10165 Feld[ax][ay] = EL_WALL;
10168 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10171 void MauerAblegerStahl(int ax, int ay)
10173 int element = Feld[ax][ay];
10174 int graphic = el2img(element);
10175 boolean oben_frei = FALSE, unten_frei = FALSE;
10176 boolean links_frei = FALSE, rechts_frei = FALSE;
10177 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10178 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10179 boolean new_wall = FALSE;
10181 if (IS_ANIMATED(graphic))
10182 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10184 if (!MovDelay[ax][ay]) /* start building new wall */
10185 MovDelay[ax][ay] = 6;
10187 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10189 MovDelay[ax][ay]--;
10190 if (MovDelay[ax][ay])
10194 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10196 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10198 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10200 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10201 rechts_frei = TRUE;
10203 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10204 element == EL_EXPANDABLE_STEELWALL_ANY)
10208 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10209 Store[ax][ay-1] = element;
10210 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10211 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10212 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10213 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10218 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10219 Store[ax][ay+1] = element;
10220 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10221 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10222 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10223 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10228 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10229 element == EL_EXPANDABLE_STEELWALL_ANY)
10233 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10234 Store[ax-1][ay] = element;
10235 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10236 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10237 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10238 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10244 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10245 Store[ax+1][ay] = element;
10246 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10247 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10248 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10249 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10254 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10255 oben_massiv = TRUE;
10256 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10257 unten_massiv = TRUE;
10258 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10259 links_massiv = TRUE;
10260 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10261 rechts_massiv = TRUE;
10263 if (((oben_massiv && unten_massiv) ||
10264 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10265 ((links_massiv && rechts_massiv) ||
10266 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10267 Feld[ax][ay] = EL_STEELWALL;
10270 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10273 void CheckForDragon(int x, int y)
10276 boolean dragon_found = FALSE;
10277 static int xy[4][2] =
10285 for (i = 0; i < NUM_DIRECTIONS; i++)
10287 for (j = 0; j < 4; j++)
10289 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10291 if (IN_LEV_FIELD(xx, yy) &&
10292 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10294 if (Feld[xx][yy] == EL_DRAGON)
10295 dragon_found = TRUE;
10304 for (i = 0; i < NUM_DIRECTIONS; i++)
10306 for (j = 0; j < 3; j++)
10308 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10310 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10312 Feld[xx][yy] = EL_EMPTY;
10313 TEST_DrawLevelField(xx, yy);
10322 static void InitBuggyBase(int x, int y)
10324 int element = Feld[x][y];
10325 int activating_delay = FRAMES_PER_SECOND / 4;
10327 ChangeDelay[x][y] =
10328 (element == EL_SP_BUGGY_BASE ?
10329 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10330 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10332 element == EL_SP_BUGGY_BASE_ACTIVE ?
10333 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10336 static void WarnBuggyBase(int x, int y)
10339 static int xy[4][2] =
10347 for (i = 0; i < NUM_DIRECTIONS; i++)
10349 int xx = x + xy[i][0];
10350 int yy = y + xy[i][1];
10352 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10354 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10361 static void InitTrap(int x, int y)
10363 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10366 static void ActivateTrap(int x, int y)
10368 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10371 static void ChangeActiveTrap(int x, int y)
10373 int graphic = IMG_TRAP_ACTIVE;
10375 /* if new animation frame was drawn, correct crumbled sand border */
10376 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10377 TEST_DrawLevelFieldCrumbled(x, y);
10380 static int getSpecialActionElement(int element, int number, int base_element)
10382 return (element != EL_EMPTY ? element :
10383 number != -1 ? base_element + number - 1 :
10387 static int getModifiedActionNumber(int value_old, int operator, int operand,
10388 int value_min, int value_max)
10390 int value_new = (operator == CA_MODE_SET ? operand :
10391 operator == CA_MODE_ADD ? value_old + operand :
10392 operator == CA_MODE_SUBTRACT ? value_old - operand :
10393 operator == CA_MODE_MULTIPLY ? value_old * operand :
10394 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10395 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10398 return (value_new < value_min ? value_min :
10399 value_new > value_max ? value_max :
10403 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10405 struct ElementInfo *ei = &element_info[element];
10406 struct ElementChangeInfo *change = &ei->change_page[page];
10407 int target_element = change->target_element;
10408 int action_type = change->action_type;
10409 int action_mode = change->action_mode;
10410 int action_arg = change->action_arg;
10411 int action_element = change->action_element;
10414 if (!change->has_action)
10417 /* ---------- determine action paramater values -------------------------- */
10419 int level_time_value =
10420 (level.time > 0 ? TimeLeft :
10423 int action_arg_element_raw =
10424 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10425 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10426 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10427 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10428 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10429 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10430 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10432 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10435 if (action_arg_element_raw == EL_GROUP_START)
10436 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10439 int action_arg_direction =
10440 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10441 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10442 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10443 change->actual_trigger_side :
10444 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10445 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10448 int action_arg_number_min =
10449 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10452 int action_arg_number_max =
10453 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10454 action_type == CA_SET_LEVEL_GEMS ? 999 :
10455 action_type == CA_SET_LEVEL_TIME ? 9999 :
10456 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10457 action_type == CA_SET_CE_VALUE ? 9999 :
10458 action_type == CA_SET_CE_SCORE ? 9999 :
10461 int action_arg_number_reset =
10462 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10463 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10464 action_type == CA_SET_LEVEL_TIME ? level.time :
10465 action_type == CA_SET_LEVEL_SCORE ? 0 :
10466 #if USE_NEW_CUSTOM_VALUE
10467 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10469 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10471 action_type == CA_SET_CE_SCORE ? 0 :
10474 int action_arg_number =
10475 (action_arg <= CA_ARG_MAX ? action_arg :
10476 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10477 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10478 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10479 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10480 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10481 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10482 #if USE_NEW_CUSTOM_VALUE
10483 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10485 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10487 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10488 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10489 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10490 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10491 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10492 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10493 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10494 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10495 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10496 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10497 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10498 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10499 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10500 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10503 int action_arg_number_old =
10504 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10505 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10506 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10507 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10508 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10511 int action_arg_number_new =
10512 getModifiedActionNumber(action_arg_number_old,
10513 action_mode, action_arg_number,
10514 action_arg_number_min, action_arg_number_max);
10517 int trigger_player_bits =
10518 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10519 change->actual_trigger_player_bits : change->trigger_player);
10521 int trigger_player_bits =
10522 (change->actual_trigger_player >= EL_PLAYER_1 &&
10523 change->actual_trigger_player <= EL_PLAYER_4 ?
10524 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10528 int action_arg_player_bits =
10529 (action_arg >= CA_ARG_PLAYER_1 &&
10530 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10531 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10532 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10535 /* ---------- execute action -------------------------------------------- */
10537 switch (action_type)
10544 /* ---------- level actions ------------------------------------------- */
10546 case CA_RESTART_LEVEL:
10548 game.restart_level = TRUE;
10553 case CA_SHOW_ENVELOPE:
10555 int element = getSpecialActionElement(action_arg_element,
10556 action_arg_number, EL_ENVELOPE_1);
10558 if (IS_ENVELOPE(element))
10559 local_player->show_envelope = element;
10564 case CA_SET_LEVEL_TIME:
10566 if (level.time > 0) /* only modify limited time value */
10568 TimeLeft = action_arg_number_new;
10571 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10573 DisplayGameControlValues();
10575 DrawGameValue_Time(TimeLeft);
10578 if (!TimeLeft && setup.time_limit)
10579 for (i = 0; i < MAX_PLAYERS; i++)
10580 KillPlayer(&stored_player[i]);
10586 case CA_SET_LEVEL_SCORE:
10588 local_player->score = action_arg_number_new;
10591 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10593 DisplayGameControlValues();
10595 DrawGameValue_Score(local_player->score);
10601 case CA_SET_LEVEL_GEMS:
10603 local_player->gems_still_needed = action_arg_number_new;
10606 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10608 DisplayGameControlValues();
10610 DrawGameValue_Emeralds(local_player->gems_still_needed);
10616 #if !USE_PLAYER_GRAVITY
10617 case CA_SET_LEVEL_GRAVITY:
10619 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10620 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10621 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10627 case CA_SET_LEVEL_WIND:
10629 game.wind_direction = action_arg_direction;
10634 case CA_SET_LEVEL_RANDOM_SEED:
10637 /* ensure that setting a new random seed while playing is predictable */
10638 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10640 InitRND(action_arg_number_new);
10644 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10652 for (i = 0; i < 9; i++)
10653 printf("%d, ", RND(2));
10661 /* ---------- player actions ------------------------------------------ */
10663 case CA_MOVE_PLAYER:
10665 /* automatically move to the next field in specified direction */
10666 for (i = 0; i < MAX_PLAYERS; i++)
10667 if (trigger_player_bits & (1 << i))
10668 stored_player[i].programmed_action = action_arg_direction;
10673 case CA_EXIT_PLAYER:
10675 for (i = 0; i < MAX_PLAYERS; i++)
10676 if (action_arg_player_bits & (1 << i))
10677 PlayerWins(&stored_player[i]);
10682 case CA_KILL_PLAYER:
10684 for (i = 0; i < MAX_PLAYERS; i++)
10685 if (action_arg_player_bits & (1 << i))
10686 KillPlayer(&stored_player[i]);
10691 case CA_SET_PLAYER_KEYS:
10693 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10694 int element = getSpecialActionElement(action_arg_element,
10695 action_arg_number, EL_KEY_1);
10697 if (IS_KEY(element))
10699 for (i = 0; i < MAX_PLAYERS; i++)
10701 if (trigger_player_bits & (1 << i))
10703 stored_player[i].key[KEY_NR(element)] = key_state;
10705 DrawGameDoorValues();
10713 case CA_SET_PLAYER_SPEED:
10716 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10719 for (i = 0; i < MAX_PLAYERS; i++)
10721 if (trigger_player_bits & (1 << i))
10723 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10725 if (action_arg == CA_ARG_SPEED_FASTER &&
10726 stored_player[i].cannot_move)
10728 action_arg_number = STEPSIZE_VERY_SLOW;
10730 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10731 action_arg == CA_ARG_SPEED_FASTER)
10733 action_arg_number = 2;
10734 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10737 else if (action_arg == CA_ARG_NUMBER_RESET)
10739 action_arg_number = level.initial_player_stepsize[i];
10743 getModifiedActionNumber(move_stepsize,
10746 action_arg_number_min,
10747 action_arg_number_max);
10749 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10756 case CA_SET_PLAYER_SHIELD:
10758 for (i = 0; i < MAX_PLAYERS; i++)
10760 if (trigger_player_bits & (1 << i))
10762 if (action_arg == CA_ARG_SHIELD_OFF)
10764 stored_player[i].shield_normal_time_left = 0;
10765 stored_player[i].shield_deadly_time_left = 0;
10767 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10769 stored_player[i].shield_normal_time_left = 999999;
10771 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10773 stored_player[i].shield_normal_time_left = 999999;
10774 stored_player[i].shield_deadly_time_left = 999999;
10782 #if USE_PLAYER_GRAVITY
10783 case CA_SET_PLAYER_GRAVITY:
10785 for (i = 0; i < MAX_PLAYERS; i++)
10787 if (trigger_player_bits & (1 << i))
10789 stored_player[i].gravity =
10790 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10791 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10792 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10793 stored_player[i].gravity);
10801 case CA_SET_PLAYER_ARTWORK:
10803 for (i = 0; i < MAX_PLAYERS; i++)
10805 if (trigger_player_bits & (1 << i))
10807 int artwork_element = action_arg_element;
10809 if (action_arg == CA_ARG_ELEMENT_RESET)
10811 (level.use_artwork_element[i] ? level.artwork_element[i] :
10812 stored_player[i].element_nr);
10814 #if USE_GFX_RESET_PLAYER_ARTWORK
10815 if (stored_player[i].artwork_element != artwork_element)
10816 stored_player[i].Frame = 0;
10819 stored_player[i].artwork_element = artwork_element;
10821 SetPlayerWaiting(&stored_player[i], FALSE);
10823 /* set number of special actions for bored and sleeping animation */
10824 stored_player[i].num_special_action_bored =
10825 get_num_special_action(artwork_element,
10826 ACTION_BORING_1, ACTION_BORING_LAST);
10827 stored_player[i].num_special_action_sleeping =
10828 get_num_special_action(artwork_element,
10829 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10836 case CA_SET_PLAYER_INVENTORY:
10838 for (i = 0; i < MAX_PLAYERS; i++)
10840 struct PlayerInfo *player = &stored_player[i];
10843 if (trigger_player_bits & (1 << i))
10845 int inventory_element = action_arg_element;
10847 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10848 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10849 action_arg == CA_ARG_ELEMENT_ACTION)
10851 int element = inventory_element;
10852 int collect_count = element_info[element].collect_count_initial;
10854 if (!IS_CUSTOM_ELEMENT(element))
10857 if (collect_count == 0)
10858 player->inventory_infinite_element = element;
10860 for (k = 0; k < collect_count; k++)
10861 if (player->inventory_size < MAX_INVENTORY_SIZE)
10862 player->inventory_element[player->inventory_size++] =
10865 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10866 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10867 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10869 if (player->inventory_infinite_element != EL_UNDEFINED &&
10870 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10871 action_arg_element_raw))
10872 player->inventory_infinite_element = EL_UNDEFINED;
10874 for (k = 0, j = 0; j < player->inventory_size; j++)
10876 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10877 action_arg_element_raw))
10878 player->inventory_element[k++] = player->inventory_element[j];
10881 player->inventory_size = k;
10883 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10885 if (player->inventory_size > 0)
10887 for (j = 0; j < player->inventory_size - 1; j++)
10888 player->inventory_element[j] = player->inventory_element[j + 1];
10890 player->inventory_size--;
10893 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10895 if (player->inventory_size > 0)
10896 player->inventory_size--;
10898 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10900 player->inventory_infinite_element = EL_UNDEFINED;
10901 player->inventory_size = 0;
10903 else if (action_arg == CA_ARG_INVENTORY_RESET)
10905 player->inventory_infinite_element = EL_UNDEFINED;
10906 player->inventory_size = 0;
10908 if (level.use_initial_inventory[i])
10910 for (j = 0; j < level.initial_inventory_size[i]; j++)
10912 int element = level.initial_inventory_content[i][j];
10913 int collect_count = element_info[element].collect_count_initial;
10915 if (!IS_CUSTOM_ELEMENT(element))
10918 if (collect_count == 0)
10919 player->inventory_infinite_element = element;
10921 for (k = 0; k < collect_count; k++)
10922 if (player->inventory_size < MAX_INVENTORY_SIZE)
10923 player->inventory_element[player->inventory_size++] =
10934 /* ---------- CE actions ---------------------------------------------- */
10936 case CA_SET_CE_VALUE:
10938 #if USE_NEW_CUSTOM_VALUE
10939 int last_ce_value = CustomValue[x][y];
10941 CustomValue[x][y] = action_arg_number_new;
10943 if (CustomValue[x][y] != last_ce_value)
10945 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10946 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10948 if (CustomValue[x][y] == 0)
10950 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10951 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10959 case CA_SET_CE_SCORE:
10961 #if USE_NEW_CUSTOM_VALUE
10962 int last_ce_score = ei->collect_score;
10964 ei->collect_score = action_arg_number_new;
10966 if (ei->collect_score != last_ce_score)
10968 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10969 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10971 if (ei->collect_score == 0)
10975 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10976 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10979 This is a very special case that seems to be a mixture between
10980 CheckElementChange() and CheckTriggeredElementChange(): while
10981 the first one only affects single elements that are triggered
10982 directly, the second one affects multiple elements in the playfield
10983 that are triggered indirectly by another element. This is a third
10984 case: Changing the CE score always affects multiple identical CEs,
10985 so every affected CE must be checked, not only the single CE for
10986 which the CE score was changed in the first place (as every instance
10987 of that CE shares the same CE score, and therefore also can change)!
10989 SCAN_PLAYFIELD(xx, yy)
10991 if (Feld[xx][yy] == element)
10992 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10993 CE_SCORE_GETS_ZERO);
11002 case CA_SET_CE_ARTWORK:
11004 int artwork_element = action_arg_element;
11005 boolean reset_frame = FALSE;
11008 if (action_arg == CA_ARG_ELEMENT_RESET)
11009 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11012 if (ei->gfx_element != artwork_element)
11013 reset_frame = TRUE;
11015 ei->gfx_element = artwork_element;
11017 SCAN_PLAYFIELD(xx, yy)
11019 if (Feld[xx][yy] == element)
11023 ResetGfxAnimation(xx, yy);
11024 ResetRandomAnimationValue(xx, yy);
11027 TEST_DrawLevelField(xx, yy);
11034 /* ---------- engine actions ------------------------------------------ */
11036 case CA_SET_ENGINE_SCAN_MODE:
11038 InitPlayfieldScanMode(action_arg);
11048 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11050 int old_element = Feld[x][y];
11051 int new_element = GetElementFromGroupElement(element);
11052 int previous_move_direction = MovDir[x][y];
11053 #if USE_NEW_CUSTOM_VALUE
11054 int last_ce_value = CustomValue[x][y];
11056 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11057 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11058 boolean add_player_onto_element = (new_element_is_player &&
11059 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11060 /* this breaks SnakeBite when a snake is
11061 halfway through a door that closes */
11062 /* NOW FIXED AT LEVEL INIT IN files.c */
11063 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11065 IS_WALKABLE(old_element));
11068 /* check if element under the player changes from accessible to unaccessible
11069 (needed for special case of dropping element which then changes) */
11070 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11071 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11079 if (!add_player_onto_element)
11081 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11082 RemoveMovingField(x, y);
11086 Feld[x][y] = new_element;
11088 #if !USE_GFX_RESET_GFX_ANIMATION
11089 ResetGfxAnimation(x, y);
11090 ResetRandomAnimationValue(x, y);
11093 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11094 MovDir[x][y] = previous_move_direction;
11096 #if USE_NEW_CUSTOM_VALUE
11097 if (element_info[new_element].use_last_ce_value)
11098 CustomValue[x][y] = last_ce_value;
11101 InitField_WithBug1(x, y, FALSE);
11103 new_element = Feld[x][y]; /* element may have changed */
11105 #if USE_GFX_RESET_GFX_ANIMATION
11106 ResetGfxAnimation(x, y);
11107 ResetRandomAnimationValue(x, y);
11110 TEST_DrawLevelField(x, y);
11112 if (GFX_CRUMBLED(new_element))
11113 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11117 /* check if element under the player changes from accessible to unaccessible
11118 (needed for special case of dropping element which then changes) */
11119 /* (must be checked after creating new element for walkable group elements) */
11120 #if USE_FIX_KILLED_BY_NON_WALKABLE
11121 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11122 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11129 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11130 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11139 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11140 if (new_element_is_player)
11141 RelocatePlayer(x, y, new_element);
11144 ChangeCount[x][y]++; /* count number of changes in the same frame */
11146 TestIfBadThingTouchesPlayer(x, y);
11147 TestIfPlayerTouchesCustomElement(x, y);
11148 TestIfElementTouchesCustomElement(x, y);
11151 static void CreateField(int x, int y, int element)
11153 CreateFieldExt(x, y, element, FALSE);
11156 static void CreateElementFromChange(int x, int y, int element)
11158 element = GET_VALID_RUNTIME_ELEMENT(element);
11160 #if USE_STOP_CHANGED_ELEMENTS
11161 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11163 int old_element = Feld[x][y];
11165 /* prevent changed element from moving in same engine frame
11166 unless both old and new element can either fall or move */
11167 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11168 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11173 CreateFieldExt(x, y, element, TRUE);
11176 static boolean ChangeElement(int x, int y, int element, int page)
11178 struct ElementInfo *ei = &element_info[element];
11179 struct ElementChangeInfo *change = &ei->change_page[page];
11180 int ce_value = CustomValue[x][y];
11181 int ce_score = ei->collect_score;
11182 int target_element;
11183 int old_element = Feld[x][y];
11185 /* always use default change event to prevent running into a loop */
11186 if (ChangeEvent[x][y] == -1)
11187 ChangeEvent[x][y] = CE_DELAY;
11189 if (ChangeEvent[x][y] == CE_DELAY)
11191 /* reset actual trigger element, trigger player and action element */
11192 change->actual_trigger_element = EL_EMPTY;
11193 change->actual_trigger_player = EL_EMPTY;
11194 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11195 change->actual_trigger_side = CH_SIDE_NONE;
11196 change->actual_trigger_ce_value = 0;
11197 change->actual_trigger_ce_score = 0;
11200 /* do not change elements more than a specified maximum number of changes */
11201 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11204 ChangeCount[x][y]++; /* count number of changes in the same frame */
11206 if (change->explode)
11213 if (change->use_target_content)
11215 boolean complete_replace = TRUE;
11216 boolean can_replace[3][3];
11219 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11222 boolean is_walkable;
11223 boolean is_diggable;
11224 boolean is_collectible;
11225 boolean is_removable;
11226 boolean is_destructible;
11227 int ex = x + xx - 1;
11228 int ey = y + yy - 1;
11229 int content_element = change->target_content.e[xx][yy];
11232 can_replace[xx][yy] = TRUE;
11234 if (ex == x && ey == y) /* do not check changing element itself */
11237 if (content_element == EL_EMPTY_SPACE)
11239 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11244 if (!IN_LEV_FIELD(ex, ey))
11246 can_replace[xx][yy] = FALSE;
11247 complete_replace = FALSE;
11254 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11255 e = MovingOrBlocked2Element(ex, ey);
11257 is_empty = (IS_FREE(ex, ey) ||
11258 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11260 is_walkable = (is_empty || IS_WALKABLE(e));
11261 is_diggable = (is_empty || IS_DIGGABLE(e));
11262 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11263 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11264 is_removable = (is_diggable || is_collectible);
11266 can_replace[xx][yy] =
11267 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11268 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11269 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11270 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11271 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11272 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11273 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11275 if (!can_replace[xx][yy])
11276 complete_replace = FALSE;
11279 if (!change->only_if_complete || complete_replace)
11281 boolean something_has_changed = FALSE;
11283 if (change->only_if_complete && change->use_random_replace &&
11284 RND(100) < change->random_percentage)
11287 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11289 int ex = x + xx - 1;
11290 int ey = y + yy - 1;
11291 int content_element;
11293 if (can_replace[xx][yy] && (!change->use_random_replace ||
11294 RND(100) < change->random_percentage))
11296 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11297 RemoveMovingField(ex, ey);
11299 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11301 content_element = change->target_content.e[xx][yy];
11302 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11303 ce_value, ce_score);
11305 CreateElementFromChange(ex, ey, target_element);
11307 something_has_changed = TRUE;
11309 /* for symmetry reasons, freeze newly created border elements */
11310 if (ex != x || ey != y)
11311 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11315 if (something_has_changed)
11317 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11318 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11324 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11325 ce_value, ce_score);
11327 if (element == EL_DIAGONAL_GROWING ||
11328 element == EL_DIAGONAL_SHRINKING)
11330 target_element = Store[x][y];
11332 Store[x][y] = EL_EMPTY;
11335 CreateElementFromChange(x, y, target_element);
11337 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11338 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11341 /* this uses direct change before indirect change */
11342 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11347 #if USE_NEW_DELAYED_ACTION
11349 static void HandleElementChange(int x, int y, int page)
11351 int element = MovingOrBlocked2Element(x, y);
11352 struct ElementInfo *ei = &element_info[element];
11353 struct ElementChangeInfo *change = &ei->change_page[page];
11354 boolean handle_action_before_change = FALSE;
11357 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11358 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11361 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11362 x, y, element, element_info[element].token_name);
11363 printf("HandleElementChange(): This should never happen!\n");
11368 /* this can happen with classic bombs on walkable, changing elements */
11369 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11372 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11373 ChangeDelay[x][y] = 0;
11379 if (ChangeDelay[x][y] == 0) /* initialize element change */
11381 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11383 if (change->can_change)
11386 /* !!! not clear why graphic animation should be reset at all here !!! */
11387 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11388 #if USE_GFX_RESET_WHEN_NOT_MOVING
11389 /* when a custom element is about to change (for example by change delay),
11390 do not reset graphic animation when the custom element is moving */
11391 if (!IS_MOVING(x, y))
11394 ResetGfxAnimation(x, y);
11395 ResetRandomAnimationValue(x, y);
11399 if (change->pre_change_function)
11400 change->pre_change_function(x, y);
11404 ChangeDelay[x][y]--;
11406 if (ChangeDelay[x][y] != 0) /* continue element change */
11408 if (change->can_change)
11410 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11412 if (IS_ANIMATED(graphic))
11413 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11415 if (change->change_function)
11416 change->change_function(x, y);
11419 else /* finish element change */
11421 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11423 page = ChangePage[x][y];
11424 ChangePage[x][y] = -1;
11426 change = &ei->change_page[page];
11429 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11431 ChangeDelay[x][y] = 1; /* try change after next move step */
11432 ChangePage[x][y] = page; /* remember page to use for change */
11438 /* special case: set new level random seed before changing element */
11439 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11440 handle_action_before_change = TRUE;
11442 if (change->has_action && handle_action_before_change)
11443 ExecuteCustomElementAction(x, y, element, page);
11446 if (change->can_change)
11448 if (ChangeElement(x, y, element, page))
11450 if (change->post_change_function)
11451 change->post_change_function(x, y);
11455 if (change->has_action && !handle_action_before_change)
11456 ExecuteCustomElementAction(x, y, element, page);
11462 static void HandleElementChange(int x, int y, int page)
11464 int element = MovingOrBlocked2Element(x, y);
11465 struct ElementInfo *ei = &element_info[element];
11466 struct ElementChangeInfo *change = &ei->change_page[page];
11469 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11472 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11473 x, y, element, element_info[element].token_name);
11474 printf("HandleElementChange(): This should never happen!\n");
11479 /* this can happen with classic bombs on walkable, changing elements */
11480 if (!CAN_CHANGE(element))
11483 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11484 ChangeDelay[x][y] = 0;
11490 if (ChangeDelay[x][y] == 0) /* initialize element change */
11492 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11494 ResetGfxAnimation(x, y);
11495 ResetRandomAnimationValue(x, y);
11497 if (change->pre_change_function)
11498 change->pre_change_function(x, y);
11501 ChangeDelay[x][y]--;
11503 if (ChangeDelay[x][y] != 0) /* continue element change */
11505 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11507 if (IS_ANIMATED(graphic))
11508 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11510 if (change->change_function)
11511 change->change_function(x, y);
11513 else /* finish element change */
11515 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11517 page = ChangePage[x][y];
11518 ChangePage[x][y] = -1;
11520 change = &ei->change_page[page];
11523 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11525 ChangeDelay[x][y] = 1; /* try change after next move step */
11526 ChangePage[x][y] = page; /* remember page to use for change */
11531 if (ChangeElement(x, y, element, page))
11533 if (change->post_change_function)
11534 change->post_change_function(x, y);
11541 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11542 int trigger_element,
11544 int trigger_player,
11548 boolean change_done_any = FALSE;
11549 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11552 if (!(trigger_events[trigger_element][trigger_event]))
11556 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11557 trigger_event, recursion_loop_depth, recursion_loop_detected,
11558 recursion_loop_element, EL_NAME(recursion_loop_element));
11561 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11563 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11565 int element = EL_CUSTOM_START + i;
11566 boolean change_done = FALSE;
11569 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11570 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11573 for (p = 0; p < element_info[element].num_change_pages; p++)
11575 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11577 if (change->can_change_or_has_action &&
11578 change->has_event[trigger_event] &&
11579 change->trigger_side & trigger_side &&
11580 change->trigger_player & trigger_player &&
11581 change->trigger_page & trigger_page_bits &&
11582 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11584 change->actual_trigger_element = trigger_element;
11585 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11586 change->actual_trigger_player_bits = trigger_player;
11587 change->actual_trigger_side = trigger_side;
11588 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11589 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11592 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11593 element, EL_NAME(element), p);
11596 if ((change->can_change && !change_done) || change->has_action)
11600 SCAN_PLAYFIELD(x, y)
11602 if (Feld[x][y] == element)
11604 if (change->can_change && !change_done)
11606 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11607 /* if element already changed in this frame, not only prevent
11608 another element change (checked in ChangeElement()), but
11609 also prevent additional element actions for this element */
11611 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11612 !level.use_action_after_change_bug)
11617 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11618 element, EL_NAME(element), p);
11621 ChangeDelay[x][y] = 1;
11622 ChangeEvent[x][y] = trigger_event;
11624 HandleElementChange(x, y, p);
11626 #if USE_NEW_DELAYED_ACTION
11627 else if (change->has_action)
11629 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11630 /* if element already changed in this frame, not only prevent
11631 another element change (checked in ChangeElement()), but
11632 also prevent additional element actions for this element */
11634 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11635 !level.use_action_after_change_bug)
11641 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11642 element, EL_NAME(element), p);
11645 ExecuteCustomElementAction(x, y, element, p);
11646 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11649 if (change->has_action)
11651 ExecuteCustomElementAction(x, y, element, p);
11652 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11658 if (change->can_change)
11660 change_done = TRUE;
11661 change_done_any = TRUE;
11664 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11665 element, EL_NAME(element), p);
11674 RECURSION_LOOP_DETECTION_END();
11676 return change_done_any;
11679 static boolean CheckElementChangeExt(int x, int y,
11681 int trigger_element,
11683 int trigger_player,
11686 boolean change_done = FALSE;
11689 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11690 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11693 if (Feld[x][y] == EL_BLOCKED)
11695 Blocked2Moving(x, y, &x, &y);
11696 element = Feld[x][y];
11700 /* check if element has already changed */
11701 if (Feld[x][y] != element)
11704 /* check if element has already changed or is about to change after moving */
11705 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11706 Feld[x][y] != element) ||
11708 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11709 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11710 ChangePage[x][y] != -1)))
11715 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11716 trigger_event, recursion_loop_depth, recursion_loop_detected,
11717 recursion_loop_element, EL_NAME(recursion_loop_element));
11720 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11723 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11726 for (p = 0; p < element_info[element].num_change_pages; p++)
11728 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11730 /* check trigger element for all events where the element that is checked
11731 for changing interacts with a directly adjacent element -- this is
11732 different to element changes that affect other elements to change on the
11733 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11734 boolean check_trigger_element =
11735 (trigger_event == CE_TOUCHING_X ||
11736 trigger_event == CE_HITTING_X ||
11737 trigger_event == CE_HIT_BY_X ||
11739 /* this one was forgotten until 3.2.3 */
11740 trigger_event == CE_DIGGING_X);
11743 if (change->can_change_or_has_action &&
11744 change->has_event[trigger_event] &&
11745 change->trigger_side & trigger_side &&
11746 change->trigger_player & trigger_player &&
11747 (!check_trigger_element ||
11748 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11750 change->actual_trigger_element = trigger_element;
11751 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11752 change->actual_trigger_player_bits = trigger_player;
11753 change->actual_trigger_side = trigger_side;
11754 change->actual_trigger_ce_value = CustomValue[x][y];
11755 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11757 /* special case: trigger element not at (x,y) position for some events */
11758 if (check_trigger_element)
11770 { 0, 0 }, { 0, 0 }, { 0, 0 },
11774 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11775 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11777 change->actual_trigger_ce_value = CustomValue[xx][yy];
11778 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11781 if (change->can_change && !change_done)
11783 ChangeDelay[x][y] = 1;
11784 ChangeEvent[x][y] = trigger_event;
11786 HandleElementChange(x, y, p);
11788 change_done = TRUE;
11790 #if USE_NEW_DELAYED_ACTION
11791 else if (change->has_action)
11793 ExecuteCustomElementAction(x, y, element, p);
11794 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11797 if (change->has_action)
11799 ExecuteCustomElementAction(x, y, element, p);
11800 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11806 RECURSION_LOOP_DETECTION_END();
11808 return change_done;
11811 static void PlayPlayerSound(struct PlayerInfo *player)
11813 int jx = player->jx, jy = player->jy;
11814 int sound_element = player->artwork_element;
11815 int last_action = player->last_action_waiting;
11816 int action = player->action_waiting;
11818 if (player->is_waiting)
11820 if (action != last_action)
11821 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11823 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11827 if (action != last_action)
11828 StopSound(element_info[sound_element].sound[last_action]);
11830 if (last_action == ACTION_SLEEPING)
11831 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11835 static void PlayAllPlayersSound()
11839 for (i = 0; i < MAX_PLAYERS; i++)
11840 if (stored_player[i].active)
11841 PlayPlayerSound(&stored_player[i]);
11844 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11846 boolean last_waiting = player->is_waiting;
11847 int move_dir = player->MovDir;
11849 player->dir_waiting = move_dir;
11850 player->last_action_waiting = player->action_waiting;
11854 if (!last_waiting) /* not waiting -> waiting */
11856 player->is_waiting = TRUE;
11858 player->frame_counter_bored =
11860 game.player_boring_delay_fixed +
11861 GetSimpleRandom(game.player_boring_delay_random);
11862 player->frame_counter_sleeping =
11864 game.player_sleeping_delay_fixed +
11865 GetSimpleRandom(game.player_sleeping_delay_random);
11867 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11870 if (game.player_sleeping_delay_fixed +
11871 game.player_sleeping_delay_random > 0 &&
11872 player->anim_delay_counter == 0 &&
11873 player->post_delay_counter == 0 &&
11874 FrameCounter >= player->frame_counter_sleeping)
11875 player->is_sleeping = TRUE;
11876 else if (game.player_boring_delay_fixed +
11877 game.player_boring_delay_random > 0 &&
11878 FrameCounter >= player->frame_counter_bored)
11879 player->is_bored = TRUE;
11881 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11882 player->is_bored ? ACTION_BORING :
11885 if (player->is_sleeping && player->use_murphy)
11887 /* special case for sleeping Murphy when leaning against non-free tile */
11889 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11890 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11891 !IS_MOVING(player->jx - 1, player->jy)))
11892 move_dir = MV_LEFT;
11893 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11894 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11895 !IS_MOVING(player->jx + 1, player->jy)))
11896 move_dir = MV_RIGHT;
11898 player->is_sleeping = FALSE;
11900 player->dir_waiting = move_dir;
11903 if (player->is_sleeping)
11905 if (player->num_special_action_sleeping > 0)
11907 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11909 int last_special_action = player->special_action_sleeping;
11910 int num_special_action = player->num_special_action_sleeping;
11911 int special_action =
11912 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11913 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11914 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11915 last_special_action + 1 : ACTION_SLEEPING);
11916 int special_graphic =
11917 el_act_dir2img(player->artwork_element, special_action, move_dir);
11919 player->anim_delay_counter =
11920 graphic_info[special_graphic].anim_delay_fixed +
11921 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11922 player->post_delay_counter =
11923 graphic_info[special_graphic].post_delay_fixed +
11924 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11926 player->special_action_sleeping = special_action;
11929 if (player->anim_delay_counter > 0)
11931 player->action_waiting = player->special_action_sleeping;
11932 player->anim_delay_counter--;
11934 else if (player->post_delay_counter > 0)
11936 player->post_delay_counter--;
11940 else if (player->is_bored)
11942 if (player->num_special_action_bored > 0)
11944 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11946 int special_action =
11947 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11948 int special_graphic =
11949 el_act_dir2img(player->artwork_element, special_action, move_dir);
11951 player->anim_delay_counter =
11952 graphic_info[special_graphic].anim_delay_fixed +
11953 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11954 player->post_delay_counter =
11955 graphic_info[special_graphic].post_delay_fixed +
11956 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11958 player->special_action_bored = special_action;
11961 if (player->anim_delay_counter > 0)
11963 player->action_waiting = player->special_action_bored;
11964 player->anim_delay_counter--;
11966 else if (player->post_delay_counter > 0)
11968 player->post_delay_counter--;
11973 else if (last_waiting) /* waiting -> not waiting */
11975 player->is_waiting = FALSE;
11976 player->is_bored = FALSE;
11977 player->is_sleeping = FALSE;
11979 player->frame_counter_bored = -1;
11980 player->frame_counter_sleeping = -1;
11982 player->anim_delay_counter = 0;
11983 player->post_delay_counter = 0;
11985 player->dir_waiting = player->MovDir;
11986 player->action_waiting = ACTION_DEFAULT;
11988 player->special_action_bored = ACTION_DEFAULT;
11989 player->special_action_sleeping = ACTION_DEFAULT;
11993 static void CheckSingleStepMode(struct PlayerInfo *player)
11995 if (tape.single_step && tape.recording && !tape.pausing)
11997 /* as it is called "single step mode", just return to pause mode when the
11998 player stopped moving after one tile (or never starts moving at all) */
11999 if (!player->is_moving && !player->is_pushing)
12001 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12002 SnapField(player, 0, 0); /* stop snapping */
12007 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12009 int left = player_action & JOY_LEFT;
12010 int right = player_action & JOY_RIGHT;
12011 int up = player_action & JOY_UP;
12012 int down = player_action & JOY_DOWN;
12013 int button1 = player_action & JOY_BUTTON_1;
12014 int button2 = player_action & JOY_BUTTON_2;
12015 int dx = (left ? -1 : right ? 1 : 0);
12016 int dy = (up ? -1 : down ? 1 : 0);
12018 if (!player->active || tape.pausing)
12024 SnapField(player, dx, dy);
12028 DropElement(player);
12030 MovePlayer(player, dx, dy);
12033 CheckSingleStepMode(player);
12035 SetPlayerWaiting(player, FALSE);
12037 return player_action;
12041 /* no actions for this player (no input at player's configured device) */
12043 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12044 SnapField(player, 0, 0);
12045 CheckGravityMovementWhenNotMoving(player);
12047 if (player->MovPos == 0)
12048 SetPlayerWaiting(player, TRUE);
12050 if (player->MovPos == 0) /* needed for tape.playing */
12051 player->is_moving = FALSE;
12053 player->is_dropping = FALSE;
12054 player->is_dropping_pressed = FALSE;
12055 player->drop_pressed_delay = 0;
12057 CheckSingleStepMode(player);
12063 static void CheckLevelTime()
12067 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12068 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12070 if (level.native_em_level->lev->home == 0) /* all players at home */
12072 PlayerWins(local_player);
12074 AllPlayersGone = TRUE;
12076 level.native_em_level->lev->home = -1;
12079 if (level.native_em_level->ply[0]->alive == 0 &&
12080 level.native_em_level->ply[1]->alive == 0 &&
12081 level.native_em_level->ply[2]->alive == 0 &&
12082 level.native_em_level->ply[3]->alive == 0) /* all dead */
12083 AllPlayersGone = TRUE;
12085 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12087 if (game_sp.LevelSolved &&
12088 !game_sp.GameOver) /* game won */
12090 PlayerWins(local_player);
12092 game_sp.GameOver = TRUE;
12094 AllPlayersGone = TRUE;
12097 if (game_sp.GameOver) /* game lost */
12098 AllPlayersGone = TRUE;
12101 if (TimeFrames >= FRAMES_PER_SECOND)
12106 for (i = 0; i < MAX_PLAYERS; i++)
12108 struct PlayerInfo *player = &stored_player[i];
12110 if (SHIELD_ON(player))
12112 player->shield_normal_time_left--;
12114 if (player->shield_deadly_time_left > 0)
12115 player->shield_deadly_time_left--;
12119 if (!local_player->LevelSolved && !level.use_step_counter)
12127 if (TimeLeft <= 10 && setup.time_limit)
12128 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12131 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12132 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12134 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12136 /* (already called by UpdateAndDisplayGameControlValues() below) */
12137 // DisplayGameControlValues();
12139 DrawGameValue_Time(TimeLeft);
12142 if (!TimeLeft && setup.time_limit)
12144 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12145 level.native_em_level->lev->killed_out_of_time = TRUE;
12147 for (i = 0; i < MAX_PLAYERS; i++)
12148 KillPlayer(&stored_player[i]);
12152 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12154 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12156 /* (already called by UpdateAndDisplayGameControlValues() below) */
12157 // DisplayGameControlValues();
12160 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12161 DrawGameValue_Time(TimePlayed);
12164 level.native_em_level->lev->time =
12165 (game.no_time_limit ? TimePlayed : TimeLeft);
12168 if (tape.recording || tape.playing)
12169 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12173 UpdateAndDisplayGameControlValues();
12175 UpdateGameDoorValues();
12176 DrawGameDoorValues();
12180 void AdvanceFrameAndPlayerCounters(int player_nr)
12184 /* advance frame counters (global frame counter and time frame counter) */
12188 /* advance player counters (counters for move delay, move animation etc.) */
12189 for (i = 0; i < MAX_PLAYERS; i++)
12191 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12192 int move_delay_value = stored_player[i].move_delay_value;
12193 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12195 if (!advance_player_counters) /* not all players may be affected */
12198 #if USE_NEW_PLAYER_ANIM
12199 if (move_frames == 0) /* less than one move per game frame */
12201 int stepsize = TILEX / move_delay_value;
12202 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12203 int count = (stored_player[i].is_moving ?
12204 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12206 if (count % delay == 0)
12211 stored_player[i].Frame += move_frames;
12213 if (stored_player[i].MovPos != 0)
12214 stored_player[i].StepFrame += move_frames;
12216 if (stored_player[i].move_delay > 0)
12217 stored_player[i].move_delay--;
12219 /* due to bugs in previous versions, counter must count up, not down */
12220 if (stored_player[i].push_delay != -1)
12221 stored_player[i].push_delay++;
12223 if (stored_player[i].drop_delay > 0)
12224 stored_player[i].drop_delay--;
12226 if (stored_player[i].is_dropping_pressed)
12227 stored_player[i].drop_pressed_delay++;
12231 void StartGameActions(boolean init_network_game, boolean record_tape,
12234 unsigned int new_random_seed = InitRND(random_seed);
12237 TapeStartRecording(new_random_seed);
12239 #if defined(NETWORK_AVALIABLE)
12240 if (init_network_game)
12242 SendToServer_StartPlaying();
12253 static unsigned int game_frame_delay = 0;
12254 unsigned int game_frame_delay_value;
12255 byte *recorded_player_action;
12256 byte summarized_player_action = 0;
12257 byte tape_action[MAX_PLAYERS];
12260 /* detect endless loops, caused by custom element programming */
12261 if (recursion_loop_detected && recursion_loop_depth == 0)
12263 char *message = getStringCat3("Internal Error! Element ",
12264 EL_NAME(recursion_loop_element),
12265 " caused endless loop! Quit the game?");
12267 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12268 EL_NAME(recursion_loop_element));
12270 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12272 recursion_loop_detected = FALSE; /* if game should be continued */
12279 if (game.restart_level)
12280 StartGameActions(options.network, setup.autorecord, level.random_seed);
12282 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12283 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12285 if (level.native_em_level->lev->home == 0) /* all players at home */
12287 PlayerWins(local_player);
12289 AllPlayersGone = TRUE;
12291 level.native_em_level->lev->home = -1;
12294 if (level.native_em_level->ply[0]->alive == 0 &&
12295 level.native_em_level->ply[1]->alive == 0 &&
12296 level.native_em_level->ply[2]->alive == 0 &&
12297 level.native_em_level->ply[3]->alive == 0) /* all dead */
12298 AllPlayersGone = TRUE;
12300 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12302 if (game_sp.LevelSolved &&
12303 !game_sp.GameOver) /* game won */
12305 PlayerWins(local_player);
12307 game_sp.GameOver = TRUE;
12309 AllPlayersGone = TRUE;
12312 if (game_sp.GameOver) /* game lost */
12313 AllPlayersGone = TRUE;
12316 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12319 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12322 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12325 game_frame_delay_value =
12326 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12328 if (tape.playing && tape.warp_forward && !tape.pausing)
12329 game_frame_delay_value = 0;
12331 /* ---------- main game synchronization point ---------- */
12333 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12335 if (network_playing && !network_player_action_received)
12337 /* try to get network player actions in time */
12339 #if defined(NETWORK_AVALIABLE)
12340 /* last chance to get network player actions without main loop delay */
12341 HandleNetworking();
12344 /* game was quit by network peer */
12345 if (game_status != GAME_MODE_PLAYING)
12348 if (!network_player_action_received)
12349 return; /* failed to get network player actions in time */
12351 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12357 /* at this point we know that we really continue executing the game */
12359 network_player_action_received = FALSE;
12361 /* when playing tape, read previously recorded player input from tape data */
12362 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12365 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12370 if (tape.set_centered_player)
12372 game.centered_player_nr_next = tape.centered_player_nr_next;
12373 game.set_centered_player = TRUE;
12376 for (i = 0; i < MAX_PLAYERS; i++)
12378 summarized_player_action |= stored_player[i].action;
12381 if (!network_playing && (game.team_mode || tape.playing))
12382 stored_player[i].effective_action = stored_player[i].action;
12384 if (!network_playing)
12385 stored_player[i].effective_action = stored_player[i].action;
12389 #if defined(NETWORK_AVALIABLE)
12390 if (network_playing)
12391 SendToServer_MovePlayer(summarized_player_action);
12394 if (!options.network && !game.team_mode)
12395 local_player->effective_action = summarized_player_action;
12397 if (tape.recording &&
12399 setup.input_on_focus &&
12400 game.centered_player_nr != -1)
12402 for (i = 0; i < MAX_PLAYERS; i++)
12403 stored_player[i].effective_action =
12404 (i == game.centered_player_nr ? summarized_player_action : 0);
12407 if (recorded_player_action != NULL)
12408 for (i = 0; i < MAX_PLAYERS; i++)
12409 stored_player[i].effective_action = recorded_player_action[i];
12411 for (i = 0; i < MAX_PLAYERS; i++)
12413 tape_action[i] = stored_player[i].effective_action;
12416 /* (this may happen in the RND game engine if a player was not present on
12417 the playfield on level start, but appeared later from a custom element */
12418 if (tape.recording &&
12421 !tape.player_participates[i])
12422 tape.player_participates[i] = TRUE;
12424 /* (this can only happen in the R'n'D game engine) */
12425 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12426 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12430 /* only record actions from input devices, but not programmed actions */
12431 if (tape.recording)
12432 TapeRecordAction(tape_action);
12434 #if USE_NEW_PLAYER_ASSIGNMENTS
12436 if (game.team_mode)
12439 byte mapped_action[MAX_PLAYERS];
12441 #if DEBUG_PLAYER_ACTIONS
12443 for (i = 0; i < MAX_PLAYERS; i++)
12444 printf(" %d, ", stored_player[i].effective_action);
12447 for (i = 0; i < MAX_PLAYERS; i++)
12448 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12450 for (i = 0; i < MAX_PLAYERS; i++)
12451 stored_player[i].effective_action = mapped_action[i];
12453 #if DEBUG_PLAYER_ACTIONS
12455 for (i = 0; i < MAX_PLAYERS; i++)
12456 printf(" %d, ", stored_player[i].effective_action);
12460 #if DEBUG_PLAYER_ACTIONS
12464 for (i = 0; i < MAX_PLAYERS; i++)
12465 printf(" %d, ", stored_player[i].effective_action);
12472 printf("::: summarized_player_action == %d\n",
12473 local_player->effective_action);
12480 #if DEBUG_INIT_PLAYER
12483 printf("Player status (final):\n");
12485 for (i = 0; i < MAX_PLAYERS; i++)
12487 struct PlayerInfo *player = &stored_player[i];
12489 printf("- player %d: present == %d, connected == %d, active == %d",
12495 if (local_player == player)
12496 printf(" (local player)");
12506 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12508 GameActions_EM_Main();
12510 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12512 GameActions_SP_Main();
12520 void GameActions_EM_Main()
12522 byte effective_action[MAX_PLAYERS];
12523 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12526 for (i = 0; i < MAX_PLAYERS; i++)
12527 effective_action[i] = stored_player[i].effective_action;
12529 GameActions_EM(effective_action, warp_mode);
12533 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12536 void GameActions_SP_Main()
12538 byte effective_action[MAX_PLAYERS];
12539 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12542 for (i = 0; i < MAX_PLAYERS; i++)
12543 effective_action[i] = stored_player[i].effective_action;
12545 GameActions_SP(effective_action, warp_mode);
12549 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12552 void GameActions_RND()
12554 int magic_wall_x = 0, magic_wall_y = 0;
12555 int i, x, y, element, graphic;
12557 InitPlayfieldScanModeVars();
12559 #if USE_ONE_MORE_CHANGE_PER_FRAME
12560 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12562 SCAN_PLAYFIELD(x, y)
12564 ChangeCount[x][y] = 0;
12565 ChangeEvent[x][y] = -1;
12570 if (game.set_centered_player)
12572 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12574 /* switching to "all players" only possible if all players fit to screen */
12575 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12577 game.centered_player_nr_next = game.centered_player_nr;
12578 game.set_centered_player = FALSE;
12581 /* do not switch focus to non-existing (or non-active) player */
12582 if (game.centered_player_nr_next >= 0 &&
12583 !stored_player[game.centered_player_nr_next].active)
12585 game.centered_player_nr_next = game.centered_player_nr;
12586 game.set_centered_player = FALSE;
12590 if (game.set_centered_player &&
12591 ScreenMovPos == 0) /* screen currently aligned at tile position */
12595 if (game.centered_player_nr_next == -1)
12597 setScreenCenteredToAllPlayers(&sx, &sy);
12601 sx = stored_player[game.centered_player_nr_next].jx;
12602 sy = stored_player[game.centered_player_nr_next].jy;
12605 game.centered_player_nr = game.centered_player_nr_next;
12606 game.set_centered_player = FALSE;
12608 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12609 DrawGameDoorValues();
12612 for (i = 0; i < MAX_PLAYERS; i++)
12614 int actual_player_action = stored_player[i].effective_action;
12617 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12618 - rnd_equinox_tetrachloride 048
12619 - rnd_equinox_tetrachloride_ii 096
12620 - rnd_emanuel_schmieg 002
12621 - doctor_sloan_ww 001, 020
12623 if (stored_player[i].MovPos == 0)
12624 CheckGravityMovement(&stored_player[i]);
12627 /* overwrite programmed action with tape action */
12628 if (stored_player[i].programmed_action)
12629 actual_player_action = stored_player[i].programmed_action;
12631 PlayerActions(&stored_player[i], actual_player_action);
12633 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12636 ScrollScreen(NULL, SCROLL_GO_ON);
12638 /* for backwards compatibility, the following code emulates a fixed bug that
12639 occured when pushing elements (causing elements that just made their last
12640 pushing step to already (if possible) make their first falling step in the
12641 same game frame, which is bad); this code is also needed to use the famous
12642 "spring push bug" which is used in older levels and might be wanted to be
12643 used also in newer levels, but in this case the buggy pushing code is only
12644 affecting the "spring" element and no other elements */
12646 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12648 for (i = 0; i < MAX_PLAYERS; i++)
12650 struct PlayerInfo *player = &stored_player[i];
12651 int x = player->jx;
12652 int y = player->jy;
12654 if (player->active && player->is_pushing && player->is_moving &&
12656 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12657 Feld[x][y] == EL_SPRING))
12659 ContinueMoving(x, y);
12661 /* continue moving after pushing (this is actually a bug) */
12662 if (!IS_MOVING(x, y))
12663 Stop[x][y] = FALSE;
12669 debug_print_timestamp(0, "start main loop profiling");
12672 SCAN_PLAYFIELD(x, y)
12674 ChangeCount[x][y] = 0;
12675 ChangeEvent[x][y] = -1;
12677 /* this must be handled before main playfield loop */
12678 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12681 if (MovDelay[x][y] <= 0)
12685 #if USE_NEW_SNAP_DELAY
12686 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12689 if (MovDelay[x][y] <= 0)
12692 TEST_DrawLevelField(x, y);
12694 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12700 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12702 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12703 printf("GameActions(): This should never happen!\n");
12705 ChangePage[x][y] = -1;
12709 Stop[x][y] = FALSE;
12710 if (WasJustMoving[x][y] > 0)
12711 WasJustMoving[x][y]--;
12712 if (WasJustFalling[x][y] > 0)
12713 WasJustFalling[x][y]--;
12714 if (CheckCollision[x][y] > 0)
12715 CheckCollision[x][y]--;
12716 if (CheckImpact[x][y] > 0)
12717 CheckImpact[x][y]--;
12721 /* reset finished pushing action (not done in ContinueMoving() to allow
12722 continuous pushing animation for elements with zero push delay) */
12723 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12725 ResetGfxAnimation(x, y);
12726 TEST_DrawLevelField(x, y);
12730 if (IS_BLOCKED(x, y))
12734 Blocked2Moving(x, y, &oldx, &oldy);
12735 if (!IS_MOVING(oldx, oldy))
12737 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12738 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12739 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12740 printf("GameActions(): This should never happen!\n");
12747 debug_print_timestamp(0, "- time for pre-main loop:");
12750 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12751 SCAN_PLAYFIELD(x, y)
12753 element = Feld[x][y];
12754 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12759 int element2 = element;
12760 int graphic2 = graphic;
12762 int element2 = Feld[x][y];
12763 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12765 int last_gfx_frame = GfxFrame[x][y];
12767 if (graphic_info[graphic2].anim_global_sync)
12768 GfxFrame[x][y] = FrameCounter;
12769 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12770 GfxFrame[x][y] = CustomValue[x][y];
12771 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12772 GfxFrame[x][y] = element_info[element2].collect_score;
12773 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12774 GfxFrame[x][y] = ChangeDelay[x][y];
12776 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12777 DrawLevelGraphicAnimation(x, y, graphic2);
12780 ResetGfxFrame(x, y, TRUE);
12784 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12785 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12786 ResetRandomAnimationValue(x, y);
12790 SetRandomAnimationValue(x, y);
12794 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12797 #endif // -------------------- !!! TEST ONLY !!! --------------------
12800 debug_print_timestamp(0, "- time for TEST loop: -->");
12803 SCAN_PLAYFIELD(x, y)
12805 element = Feld[x][y];
12806 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12808 ResetGfxFrame(x, y, TRUE);
12810 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12811 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12812 ResetRandomAnimationValue(x, y);
12814 SetRandomAnimationValue(x, y);
12816 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12818 if (IS_INACTIVE(element))
12820 if (IS_ANIMATED(graphic))
12821 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12826 /* this may take place after moving, so 'element' may have changed */
12827 if (IS_CHANGING(x, y) &&
12828 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12830 int page = element_info[element].event_page_nr[CE_DELAY];
12833 HandleElementChange(x, y, page);
12835 if (CAN_CHANGE(element))
12836 HandleElementChange(x, y, page);
12838 if (HAS_ACTION(element))
12839 ExecuteCustomElementAction(x, y, element, page);
12842 element = Feld[x][y];
12843 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12846 #if 0 // ---------------------------------------------------------------------
12848 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12852 element = Feld[x][y];
12853 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12855 if (IS_ANIMATED(graphic) &&
12856 !IS_MOVING(x, y) &&
12858 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12860 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12861 TEST_DrawTwinkleOnField(x, y);
12863 else if (IS_MOVING(x, y))
12864 ContinueMoving(x, y);
12871 case EL_EM_EXIT_OPEN:
12872 case EL_SP_EXIT_OPEN:
12873 case EL_STEEL_EXIT_OPEN:
12874 case EL_EM_STEEL_EXIT_OPEN:
12875 case EL_SP_TERMINAL:
12876 case EL_SP_TERMINAL_ACTIVE:
12877 case EL_EXTRA_TIME:
12878 case EL_SHIELD_NORMAL:
12879 case EL_SHIELD_DEADLY:
12880 if (IS_ANIMATED(graphic))
12881 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12884 case EL_DYNAMITE_ACTIVE:
12885 case EL_EM_DYNAMITE_ACTIVE:
12886 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12887 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12888 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12889 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12890 case EL_SP_DISK_RED_ACTIVE:
12891 CheckDynamite(x, y);
12894 case EL_AMOEBA_GROWING:
12895 AmoebeWaechst(x, y);
12898 case EL_AMOEBA_SHRINKING:
12899 AmoebaDisappearing(x, y);
12902 #if !USE_NEW_AMOEBA_CODE
12903 case EL_AMOEBA_WET:
12904 case EL_AMOEBA_DRY:
12905 case EL_AMOEBA_FULL:
12907 case EL_EMC_DRIPPER:
12908 AmoebeAbleger(x, y);
12912 case EL_GAME_OF_LIFE:
12917 case EL_EXIT_CLOSED:
12921 case EL_EM_EXIT_CLOSED:
12925 case EL_STEEL_EXIT_CLOSED:
12926 CheckExitSteel(x, y);
12929 case EL_EM_STEEL_EXIT_CLOSED:
12930 CheckExitSteelEM(x, y);
12933 case EL_SP_EXIT_CLOSED:
12937 case EL_EXPANDABLE_WALL_GROWING:
12938 case EL_EXPANDABLE_STEELWALL_GROWING:
12939 MauerWaechst(x, y);
12942 case EL_EXPANDABLE_WALL:
12943 case EL_EXPANDABLE_WALL_HORIZONTAL:
12944 case EL_EXPANDABLE_WALL_VERTICAL:
12945 case EL_EXPANDABLE_WALL_ANY:
12946 case EL_BD_EXPANDABLE_WALL:
12947 MauerAbleger(x, y);
12950 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12951 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12952 case EL_EXPANDABLE_STEELWALL_ANY:
12953 MauerAblegerStahl(x, y);
12957 CheckForDragon(x, y);
12963 case EL_ELEMENT_SNAPPING:
12964 case EL_DIAGONAL_SHRINKING:
12965 case EL_DIAGONAL_GROWING:
12968 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12970 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12975 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12976 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12981 #else // ---------------------------------------------------------------------
12983 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12987 element = Feld[x][y];
12988 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12990 if (IS_ANIMATED(graphic) &&
12991 !IS_MOVING(x, y) &&
12993 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12995 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12996 TEST_DrawTwinkleOnField(x, y);
12998 else if ((element == EL_ACID ||
12999 element == EL_EXIT_OPEN ||
13000 element == EL_EM_EXIT_OPEN ||
13001 element == EL_SP_EXIT_OPEN ||
13002 element == EL_STEEL_EXIT_OPEN ||
13003 element == EL_EM_STEEL_EXIT_OPEN ||
13004 element == EL_SP_TERMINAL ||
13005 element == EL_SP_TERMINAL_ACTIVE ||
13006 element == EL_EXTRA_TIME ||
13007 element == EL_SHIELD_NORMAL ||
13008 element == EL_SHIELD_DEADLY) &&
13009 IS_ANIMATED(graphic))
13010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13011 else if (IS_MOVING(x, y))
13012 ContinueMoving(x, y);
13013 else if (IS_ACTIVE_BOMB(element))
13014 CheckDynamite(x, y);
13015 else if (element == EL_AMOEBA_GROWING)
13016 AmoebeWaechst(x, y);
13017 else if (element == EL_AMOEBA_SHRINKING)
13018 AmoebaDisappearing(x, y);
13020 #if !USE_NEW_AMOEBA_CODE
13021 else if (IS_AMOEBALIVE(element))
13022 AmoebeAbleger(x, y);
13025 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13027 else if (element == EL_EXIT_CLOSED)
13029 else if (element == EL_EM_EXIT_CLOSED)
13031 else if (element == EL_STEEL_EXIT_CLOSED)
13032 CheckExitSteel(x, y);
13033 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13034 CheckExitSteelEM(x, y);
13035 else if (element == EL_SP_EXIT_CLOSED)
13037 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13038 element == EL_EXPANDABLE_STEELWALL_GROWING)
13039 MauerWaechst(x, y);
13040 else if (element == EL_EXPANDABLE_WALL ||
13041 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13042 element == EL_EXPANDABLE_WALL_VERTICAL ||
13043 element == EL_EXPANDABLE_WALL_ANY ||
13044 element == EL_BD_EXPANDABLE_WALL)
13045 MauerAbleger(x, y);
13046 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13047 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13048 element == EL_EXPANDABLE_STEELWALL_ANY)
13049 MauerAblegerStahl(x, y);
13050 else if (element == EL_FLAMES)
13051 CheckForDragon(x, y);
13052 else if (element == EL_EXPLOSION)
13053 ; /* drawing of correct explosion animation is handled separately */
13054 else if (element == EL_ELEMENT_SNAPPING ||
13055 element == EL_DIAGONAL_SHRINKING ||
13056 element == EL_DIAGONAL_GROWING)
13058 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13060 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13062 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13065 #endif // ---------------------------------------------------------------------
13067 if (IS_BELT_ACTIVE(element))
13068 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13070 if (game.magic_wall_active)
13072 int jx = local_player->jx, jy = local_player->jy;
13074 /* play the element sound at the position nearest to the player */
13075 if ((element == EL_MAGIC_WALL_FULL ||
13076 element == EL_MAGIC_WALL_ACTIVE ||
13077 element == EL_MAGIC_WALL_EMPTYING ||
13078 element == EL_BD_MAGIC_WALL_FULL ||
13079 element == EL_BD_MAGIC_WALL_ACTIVE ||
13080 element == EL_BD_MAGIC_WALL_EMPTYING ||
13081 element == EL_DC_MAGIC_WALL_FULL ||
13082 element == EL_DC_MAGIC_WALL_ACTIVE ||
13083 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13084 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13093 debug_print_timestamp(0, "- time for MAIN loop: -->");
13096 #if USE_NEW_AMOEBA_CODE
13097 /* new experimental amoeba growth stuff */
13098 if (!(FrameCounter % 8))
13100 static unsigned int random = 1684108901;
13102 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13104 x = RND(lev_fieldx);
13105 y = RND(lev_fieldy);
13106 element = Feld[x][y];
13108 if (!IS_PLAYER(x,y) &&
13109 (element == EL_EMPTY ||
13110 CAN_GROW_INTO(element) ||
13111 element == EL_QUICKSAND_EMPTY ||
13112 element == EL_QUICKSAND_FAST_EMPTY ||
13113 element == EL_ACID_SPLASH_LEFT ||
13114 element == EL_ACID_SPLASH_RIGHT))
13116 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13117 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13118 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13119 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13120 Feld[x][y] = EL_AMOEBA_DROP;
13123 random = random * 129 + 1;
13129 if (game.explosions_delayed)
13132 game.explosions_delayed = FALSE;
13134 SCAN_PLAYFIELD(x, y)
13136 element = Feld[x][y];
13138 if (ExplodeField[x][y])
13139 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13140 else if (element == EL_EXPLOSION)
13141 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13143 ExplodeField[x][y] = EX_TYPE_NONE;
13146 game.explosions_delayed = TRUE;
13149 if (game.magic_wall_active)
13151 if (!(game.magic_wall_time_left % 4))
13153 int element = Feld[magic_wall_x][magic_wall_y];
13155 if (element == EL_BD_MAGIC_WALL_FULL ||
13156 element == EL_BD_MAGIC_WALL_ACTIVE ||
13157 element == EL_BD_MAGIC_WALL_EMPTYING)
13158 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13159 else if (element == EL_DC_MAGIC_WALL_FULL ||
13160 element == EL_DC_MAGIC_WALL_ACTIVE ||
13161 element == EL_DC_MAGIC_WALL_EMPTYING)
13162 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13164 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13167 if (game.magic_wall_time_left > 0)
13169 game.magic_wall_time_left--;
13171 if (!game.magic_wall_time_left)
13173 SCAN_PLAYFIELD(x, y)
13175 element = Feld[x][y];
13177 if (element == EL_MAGIC_WALL_ACTIVE ||
13178 element == EL_MAGIC_WALL_FULL)
13180 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13181 TEST_DrawLevelField(x, y);
13183 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13184 element == EL_BD_MAGIC_WALL_FULL)
13186 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13187 TEST_DrawLevelField(x, y);
13189 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13190 element == EL_DC_MAGIC_WALL_FULL)
13192 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13193 TEST_DrawLevelField(x, y);
13197 game.magic_wall_active = FALSE;
13202 if (game.light_time_left > 0)
13204 game.light_time_left--;
13206 if (game.light_time_left == 0)
13207 RedrawAllLightSwitchesAndInvisibleElements();
13210 if (game.timegate_time_left > 0)
13212 game.timegate_time_left--;
13214 if (game.timegate_time_left == 0)
13215 CloseAllOpenTimegates();
13218 if (game.lenses_time_left > 0)
13220 game.lenses_time_left--;
13222 if (game.lenses_time_left == 0)
13223 RedrawAllInvisibleElementsForLenses();
13226 if (game.magnify_time_left > 0)
13228 game.magnify_time_left--;
13230 if (game.magnify_time_left == 0)
13231 RedrawAllInvisibleElementsForMagnifier();
13234 for (i = 0; i < MAX_PLAYERS; i++)
13236 struct PlayerInfo *player = &stored_player[i];
13238 if (SHIELD_ON(player))
13240 if (player->shield_deadly_time_left)
13241 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13242 else if (player->shield_normal_time_left)
13243 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13247 #if USE_DELAYED_GFX_REDRAW
13248 SCAN_PLAYFIELD(x, y)
13251 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13253 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13254 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13257 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13258 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13260 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13261 DrawLevelField(x, y);
13263 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13264 DrawLevelFieldCrumbled(x, y);
13266 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13267 DrawLevelFieldCrumbledNeighbours(x, y);
13269 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13270 DrawTwinkleOnField(x, y);
13273 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13280 PlayAllPlayersSound();
13282 if (options.debug) /* calculate frames per second */
13284 static unsigned int fps_counter = 0;
13285 static int fps_frames = 0;
13286 unsigned int fps_delay_ms = Counter() - fps_counter;
13290 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13292 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13295 fps_counter = Counter();
13298 redraw_mask |= REDRAW_FPS;
13301 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13303 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13305 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13307 local_player->show_envelope = 0;
13311 debug_print_timestamp(0, "stop main loop profiling ");
13312 printf("----------------------------------------------------------\n");
13315 /* use random number generator in every frame to make it less predictable */
13316 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13320 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13322 int min_x = x, min_y = y, max_x = x, max_y = y;
13325 for (i = 0; i < MAX_PLAYERS; i++)
13327 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13329 if (!stored_player[i].active || &stored_player[i] == player)
13332 min_x = MIN(min_x, jx);
13333 min_y = MIN(min_y, jy);
13334 max_x = MAX(max_x, jx);
13335 max_y = MAX(max_y, jy);
13338 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13341 static boolean AllPlayersInVisibleScreen()
13345 for (i = 0; i < MAX_PLAYERS; i++)
13347 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13349 if (!stored_player[i].active)
13352 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13359 void ScrollLevel(int dx, int dy)
13362 /* (directly solved in BlitBitmap() now) */
13363 static Bitmap *bitmap_db_field2 = NULL;
13364 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13371 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13372 /* only horizontal XOR vertical scroll direction allowed */
13373 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13378 /* (directly solved in BlitBitmap() now) */
13379 if (bitmap_db_field2 == NULL)
13380 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13382 /* needed when blitting directly to same bitmap -- should not be needed with
13383 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13384 BlitBitmap(drawto_field, bitmap_db_field2,
13385 FX + TILEX * (dx == -1) - softscroll_offset,
13386 FY + TILEY * (dy == -1) - softscroll_offset,
13387 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13388 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13389 FX + TILEX * (dx == 1) - softscroll_offset,
13390 FY + TILEY * (dy == 1) - softscroll_offset);
13391 BlitBitmap(bitmap_db_field2, drawto_field,
13392 FX + TILEX * (dx == 1) - softscroll_offset,
13393 FY + TILEY * (dy == 1) - softscroll_offset,
13394 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13395 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13396 FX + TILEX * (dx == 1) - softscroll_offset,
13397 FY + TILEY * (dy == 1) - softscroll_offset);
13402 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13403 int xsize = (BX2 - BX1 + 1);
13404 int ysize = (BY2 - BY1 + 1);
13405 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13406 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13407 int step = (start < end ? +1 : -1);
13409 for (i = start; i != end; i += step)
13411 BlitBitmap(drawto_field, drawto_field,
13412 FX + TILEX * (dx != 0 ? i + step : 0),
13413 FY + TILEY * (dy != 0 ? i + step : 0),
13414 TILEX * (dx != 0 ? 1 : xsize),
13415 TILEY * (dy != 0 ? 1 : ysize),
13416 FX + TILEX * (dx != 0 ? i : 0),
13417 FY + TILEY * (dy != 0 ? i : 0));
13424 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13426 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13430 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13432 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13437 BlitBitmap(drawto_field, drawto_field,
13438 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13439 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13440 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13441 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13442 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13443 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13445 BlitBitmap(drawto_field, drawto_field,
13446 FX + TILEX * (dx == -1) - softscroll_offset,
13447 FY + TILEY * (dy == -1) - softscroll_offset,
13448 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13449 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13450 FX + TILEX * (dx == 1) - softscroll_offset,
13451 FY + TILEY * (dy == 1) - softscroll_offset);
13459 x = (dx == 1 ? BX1 : BX2);
13460 for (y = BY1; y <= BY2; y++)
13461 DrawScreenField(x, y);
13466 y = (dy == 1 ? BY1 : BY2);
13467 for (x = BX1; x <= BX2; x++)
13468 DrawScreenField(x, y);
13471 redraw_mask |= REDRAW_FIELD;
13474 static boolean canFallDown(struct PlayerInfo *player)
13476 int jx = player->jx, jy = player->jy;
13478 return (IN_LEV_FIELD(jx, jy + 1) &&
13479 (IS_FREE(jx, jy + 1) ||
13480 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13481 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13482 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13485 static boolean canPassField(int x, int y, int move_dir)
13487 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13488 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13489 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13490 int nextx = x + dx;
13491 int nexty = y + dy;
13492 int element = Feld[x][y];
13494 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13495 !CAN_MOVE(element) &&
13496 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13497 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13498 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13501 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13503 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13504 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13505 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13509 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13510 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13511 (IS_DIGGABLE(Feld[newx][newy]) ||
13512 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13513 canPassField(newx, newy, move_dir)));
13516 static void CheckGravityMovement(struct PlayerInfo *player)
13518 #if USE_PLAYER_GRAVITY
13519 if (player->gravity && !player->programmed_action)
13521 if (game.gravity && !player->programmed_action)
13524 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13525 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13526 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13527 int jx = player->jx, jy = player->jy;
13528 boolean player_is_moving_to_valid_field =
13529 (!player_is_snapping &&
13530 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13531 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13532 boolean player_can_fall_down = canFallDown(player);
13534 if (player_can_fall_down &&
13535 !player_is_moving_to_valid_field)
13536 player->programmed_action = MV_DOWN;
13540 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13542 return CheckGravityMovement(player);
13544 #if USE_PLAYER_GRAVITY
13545 if (player->gravity && !player->programmed_action)
13547 if (game.gravity && !player->programmed_action)
13550 int jx = player->jx, jy = player->jy;
13551 boolean field_under_player_is_free =
13552 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13553 boolean player_is_standing_on_valid_field =
13554 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13555 (IS_WALKABLE(Feld[jx][jy]) &&
13556 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13558 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13559 player->programmed_action = MV_DOWN;
13564 MovePlayerOneStep()
13565 -----------------------------------------------------------------------------
13566 dx, dy: direction (non-diagonal) to try to move the player to
13567 real_dx, real_dy: direction as read from input device (can be diagonal)
13570 boolean MovePlayerOneStep(struct PlayerInfo *player,
13571 int dx, int dy, int real_dx, int real_dy)
13573 int jx = player->jx, jy = player->jy;
13574 int new_jx = jx + dx, new_jy = jy + dy;
13575 #if !USE_FIXED_DONT_RUN_INTO
13579 boolean player_can_move = !player->cannot_move;
13581 if (!player->active || (!dx && !dy))
13582 return MP_NO_ACTION;
13584 player->MovDir = (dx < 0 ? MV_LEFT :
13585 dx > 0 ? MV_RIGHT :
13587 dy > 0 ? MV_DOWN : MV_NONE);
13589 if (!IN_LEV_FIELD(new_jx, new_jy))
13590 return MP_NO_ACTION;
13592 if (!player_can_move)
13594 if (player->MovPos == 0)
13596 player->is_moving = FALSE;
13597 player->is_digging = FALSE;
13598 player->is_collecting = FALSE;
13599 player->is_snapping = FALSE;
13600 player->is_pushing = FALSE;
13605 if (!options.network && game.centered_player_nr == -1 &&
13606 !AllPlayersInSight(player, new_jx, new_jy))
13607 return MP_NO_ACTION;
13609 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13610 return MP_NO_ACTION;
13613 #if !USE_FIXED_DONT_RUN_INTO
13614 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13616 /* (moved to DigField()) */
13617 if (player_can_move && DONT_RUN_INTO(element))
13619 if (element == EL_ACID && dx == 0 && dy == 1)
13621 SplashAcid(new_jx, new_jy);
13622 Feld[jx][jy] = EL_PLAYER_1;
13623 InitMovingField(jx, jy, MV_DOWN);
13624 Store[jx][jy] = EL_ACID;
13625 ContinueMoving(jx, jy);
13626 BuryPlayer(player);
13629 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13635 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13636 if (can_move != MP_MOVING)
13639 /* check if DigField() has caused relocation of the player */
13640 if (player->jx != jx || player->jy != jy)
13641 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13643 StorePlayer[jx][jy] = 0;
13644 player->last_jx = jx;
13645 player->last_jy = jy;
13646 player->jx = new_jx;
13647 player->jy = new_jy;
13648 StorePlayer[new_jx][new_jy] = player->element_nr;
13650 if (player->move_delay_value_next != -1)
13652 player->move_delay_value = player->move_delay_value_next;
13653 player->move_delay_value_next = -1;
13657 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13659 player->step_counter++;
13661 PlayerVisit[jx][jy] = FrameCounter;
13663 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13664 player->is_moving = TRUE;
13668 /* should better be called in MovePlayer(), but this breaks some tapes */
13669 ScrollPlayer(player, SCROLL_INIT);
13675 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13677 int jx = player->jx, jy = player->jy;
13678 int old_jx = jx, old_jy = jy;
13679 int moved = MP_NO_ACTION;
13681 if (!player->active)
13686 if (player->MovPos == 0)
13688 player->is_moving = FALSE;
13689 player->is_digging = FALSE;
13690 player->is_collecting = FALSE;
13691 player->is_snapping = FALSE;
13692 player->is_pushing = FALSE;
13698 if (player->move_delay > 0)
13701 player->move_delay = -1; /* set to "uninitialized" value */
13703 /* store if player is automatically moved to next field */
13704 player->is_auto_moving = (player->programmed_action != MV_NONE);
13706 /* remove the last programmed player action */
13707 player->programmed_action = 0;
13709 if (player->MovPos)
13711 /* should only happen if pre-1.2 tape recordings are played */
13712 /* this is only for backward compatibility */
13714 int original_move_delay_value = player->move_delay_value;
13717 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13721 /* scroll remaining steps with finest movement resolution */
13722 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13724 while (player->MovPos)
13726 ScrollPlayer(player, SCROLL_GO_ON);
13727 ScrollScreen(NULL, SCROLL_GO_ON);
13729 AdvanceFrameAndPlayerCounters(player->index_nr);
13735 player->move_delay_value = original_move_delay_value;
13738 player->is_active = FALSE;
13740 if (player->last_move_dir & MV_HORIZONTAL)
13742 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13743 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13747 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13748 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13751 #if USE_FIXED_BORDER_RUNNING_GFX
13752 if (!moved && !player->is_active)
13754 player->is_moving = FALSE;
13755 player->is_digging = FALSE;
13756 player->is_collecting = FALSE;
13757 player->is_snapping = FALSE;
13758 player->is_pushing = FALSE;
13766 if (moved & MP_MOVING && !ScreenMovPos &&
13767 (player->index_nr == game.centered_player_nr ||
13768 game.centered_player_nr == -1))
13770 if (moved & MP_MOVING && !ScreenMovPos &&
13771 (player == local_player || !options.network))
13774 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13775 int offset = game.scroll_delay_value;
13777 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13779 /* actual player has left the screen -- scroll in that direction */
13780 if (jx != old_jx) /* player has moved horizontally */
13781 scroll_x += (jx - old_jx);
13782 else /* player has moved vertically */
13783 scroll_y += (jy - old_jy);
13787 if (jx != old_jx) /* player has moved horizontally */
13789 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13790 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13791 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13793 /* don't scroll over playfield boundaries */
13794 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13795 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13797 /* don't scroll more than one field at a time */
13798 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13800 /* don't scroll against the player's moving direction */
13801 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13802 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13803 scroll_x = old_scroll_x;
13805 else /* player has moved vertically */
13807 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13808 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13809 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13811 /* don't scroll over playfield boundaries */
13812 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13813 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13815 /* don't scroll more than one field at a time */
13816 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13818 /* don't scroll against the player's moving direction */
13819 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13820 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13821 scroll_y = old_scroll_y;
13825 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13828 if (!options.network && game.centered_player_nr == -1 &&
13829 !AllPlayersInVisibleScreen())
13831 scroll_x = old_scroll_x;
13832 scroll_y = old_scroll_y;
13836 if (!options.network && !AllPlayersInVisibleScreen())
13838 scroll_x = old_scroll_x;
13839 scroll_y = old_scroll_y;
13844 ScrollScreen(player, SCROLL_INIT);
13845 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13850 player->StepFrame = 0;
13852 if (moved & MP_MOVING)
13854 if (old_jx != jx && old_jy == jy)
13855 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13856 else if (old_jx == jx && old_jy != jy)
13857 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13859 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13861 player->last_move_dir = player->MovDir;
13862 player->is_moving = TRUE;
13863 player->is_snapping = FALSE;
13864 player->is_switching = FALSE;
13865 player->is_dropping = FALSE;
13866 player->is_dropping_pressed = FALSE;
13867 player->drop_pressed_delay = 0;
13870 /* should better be called here than above, but this breaks some tapes */
13871 ScrollPlayer(player, SCROLL_INIT);
13876 CheckGravityMovementWhenNotMoving(player);
13878 player->is_moving = FALSE;
13880 /* at this point, the player is allowed to move, but cannot move right now
13881 (e.g. because of something blocking the way) -- ensure that the player
13882 is also allowed to move in the next frame (in old versions before 3.1.1,
13883 the player was forced to wait again for eight frames before next try) */
13885 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13886 player->move_delay = 0; /* allow direct movement in the next frame */
13889 if (player->move_delay == -1) /* not yet initialized by DigField() */
13890 player->move_delay = player->move_delay_value;
13892 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13894 TestIfPlayerTouchesBadThing(jx, jy);
13895 TestIfPlayerTouchesCustomElement(jx, jy);
13898 if (!player->active)
13899 RemovePlayer(player);
13904 void ScrollPlayer(struct PlayerInfo *player, int mode)
13906 int jx = player->jx, jy = player->jy;
13907 int last_jx = player->last_jx, last_jy = player->last_jy;
13908 int move_stepsize = TILEX / player->move_delay_value;
13910 #if USE_NEW_PLAYER_SPEED
13911 if (!player->active)
13914 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13917 if (!player->active || player->MovPos == 0)
13921 if (mode == SCROLL_INIT)
13923 player->actual_frame_counter = FrameCounter;
13924 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13926 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13927 Feld[last_jx][last_jy] == EL_EMPTY)
13929 int last_field_block_delay = 0; /* start with no blocking at all */
13930 int block_delay_adjustment = player->block_delay_adjustment;
13932 /* if player blocks last field, add delay for exactly one move */
13933 if (player->block_last_field)
13935 last_field_block_delay += player->move_delay_value;
13937 /* when blocking enabled, prevent moving up despite gravity */
13938 #if USE_PLAYER_GRAVITY
13939 if (player->gravity && player->MovDir == MV_UP)
13940 block_delay_adjustment = -1;
13942 if (game.gravity && player->MovDir == MV_UP)
13943 block_delay_adjustment = -1;
13947 /* add block delay adjustment (also possible when not blocking) */
13948 last_field_block_delay += block_delay_adjustment;
13950 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13951 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13954 #if USE_NEW_PLAYER_SPEED
13955 if (player->MovPos != 0) /* player has not yet reached destination */
13961 else if (!FrameReached(&player->actual_frame_counter, 1))
13964 #if USE_NEW_PLAYER_SPEED
13965 if (player->MovPos != 0)
13967 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13968 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13970 /* before DrawPlayer() to draw correct player graphic for this case */
13971 if (player->MovPos == 0)
13972 CheckGravityMovement(player);
13975 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13976 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13978 /* before DrawPlayer() to draw correct player graphic for this case */
13979 if (player->MovPos == 0)
13980 CheckGravityMovement(player);
13983 if (player->MovPos == 0) /* player reached destination field */
13985 if (player->move_delay_reset_counter > 0)
13987 player->move_delay_reset_counter--;
13989 if (player->move_delay_reset_counter == 0)
13991 /* continue with normal speed after quickly moving through gate */
13992 HALVE_PLAYER_SPEED(player);
13994 /* be able to make the next move without delay */
13995 player->move_delay = 0;
13999 player->last_jx = jx;
14000 player->last_jy = jy;
14002 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14003 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14005 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14007 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14008 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14010 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14012 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14013 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14015 DrawPlayer(player); /* needed here only to cleanup last field */
14016 RemovePlayer(player);
14018 if (local_player->friends_still_needed == 0 ||
14019 IS_SP_ELEMENT(Feld[jx][jy]))
14020 PlayerWins(player);
14023 /* this breaks one level: "machine", level 000 */
14025 int move_direction = player->MovDir;
14026 int enter_side = MV_DIR_OPPOSITE(move_direction);
14027 int leave_side = move_direction;
14028 int old_jx = last_jx;
14029 int old_jy = last_jy;
14030 int old_element = Feld[old_jx][old_jy];
14031 int new_element = Feld[jx][jy];
14033 if (IS_CUSTOM_ELEMENT(old_element))
14034 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14036 player->index_bit, leave_side);
14038 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14039 CE_PLAYER_LEAVES_X,
14040 player->index_bit, leave_side);
14042 if (IS_CUSTOM_ELEMENT(new_element))
14043 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14044 player->index_bit, enter_side);
14046 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14047 CE_PLAYER_ENTERS_X,
14048 player->index_bit, enter_side);
14050 #if USE_FIX_CE_ACTION_WITH_PLAYER
14051 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14052 CE_MOVE_OF_X, move_direction);
14054 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14055 CE_MOVE_OF_X, move_direction);
14059 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14061 TestIfPlayerTouchesBadThing(jx, jy);
14062 TestIfPlayerTouchesCustomElement(jx, jy);
14064 /* needed because pushed element has not yet reached its destination,
14065 so it would trigger a change event at its previous field location */
14066 if (!player->is_pushing)
14067 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14069 if (!player->active)
14070 RemovePlayer(player);
14073 if (!local_player->LevelSolved && level.use_step_counter)
14083 if (TimeLeft <= 10 && setup.time_limit)
14084 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14087 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14089 DisplayGameControlValues();
14091 DrawGameValue_Time(TimeLeft);
14094 if (!TimeLeft && setup.time_limit)
14095 for (i = 0; i < MAX_PLAYERS; i++)
14096 KillPlayer(&stored_player[i]);
14099 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14101 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14103 DisplayGameControlValues();
14106 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14107 DrawGameValue_Time(TimePlayed);
14111 if (tape.single_step && tape.recording && !tape.pausing &&
14112 !player->programmed_action)
14113 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14117 void ScrollScreen(struct PlayerInfo *player, int mode)
14119 static unsigned int screen_frame_counter = 0;
14121 if (mode == SCROLL_INIT)
14123 /* set scrolling step size according to actual player's moving speed */
14124 ScrollStepSize = TILEX / player->move_delay_value;
14126 screen_frame_counter = FrameCounter;
14127 ScreenMovDir = player->MovDir;
14128 ScreenMovPos = player->MovPos;
14129 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14132 else if (!FrameReached(&screen_frame_counter, 1))
14137 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14138 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14139 redraw_mask |= REDRAW_FIELD;
14142 ScreenMovDir = MV_NONE;
14145 void TestIfPlayerTouchesCustomElement(int x, int y)
14147 static int xy[4][2] =
14154 static int trigger_sides[4][2] =
14156 /* center side border side */
14157 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14158 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14159 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14160 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14162 static int touch_dir[4] =
14164 MV_LEFT | MV_RIGHT,
14169 int center_element = Feld[x][y]; /* should always be non-moving! */
14172 for (i = 0; i < NUM_DIRECTIONS; i++)
14174 int xx = x + xy[i][0];
14175 int yy = y + xy[i][1];
14176 int center_side = trigger_sides[i][0];
14177 int border_side = trigger_sides[i][1];
14178 int border_element;
14180 if (!IN_LEV_FIELD(xx, yy))
14183 if (IS_PLAYER(x, y)) /* player found at center element */
14185 struct PlayerInfo *player = PLAYERINFO(x, y);
14187 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14188 border_element = Feld[xx][yy]; /* may be moving! */
14189 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14190 border_element = Feld[xx][yy];
14191 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14192 border_element = MovingOrBlocked2Element(xx, yy);
14194 continue; /* center and border element do not touch */
14196 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14197 player->index_bit, border_side);
14198 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14199 CE_PLAYER_TOUCHES_X,
14200 player->index_bit, border_side);
14202 #if USE_FIX_CE_ACTION_WITH_PLAYER
14204 /* use player element that is initially defined in the level playfield,
14205 not the player element that corresponds to the runtime player number
14206 (example: a level that contains EL_PLAYER_3 as the only player would
14207 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14208 int player_element = PLAYERINFO(x, y)->initial_element;
14210 CheckElementChangeBySide(xx, yy, border_element, player_element,
14211 CE_TOUCHING_X, border_side);
14215 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14217 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14219 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14221 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14222 continue; /* center and border element do not touch */
14225 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14226 player->index_bit, center_side);
14227 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14228 CE_PLAYER_TOUCHES_X,
14229 player->index_bit, center_side);
14231 #if USE_FIX_CE_ACTION_WITH_PLAYER
14233 /* use player element that is initially defined in the level playfield,
14234 not the player element that corresponds to the runtime player number
14235 (example: a level that contains EL_PLAYER_3 as the only player would
14236 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14237 int player_element = PLAYERINFO(xx, yy)->initial_element;
14239 CheckElementChangeBySide(x, y, center_element, player_element,
14240 CE_TOUCHING_X, center_side);
14249 #if USE_ELEMENT_TOUCHING_BUGFIX
14251 void TestIfElementTouchesCustomElement(int x, int y)
14253 static int xy[4][2] =
14260 static int trigger_sides[4][2] =
14262 /* center side border side */
14263 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14264 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14265 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14266 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14268 static int touch_dir[4] =
14270 MV_LEFT | MV_RIGHT,
14275 boolean change_center_element = FALSE;
14276 int center_element = Feld[x][y]; /* should always be non-moving! */
14277 int border_element_old[NUM_DIRECTIONS];
14280 for (i = 0; i < NUM_DIRECTIONS; i++)
14282 int xx = x + xy[i][0];
14283 int yy = y + xy[i][1];
14284 int border_element;
14286 border_element_old[i] = -1;
14288 if (!IN_LEV_FIELD(xx, yy))
14291 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14292 border_element = Feld[xx][yy]; /* may be moving! */
14293 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14294 border_element = Feld[xx][yy];
14295 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14296 border_element = MovingOrBlocked2Element(xx, yy);
14298 continue; /* center and border element do not touch */
14300 border_element_old[i] = border_element;
14303 for (i = 0; i < NUM_DIRECTIONS; i++)
14305 int xx = x + xy[i][0];
14306 int yy = y + xy[i][1];
14307 int center_side = trigger_sides[i][0];
14308 int border_element = border_element_old[i];
14310 if (border_element == -1)
14313 /* check for change of border element */
14314 CheckElementChangeBySide(xx, yy, border_element, center_element,
14315 CE_TOUCHING_X, center_side);
14317 /* (center element cannot be player, so we dont have to check this here) */
14320 for (i = 0; i < NUM_DIRECTIONS; i++)
14322 int xx = x + xy[i][0];
14323 int yy = y + xy[i][1];
14324 int border_side = trigger_sides[i][1];
14325 int border_element = border_element_old[i];
14327 if (border_element == -1)
14330 /* check for change of center element (but change it only once) */
14331 if (!change_center_element)
14332 change_center_element =
14333 CheckElementChangeBySide(x, y, center_element, border_element,
14334 CE_TOUCHING_X, border_side);
14336 #if USE_FIX_CE_ACTION_WITH_PLAYER
14337 if (IS_PLAYER(xx, yy))
14339 /* use player element that is initially defined in the level playfield,
14340 not the player element that corresponds to the runtime player number
14341 (example: a level that contains EL_PLAYER_3 as the only player would
14342 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14343 int player_element = PLAYERINFO(xx, yy)->initial_element;
14345 CheckElementChangeBySide(x, y, center_element, player_element,
14346 CE_TOUCHING_X, border_side);
14354 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14356 static int xy[4][2] =
14363 static int trigger_sides[4][2] =
14365 /* center side border side */
14366 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14367 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14368 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14369 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14371 static int touch_dir[4] =
14373 MV_LEFT | MV_RIGHT,
14378 boolean change_center_element = FALSE;
14379 int center_element = Feld[x][y]; /* should always be non-moving! */
14382 for (i = 0; i < NUM_DIRECTIONS; i++)
14384 int xx = x + xy[i][0];
14385 int yy = y + xy[i][1];
14386 int center_side = trigger_sides[i][0];
14387 int border_side = trigger_sides[i][1];
14388 int border_element;
14390 if (!IN_LEV_FIELD(xx, yy))
14393 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14394 border_element = Feld[xx][yy]; /* may be moving! */
14395 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14396 border_element = Feld[xx][yy];
14397 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14398 border_element = MovingOrBlocked2Element(xx, yy);
14400 continue; /* center and border element do not touch */
14402 /* check for change of center element (but change it only once) */
14403 if (!change_center_element)
14404 change_center_element =
14405 CheckElementChangeBySide(x, y, center_element, border_element,
14406 CE_TOUCHING_X, border_side);
14408 /* check for change of border element */
14409 CheckElementChangeBySide(xx, yy, border_element, center_element,
14410 CE_TOUCHING_X, center_side);
14416 void TestIfElementHitsCustomElement(int x, int y, int direction)
14418 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14419 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14420 int hitx = x + dx, hity = y + dy;
14421 int hitting_element = Feld[x][y];
14422 int touched_element;
14424 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14427 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14428 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14430 if (IN_LEV_FIELD(hitx, hity))
14432 int opposite_direction = MV_DIR_OPPOSITE(direction);
14433 int hitting_side = direction;
14434 int touched_side = opposite_direction;
14435 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14436 MovDir[hitx][hity] != direction ||
14437 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14443 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14444 CE_HITTING_X, touched_side);
14446 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14447 CE_HIT_BY_X, hitting_side);
14449 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14450 CE_HIT_BY_SOMETHING, opposite_direction);
14452 #if USE_FIX_CE_ACTION_WITH_PLAYER
14453 if (IS_PLAYER(hitx, hity))
14455 /* use player element that is initially defined in the level playfield,
14456 not the player element that corresponds to the runtime player number
14457 (example: a level that contains EL_PLAYER_3 as the only player would
14458 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14459 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14461 CheckElementChangeBySide(x, y, hitting_element, player_element,
14462 CE_HITTING_X, touched_side);
14468 /* "hitting something" is also true when hitting the playfield border */
14469 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14470 CE_HITTING_SOMETHING, direction);
14474 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14476 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14477 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14478 int hitx = x + dx, hity = y + dy;
14479 int hitting_element = Feld[x][y];
14480 int touched_element;
14482 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14483 !IS_FREE(hitx, hity) &&
14484 (!IS_MOVING(hitx, hity) ||
14485 MovDir[hitx][hity] != direction ||
14486 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14489 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14493 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14497 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14498 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14500 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14501 EP_CAN_SMASH_EVERYTHING, direction);
14503 if (IN_LEV_FIELD(hitx, hity))
14505 int opposite_direction = MV_DIR_OPPOSITE(direction);
14506 int hitting_side = direction;
14507 int touched_side = opposite_direction;
14509 int touched_element = MovingOrBlocked2Element(hitx, hity);
14512 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14513 MovDir[hitx][hity] != direction ||
14514 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14523 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14524 CE_SMASHED_BY_SOMETHING, opposite_direction);
14526 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14527 CE_OTHER_IS_SMASHING, touched_side);
14529 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14530 CE_OTHER_GETS_SMASHED, hitting_side);
14536 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14538 int i, kill_x = -1, kill_y = -1;
14540 int bad_element = -1;
14541 static int test_xy[4][2] =
14548 static int test_dir[4] =
14556 for (i = 0; i < NUM_DIRECTIONS; i++)
14558 int test_x, test_y, test_move_dir, test_element;
14560 test_x = good_x + test_xy[i][0];
14561 test_y = good_y + test_xy[i][1];
14563 if (!IN_LEV_FIELD(test_x, test_y))
14567 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14569 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14571 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14572 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14574 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14575 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14579 bad_element = test_element;
14585 if (kill_x != -1 || kill_y != -1)
14587 if (IS_PLAYER(good_x, good_y))
14589 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14591 if (player->shield_deadly_time_left > 0 &&
14592 !IS_INDESTRUCTIBLE(bad_element))
14593 Bang(kill_x, kill_y);
14594 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14595 KillPlayer(player);
14598 Bang(good_x, good_y);
14602 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14604 int i, kill_x = -1, kill_y = -1;
14605 int bad_element = Feld[bad_x][bad_y];
14606 static int test_xy[4][2] =
14613 static int touch_dir[4] =
14615 MV_LEFT | MV_RIGHT,
14620 static int test_dir[4] =
14628 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14631 for (i = 0; i < NUM_DIRECTIONS; i++)
14633 int test_x, test_y, test_move_dir, test_element;
14635 test_x = bad_x + test_xy[i][0];
14636 test_y = bad_y + test_xy[i][1];
14638 if (!IN_LEV_FIELD(test_x, test_y))
14642 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14644 test_element = Feld[test_x][test_y];
14646 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14647 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14649 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14650 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14652 /* good thing is player or penguin that does not move away */
14653 if (IS_PLAYER(test_x, test_y))
14655 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14657 if (bad_element == EL_ROBOT && player->is_moving)
14658 continue; /* robot does not kill player if he is moving */
14660 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14662 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14663 continue; /* center and border element do not touch */
14671 else if (test_element == EL_PENGUIN)
14681 if (kill_x != -1 || kill_y != -1)
14683 if (IS_PLAYER(kill_x, kill_y))
14685 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14687 if (player->shield_deadly_time_left > 0 &&
14688 !IS_INDESTRUCTIBLE(bad_element))
14689 Bang(bad_x, bad_y);
14690 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14691 KillPlayer(player);
14694 Bang(kill_x, kill_y);
14698 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14700 int bad_element = Feld[bad_x][bad_y];
14701 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14702 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14703 int test_x = bad_x + dx, test_y = bad_y + dy;
14704 int test_move_dir, test_element;
14705 int kill_x = -1, kill_y = -1;
14707 if (!IN_LEV_FIELD(test_x, test_y))
14711 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14713 test_element = Feld[test_x][test_y];
14715 if (test_move_dir != bad_move_dir)
14717 /* good thing can be player or penguin that does not move away */
14718 if (IS_PLAYER(test_x, test_y))
14720 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14722 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14723 player as being hit when he is moving towards the bad thing, because
14724 the "get hit by" condition would be lost after the player stops) */
14725 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14726 return; /* player moves away from bad thing */
14731 else if (test_element == EL_PENGUIN)
14738 if (kill_x != -1 || kill_y != -1)
14740 if (IS_PLAYER(kill_x, kill_y))
14742 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14744 if (player->shield_deadly_time_left > 0 &&
14745 !IS_INDESTRUCTIBLE(bad_element))
14746 Bang(bad_x, bad_y);
14747 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14748 KillPlayer(player);
14751 Bang(kill_x, kill_y);
14755 void TestIfPlayerTouchesBadThing(int x, int y)
14757 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14760 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14762 TestIfGoodThingHitsBadThing(x, y, move_dir);
14765 void TestIfBadThingTouchesPlayer(int x, int y)
14767 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14770 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14772 TestIfBadThingHitsGoodThing(x, y, move_dir);
14775 void TestIfFriendTouchesBadThing(int x, int y)
14777 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14780 void TestIfBadThingTouchesFriend(int x, int y)
14782 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14785 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14787 int i, kill_x = bad_x, kill_y = bad_y;
14788 static int xy[4][2] =
14796 for (i = 0; i < NUM_DIRECTIONS; i++)
14800 x = bad_x + xy[i][0];
14801 y = bad_y + xy[i][1];
14802 if (!IN_LEV_FIELD(x, y))
14805 element = Feld[x][y];
14806 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14807 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14815 if (kill_x != bad_x || kill_y != bad_y)
14816 Bang(bad_x, bad_y);
14819 void KillPlayer(struct PlayerInfo *player)
14821 int jx = player->jx, jy = player->jy;
14823 if (!player->active)
14827 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14828 player->killed, player->active, player->reanimated);
14831 /* the following code was introduced to prevent an infinite loop when calling
14833 -> CheckTriggeredElementChangeExt()
14834 -> ExecuteCustomElementAction()
14836 -> (infinitely repeating the above sequence of function calls)
14837 which occurs when killing the player while having a CE with the setting
14838 "kill player X when explosion of <player X>"; the solution using a new
14839 field "player->killed" was chosen for backwards compatibility, although
14840 clever use of the fields "player->active" etc. would probably also work */
14842 if (player->killed)
14846 player->killed = TRUE;
14848 /* remove accessible field at the player's position */
14849 Feld[jx][jy] = EL_EMPTY;
14851 /* deactivate shield (else Bang()/Explode() would not work right) */
14852 player->shield_normal_time_left = 0;
14853 player->shield_deadly_time_left = 0;
14856 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14857 player->killed, player->active, player->reanimated);
14863 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14864 player->killed, player->active, player->reanimated);
14867 #if USE_PLAYER_REANIMATION
14869 if (player->reanimated) /* killed player may have been reanimated */
14870 player->killed = player->reanimated = FALSE;
14872 BuryPlayer(player);
14874 if (player->killed) /* player may have been reanimated */
14875 BuryPlayer(player);
14878 BuryPlayer(player);
14882 static void KillPlayerUnlessEnemyProtected(int x, int y)
14884 if (!PLAYER_ENEMY_PROTECTED(x, y))
14885 KillPlayer(PLAYERINFO(x, y));
14888 static void KillPlayerUnlessExplosionProtected(int x, int y)
14890 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14891 KillPlayer(PLAYERINFO(x, y));
14894 void BuryPlayer(struct PlayerInfo *player)
14896 int jx = player->jx, jy = player->jy;
14898 if (!player->active)
14901 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14902 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14904 player->GameOver = TRUE;
14905 RemovePlayer(player);
14908 void RemovePlayer(struct PlayerInfo *player)
14910 int jx = player->jx, jy = player->jy;
14911 int i, found = FALSE;
14913 player->present = FALSE;
14914 player->active = FALSE;
14916 if (!ExplodeField[jx][jy])
14917 StorePlayer[jx][jy] = 0;
14919 if (player->is_moving)
14920 TEST_DrawLevelField(player->last_jx, player->last_jy);
14922 for (i = 0; i < MAX_PLAYERS; i++)
14923 if (stored_player[i].active)
14927 AllPlayersGone = TRUE;
14933 #if USE_NEW_SNAP_DELAY
14934 static void setFieldForSnapping(int x, int y, int element, int direction)
14936 struct ElementInfo *ei = &element_info[element];
14937 int direction_bit = MV_DIR_TO_BIT(direction);
14938 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14939 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14940 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14942 Feld[x][y] = EL_ELEMENT_SNAPPING;
14943 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14945 ResetGfxAnimation(x, y);
14947 GfxElement[x][y] = element;
14948 GfxAction[x][y] = action;
14949 GfxDir[x][y] = direction;
14950 GfxFrame[x][y] = -1;
14955 =============================================================================
14956 checkDiagonalPushing()
14957 -----------------------------------------------------------------------------
14958 check if diagonal input device direction results in pushing of object
14959 (by checking if the alternative direction is walkable, diggable, ...)
14960 =============================================================================
14963 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14964 int x, int y, int real_dx, int real_dy)
14966 int jx, jy, dx, dy, xx, yy;
14968 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14971 /* diagonal direction: check alternative direction */
14976 xx = jx + (dx == 0 ? real_dx : 0);
14977 yy = jy + (dy == 0 ? real_dy : 0);
14979 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14983 =============================================================================
14985 -----------------------------------------------------------------------------
14986 x, y: field next to player (non-diagonal) to try to dig to
14987 real_dx, real_dy: direction as read from input device (can be diagonal)
14988 =============================================================================
14991 static int DigField(struct PlayerInfo *player,
14992 int oldx, int oldy, int x, int y,
14993 int real_dx, int real_dy, int mode)
14995 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14996 boolean player_was_pushing = player->is_pushing;
14997 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14998 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14999 int jx = oldx, jy = oldy;
15000 int dx = x - jx, dy = y - jy;
15001 int nextx = x + dx, nexty = y + dy;
15002 int move_direction = (dx == -1 ? MV_LEFT :
15003 dx == +1 ? MV_RIGHT :
15005 dy == +1 ? MV_DOWN : MV_NONE);
15006 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15007 int dig_side = MV_DIR_OPPOSITE(move_direction);
15008 int old_element = Feld[jx][jy];
15009 #if USE_FIXED_DONT_RUN_INTO
15010 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15016 if (is_player) /* function can also be called by EL_PENGUIN */
15018 if (player->MovPos == 0)
15020 player->is_digging = FALSE;
15021 player->is_collecting = FALSE;
15024 if (player->MovPos == 0) /* last pushing move finished */
15025 player->is_pushing = FALSE;
15027 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15029 player->is_switching = FALSE;
15030 player->push_delay = -1;
15032 return MP_NO_ACTION;
15036 #if !USE_FIXED_DONT_RUN_INTO
15037 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15038 return MP_NO_ACTION;
15041 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15042 old_element = Back[jx][jy];
15044 /* in case of element dropped at player position, check background */
15045 else if (Back[jx][jy] != EL_EMPTY &&
15046 game.engine_version >= VERSION_IDENT(2,2,0,0))
15047 old_element = Back[jx][jy];
15049 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15050 return MP_NO_ACTION; /* field has no opening in this direction */
15052 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15053 return MP_NO_ACTION; /* field has no opening in this direction */
15055 #if USE_FIXED_DONT_RUN_INTO
15056 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15060 Feld[jx][jy] = player->artwork_element;
15061 InitMovingField(jx, jy, MV_DOWN);
15062 Store[jx][jy] = EL_ACID;
15063 ContinueMoving(jx, jy);
15064 BuryPlayer(player);
15066 return MP_DONT_RUN_INTO;
15070 #if USE_FIXED_DONT_RUN_INTO
15071 if (player_can_move && DONT_RUN_INTO(element))
15073 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15075 return MP_DONT_RUN_INTO;
15079 #if USE_FIXED_DONT_RUN_INTO
15080 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15081 return MP_NO_ACTION;
15084 #if !USE_FIXED_DONT_RUN_INTO
15085 element = Feld[x][y];
15088 collect_count = element_info[element].collect_count_initial;
15090 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15091 return MP_NO_ACTION;
15093 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15094 player_can_move = player_can_move_or_snap;
15096 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15097 game.engine_version >= VERSION_IDENT(2,2,0,0))
15099 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15100 player->index_bit, dig_side);
15101 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15102 player->index_bit, dig_side);
15104 if (element == EL_DC_LANDMINE)
15107 if (Feld[x][y] != element) /* field changed by snapping */
15110 return MP_NO_ACTION;
15113 #if USE_PLAYER_GRAVITY
15114 if (player->gravity && is_player && !player->is_auto_moving &&
15115 canFallDown(player) && move_direction != MV_DOWN &&
15116 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15117 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15119 if (game.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 */
15125 if (player_can_move &&
15126 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15128 int sound_element = SND_ELEMENT(element);
15129 int sound_action = ACTION_WALKING;
15131 if (IS_RND_GATE(element))
15133 if (!player->key[RND_GATE_NR(element)])
15134 return MP_NO_ACTION;
15136 else if (IS_RND_GATE_GRAY(element))
15138 if (!player->key[RND_GATE_GRAY_NR(element)])
15139 return MP_NO_ACTION;
15141 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15143 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15144 return MP_NO_ACTION;
15146 else if (element == EL_EXIT_OPEN ||
15147 element == EL_EM_EXIT_OPEN ||
15149 element == EL_EM_EXIT_OPENING ||
15151 element == EL_STEEL_EXIT_OPEN ||
15152 element == EL_EM_STEEL_EXIT_OPEN ||
15154 element == EL_EM_STEEL_EXIT_OPENING ||
15156 element == EL_SP_EXIT_OPEN ||
15157 element == EL_SP_EXIT_OPENING)
15159 sound_action = ACTION_PASSING; /* player is passing exit */
15161 else if (element == EL_EMPTY)
15163 sound_action = ACTION_MOVING; /* nothing to walk on */
15166 /* play sound from background or player, whatever is available */
15167 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15168 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15170 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15172 else if (player_can_move &&
15173 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15175 if (!ACCESS_FROM(element, opposite_direction))
15176 return MP_NO_ACTION; /* field not accessible from this direction */
15178 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15179 return MP_NO_ACTION;
15181 if (IS_EM_GATE(element))
15183 if (!player->key[EM_GATE_NR(element)])
15184 return MP_NO_ACTION;
15186 else if (IS_EM_GATE_GRAY(element))
15188 if (!player->key[EM_GATE_GRAY_NR(element)])
15189 return MP_NO_ACTION;
15191 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15193 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15194 return MP_NO_ACTION;
15196 else if (IS_EMC_GATE(element))
15198 if (!player->key[EMC_GATE_NR(element)])
15199 return MP_NO_ACTION;
15201 else if (IS_EMC_GATE_GRAY(element))
15203 if (!player->key[EMC_GATE_GRAY_NR(element)])
15204 return MP_NO_ACTION;
15206 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15208 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15209 return MP_NO_ACTION;
15211 else if (element == EL_DC_GATE_WHITE ||
15212 element == EL_DC_GATE_WHITE_GRAY ||
15213 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15215 if (player->num_white_keys == 0)
15216 return MP_NO_ACTION;
15218 player->num_white_keys--;
15220 else if (IS_SP_PORT(element))
15222 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15223 element == EL_SP_GRAVITY_PORT_RIGHT ||
15224 element == EL_SP_GRAVITY_PORT_UP ||
15225 element == EL_SP_GRAVITY_PORT_DOWN)
15226 #if USE_PLAYER_GRAVITY
15227 player->gravity = !player->gravity;
15229 game.gravity = !game.gravity;
15231 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15232 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15233 element == EL_SP_GRAVITY_ON_PORT_UP ||
15234 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15235 #if USE_PLAYER_GRAVITY
15236 player->gravity = TRUE;
15238 game.gravity = TRUE;
15240 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15241 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15242 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15243 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15244 #if USE_PLAYER_GRAVITY
15245 player->gravity = FALSE;
15247 game.gravity = FALSE;
15251 /* automatically move to the next field with double speed */
15252 player->programmed_action = move_direction;
15254 if (player->move_delay_reset_counter == 0)
15256 player->move_delay_reset_counter = 2; /* two double speed steps */
15258 DOUBLE_PLAYER_SPEED(player);
15261 PlayLevelSoundAction(x, y, ACTION_PASSING);
15263 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15267 if (mode != DF_SNAP)
15269 GfxElement[x][y] = GFX_ELEMENT(element);
15270 player->is_digging = TRUE;
15273 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15275 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15276 player->index_bit, dig_side);
15278 if (mode == DF_SNAP)
15280 #if USE_NEW_SNAP_DELAY
15281 if (level.block_snap_field)
15282 setFieldForSnapping(x, y, element, move_direction);
15284 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15286 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15289 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15290 player->index_bit, dig_side);
15293 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15297 if (is_player && mode != DF_SNAP)
15299 GfxElement[x][y] = element;
15300 player->is_collecting = TRUE;
15303 if (element == EL_SPEED_PILL)
15305 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15307 else if (element == EL_EXTRA_TIME && level.time > 0)
15309 TimeLeft += level.extra_time;
15312 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15314 DisplayGameControlValues();
15316 DrawGameValue_Time(TimeLeft);
15319 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15321 player->shield_normal_time_left += level.shield_normal_time;
15322 if (element == EL_SHIELD_DEADLY)
15323 player->shield_deadly_time_left += level.shield_deadly_time;
15325 else if (element == EL_DYNAMITE ||
15326 element == EL_EM_DYNAMITE ||
15327 element == EL_SP_DISK_RED)
15329 if (player->inventory_size < MAX_INVENTORY_SIZE)
15330 player->inventory_element[player->inventory_size++] = element;
15332 DrawGameDoorValues();
15334 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15336 player->dynabomb_count++;
15337 player->dynabombs_left++;
15339 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15341 player->dynabomb_size++;
15343 else if (element == EL_DYNABOMB_INCREASE_POWER)
15345 player->dynabomb_xl = TRUE;
15347 else if (IS_KEY(element))
15349 player->key[KEY_NR(element)] = TRUE;
15351 DrawGameDoorValues();
15353 else if (element == EL_DC_KEY_WHITE)
15355 player->num_white_keys++;
15357 /* display white keys? */
15358 /* DrawGameDoorValues(); */
15360 else if (IS_ENVELOPE(element))
15362 player->show_envelope = element;
15364 else if (element == EL_EMC_LENSES)
15366 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15368 RedrawAllInvisibleElementsForLenses();
15370 else if (element == EL_EMC_MAGNIFIER)
15372 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15374 RedrawAllInvisibleElementsForMagnifier();
15376 else if (IS_DROPPABLE(element) ||
15377 IS_THROWABLE(element)) /* can be collected and dropped */
15381 if (collect_count == 0)
15382 player->inventory_infinite_element = element;
15384 for (i = 0; i < collect_count; i++)
15385 if (player->inventory_size < MAX_INVENTORY_SIZE)
15386 player->inventory_element[player->inventory_size++] = element;
15388 DrawGameDoorValues();
15390 else if (collect_count > 0)
15392 local_player->gems_still_needed -= collect_count;
15393 if (local_player->gems_still_needed < 0)
15394 local_player->gems_still_needed = 0;
15397 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15399 DisplayGameControlValues();
15401 DrawGameValue_Emeralds(local_player->gems_still_needed);
15405 RaiseScoreElement(element);
15406 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15409 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15410 player->index_bit, dig_side);
15412 if (mode == DF_SNAP)
15414 #if USE_NEW_SNAP_DELAY
15415 if (level.block_snap_field)
15416 setFieldForSnapping(x, y, element, move_direction);
15418 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15420 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15423 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15424 player->index_bit, dig_side);
15427 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15429 if (mode == DF_SNAP && element != EL_BD_ROCK)
15430 return MP_NO_ACTION;
15432 if (CAN_FALL(element) && dy)
15433 return MP_NO_ACTION;
15435 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15436 !(element == EL_SPRING && level.use_spring_bug))
15437 return MP_NO_ACTION;
15439 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15440 ((move_direction & MV_VERTICAL &&
15441 ((element_info[element].move_pattern & MV_LEFT &&
15442 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15443 (element_info[element].move_pattern & MV_RIGHT &&
15444 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15445 (move_direction & MV_HORIZONTAL &&
15446 ((element_info[element].move_pattern & MV_UP &&
15447 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15448 (element_info[element].move_pattern & MV_DOWN &&
15449 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15450 return MP_NO_ACTION;
15452 /* do not push elements already moving away faster than player */
15453 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15454 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15455 return MP_NO_ACTION;
15457 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15459 if (player->push_delay_value == -1 || !player_was_pushing)
15460 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15462 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15464 if (player->push_delay_value == -1)
15465 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15467 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15469 if (!player->is_pushing)
15470 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15473 player->is_pushing = TRUE;
15474 player->is_active = TRUE;
15476 if (!(IN_LEV_FIELD(nextx, nexty) &&
15477 (IS_FREE(nextx, nexty) ||
15478 (IS_SB_ELEMENT(element) &&
15479 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15480 (IS_CUSTOM_ELEMENT(element) &&
15481 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15482 return MP_NO_ACTION;
15484 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15485 return MP_NO_ACTION;
15487 if (player->push_delay == -1) /* new pushing; restart delay */
15488 player->push_delay = 0;
15490 if (player->push_delay < player->push_delay_value &&
15491 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15492 element != EL_SPRING && element != EL_BALLOON)
15494 /* make sure that there is no move delay before next try to push */
15495 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15496 player->move_delay = 0;
15498 return MP_NO_ACTION;
15501 if (IS_CUSTOM_ELEMENT(element) &&
15502 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15504 if (!DigFieldByCE(nextx, nexty, element))
15505 return MP_NO_ACTION;
15508 if (IS_SB_ELEMENT(element))
15510 if (element == EL_SOKOBAN_FIELD_FULL)
15512 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15513 local_player->sokobanfields_still_needed++;
15516 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15518 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15519 local_player->sokobanfields_still_needed--;
15522 Feld[x][y] = EL_SOKOBAN_OBJECT;
15524 if (Back[x][y] == Back[nextx][nexty])
15525 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15526 else if (Back[x][y] != 0)
15527 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15530 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15534 if (local_player->sokobanfields_still_needed == 0 &&
15535 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15537 if (local_player->sokobanfields_still_needed == 0 &&
15538 game.emulation == EMU_SOKOBAN)
15541 PlayerWins(player);
15543 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15547 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15549 InitMovingField(x, y, move_direction);
15550 GfxAction[x][y] = ACTION_PUSHING;
15552 if (mode == DF_SNAP)
15553 ContinueMoving(x, y);
15555 MovPos[x][y] = (dx != 0 ? dx : dy);
15557 Pushed[x][y] = TRUE;
15558 Pushed[nextx][nexty] = TRUE;
15560 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15561 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15563 player->push_delay_value = -1; /* get new value later */
15565 /* check for element change _after_ element has been pushed */
15566 if (game.use_change_when_pushing_bug)
15568 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15569 player->index_bit, dig_side);
15570 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15571 player->index_bit, dig_side);
15574 else if (IS_SWITCHABLE(element))
15576 if (PLAYER_SWITCHING(player, x, y))
15578 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15579 player->index_bit, dig_side);
15584 player->is_switching = TRUE;
15585 player->switch_x = x;
15586 player->switch_y = y;
15588 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15590 if (element == EL_ROBOT_WHEEL)
15592 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15596 game.robot_wheel_active = TRUE;
15598 TEST_DrawLevelField(x, y);
15600 else if (element == EL_SP_TERMINAL)
15604 SCAN_PLAYFIELD(xx, yy)
15606 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15608 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15609 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15612 else if (IS_BELT_SWITCH(element))
15614 ToggleBeltSwitch(x, y);
15616 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15617 element == EL_SWITCHGATE_SWITCH_DOWN ||
15618 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15619 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15621 ToggleSwitchgateSwitch(x, y);
15623 else if (element == EL_LIGHT_SWITCH ||
15624 element == EL_LIGHT_SWITCH_ACTIVE)
15626 ToggleLightSwitch(x, y);
15628 else if (element == EL_TIMEGATE_SWITCH ||
15629 element == EL_DC_TIMEGATE_SWITCH)
15631 ActivateTimegateSwitch(x, y);
15633 else if (element == EL_BALLOON_SWITCH_LEFT ||
15634 element == EL_BALLOON_SWITCH_RIGHT ||
15635 element == EL_BALLOON_SWITCH_UP ||
15636 element == EL_BALLOON_SWITCH_DOWN ||
15637 element == EL_BALLOON_SWITCH_NONE ||
15638 element == EL_BALLOON_SWITCH_ANY)
15640 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15641 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15642 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15643 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15644 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15647 else if (element == EL_LAMP)
15649 Feld[x][y] = EL_LAMP_ACTIVE;
15650 local_player->lights_still_needed--;
15652 ResetGfxAnimation(x, y);
15653 TEST_DrawLevelField(x, y);
15655 else if (element == EL_TIME_ORB_FULL)
15657 Feld[x][y] = EL_TIME_ORB_EMPTY;
15659 if (level.time > 0 || level.use_time_orb_bug)
15661 TimeLeft += level.time_orb_time;
15662 game.no_time_limit = FALSE;
15665 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15667 DisplayGameControlValues();
15669 DrawGameValue_Time(TimeLeft);
15673 ResetGfxAnimation(x, y);
15674 TEST_DrawLevelField(x, y);
15676 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15677 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15681 game.ball_state = !game.ball_state;
15683 SCAN_PLAYFIELD(xx, yy)
15685 int e = Feld[xx][yy];
15687 if (game.ball_state)
15689 if (e == EL_EMC_MAGIC_BALL)
15690 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15691 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15692 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15696 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15697 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15698 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15699 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15704 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15705 player->index_bit, dig_side);
15707 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15708 player->index_bit, dig_side);
15710 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15711 player->index_bit, dig_side);
15717 if (!PLAYER_SWITCHING(player, x, y))
15719 player->is_switching = TRUE;
15720 player->switch_x = x;
15721 player->switch_y = y;
15723 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15724 player->index_bit, dig_side);
15725 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15726 player->index_bit, dig_side);
15728 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15729 player->index_bit, dig_side);
15730 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15731 player->index_bit, dig_side);
15734 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15735 player->index_bit, dig_side);
15736 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15737 player->index_bit, dig_side);
15739 return MP_NO_ACTION;
15742 player->push_delay = -1;
15744 if (is_player) /* function can also be called by EL_PENGUIN */
15746 if (Feld[x][y] != element) /* really digged/collected something */
15748 player->is_collecting = !player->is_digging;
15749 player->is_active = TRUE;
15756 static boolean DigFieldByCE(int x, int y, int digging_element)
15758 int element = Feld[x][y];
15760 if (!IS_FREE(x, y))
15762 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15763 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15766 /* no element can dig solid indestructible elements */
15767 if (IS_INDESTRUCTIBLE(element) &&
15768 !IS_DIGGABLE(element) &&
15769 !IS_COLLECTIBLE(element))
15772 if (AmoebaNr[x][y] &&
15773 (element == EL_AMOEBA_FULL ||
15774 element == EL_BD_AMOEBA ||
15775 element == EL_AMOEBA_GROWING))
15777 AmoebaCnt[AmoebaNr[x][y]]--;
15778 AmoebaCnt2[AmoebaNr[x][y]]--;
15781 if (IS_MOVING(x, y))
15782 RemoveMovingField(x, y);
15786 TEST_DrawLevelField(x, y);
15789 /* if digged element was about to explode, prevent the explosion */
15790 ExplodeField[x][y] = EX_TYPE_NONE;
15792 PlayLevelSoundAction(x, y, action);
15795 Store[x][y] = EL_EMPTY;
15798 /* this makes it possible to leave the removed element again */
15799 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15800 Store[x][y] = element;
15802 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15804 int move_leave_element = element_info[digging_element].move_leave_element;
15806 /* this makes it possible to leave the removed element again */
15807 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15808 element : move_leave_element);
15815 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15817 int jx = player->jx, jy = player->jy;
15818 int x = jx + dx, y = jy + dy;
15819 int snap_direction = (dx == -1 ? MV_LEFT :
15820 dx == +1 ? MV_RIGHT :
15822 dy == +1 ? MV_DOWN : MV_NONE);
15823 boolean can_continue_snapping = (level.continuous_snapping &&
15824 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15826 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15829 if (!player->active || !IN_LEV_FIELD(x, y))
15837 if (player->MovPos == 0)
15838 player->is_pushing = FALSE;
15840 player->is_snapping = FALSE;
15842 if (player->MovPos == 0)
15844 player->is_moving = FALSE;
15845 player->is_digging = FALSE;
15846 player->is_collecting = FALSE;
15852 #if USE_NEW_CONTINUOUS_SNAPPING
15853 /* prevent snapping with already pressed snap key when not allowed */
15854 if (player->is_snapping && !can_continue_snapping)
15857 if (player->is_snapping)
15861 player->MovDir = snap_direction;
15863 if (player->MovPos == 0)
15865 player->is_moving = FALSE;
15866 player->is_digging = FALSE;
15867 player->is_collecting = FALSE;
15870 player->is_dropping = FALSE;
15871 player->is_dropping_pressed = FALSE;
15872 player->drop_pressed_delay = 0;
15874 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15877 player->is_snapping = TRUE;
15878 player->is_active = TRUE;
15880 if (player->MovPos == 0)
15882 player->is_moving = FALSE;
15883 player->is_digging = FALSE;
15884 player->is_collecting = FALSE;
15887 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15888 TEST_DrawLevelField(player->last_jx, player->last_jy);
15890 TEST_DrawLevelField(x, y);
15895 static boolean DropElement(struct PlayerInfo *player)
15897 int old_element, new_element;
15898 int dropx = player->jx, dropy = player->jy;
15899 int drop_direction = player->MovDir;
15900 int drop_side = drop_direction;
15902 int drop_element = get_next_dropped_element(player);
15904 int drop_element = (player->inventory_size > 0 ?
15905 player->inventory_element[player->inventory_size - 1] :
15906 player->inventory_infinite_element != EL_UNDEFINED ?
15907 player->inventory_infinite_element :
15908 player->dynabombs_left > 0 ?
15909 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15913 player->is_dropping_pressed = TRUE;
15915 /* do not drop an element on top of another element; when holding drop key
15916 pressed without moving, dropped element must move away before the next
15917 element can be dropped (this is especially important if the next element
15918 is dynamite, which can be placed on background for historical reasons) */
15919 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15922 if (IS_THROWABLE(drop_element))
15924 dropx += GET_DX_FROM_DIR(drop_direction);
15925 dropy += GET_DY_FROM_DIR(drop_direction);
15927 if (!IN_LEV_FIELD(dropx, dropy))
15931 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15932 new_element = drop_element; /* default: no change when dropping */
15934 /* check if player is active, not moving and ready to drop */
15935 if (!player->active || player->MovPos || player->drop_delay > 0)
15938 /* check if player has anything that can be dropped */
15939 if (new_element == EL_UNDEFINED)
15942 /* check if drop key was pressed long enough for EM style dynamite */
15943 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15946 /* check if anything can be dropped at the current position */
15947 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15950 /* collected custom elements can only be dropped on empty fields */
15951 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15954 if (old_element != EL_EMPTY)
15955 Back[dropx][dropy] = old_element; /* store old element on this field */
15957 ResetGfxAnimation(dropx, dropy);
15958 ResetRandomAnimationValue(dropx, dropy);
15960 if (player->inventory_size > 0 ||
15961 player->inventory_infinite_element != EL_UNDEFINED)
15963 if (player->inventory_size > 0)
15965 player->inventory_size--;
15967 DrawGameDoorValues();
15969 if (new_element == EL_DYNAMITE)
15970 new_element = EL_DYNAMITE_ACTIVE;
15971 else if (new_element == EL_EM_DYNAMITE)
15972 new_element = EL_EM_DYNAMITE_ACTIVE;
15973 else if (new_element == EL_SP_DISK_RED)
15974 new_element = EL_SP_DISK_RED_ACTIVE;
15977 Feld[dropx][dropy] = new_element;
15979 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15980 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15981 el2img(Feld[dropx][dropy]), 0);
15983 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15985 /* needed if previous element just changed to "empty" in the last frame */
15986 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15988 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15989 player->index_bit, drop_side);
15990 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15992 player->index_bit, drop_side);
15994 TestIfElementTouchesCustomElement(dropx, dropy);
15996 else /* player is dropping a dyna bomb */
15998 player->dynabombs_left--;
16000 Feld[dropx][dropy] = new_element;
16002 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16003 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16004 el2img(Feld[dropx][dropy]), 0);
16006 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16009 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16010 InitField_WithBug1(dropx, dropy, FALSE);
16012 new_element = Feld[dropx][dropy]; /* element might have changed */
16014 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16015 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16018 int move_direction;
16022 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16023 MovDir[dropx][dropy] = drop_direction;
16026 move_direction = MovDir[dropx][dropy];
16027 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16028 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16031 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16033 #if USE_FIX_IMPACT_COLLISION
16034 /* do not cause impact style collision by dropping elements that can fall */
16035 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16037 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16041 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16042 player->is_dropping = TRUE;
16044 player->drop_pressed_delay = 0;
16045 player->is_dropping_pressed = FALSE;
16047 player->drop_x = dropx;
16048 player->drop_y = dropy;
16053 /* ------------------------------------------------------------------------- */
16054 /* game sound playing functions */
16055 /* ------------------------------------------------------------------------- */
16057 static int *loop_sound_frame = NULL;
16058 static int *loop_sound_volume = NULL;
16060 void InitPlayLevelSound()
16062 int num_sounds = getSoundListSize();
16064 checked_free(loop_sound_frame);
16065 checked_free(loop_sound_volume);
16067 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16068 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16071 static void PlayLevelSound(int x, int y, int nr)
16073 int sx = SCREENX(x), sy = SCREENY(y);
16074 int volume, stereo_position;
16075 int max_distance = 8;
16076 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16078 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16079 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16082 if (!IN_LEV_FIELD(x, y) ||
16083 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16084 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16087 volume = SOUND_MAX_VOLUME;
16089 if (!IN_SCR_FIELD(sx, sy))
16091 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16092 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16094 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16097 stereo_position = (SOUND_MAX_LEFT +
16098 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16099 (SCR_FIELDX + 2 * max_distance));
16101 if (IS_LOOP_SOUND(nr))
16103 /* This assures that quieter loop sounds do not overwrite louder ones,
16104 while restarting sound volume comparison with each new game frame. */
16106 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16109 loop_sound_volume[nr] = volume;
16110 loop_sound_frame[nr] = FrameCounter;
16113 PlaySoundExt(nr, volume, stereo_position, type);
16116 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16118 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16119 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16120 y < LEVELY(BY1) ? LEVELY(BY1) :
16121 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16125 static void PlayLevelSoundAction(int x, int y, int action)
16127 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16130 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16132 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16134 if (sound_effect != SND_UNDEFINED)
16135 PlayLevelSound(x, y, sound_effect);
16138 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16141 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16143 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16144 PlayLevelSound(x, y, sound_effect);
16147 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16149 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16151 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16152 PlayLevelSound(x, y, sound_effect);
16155 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16157 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16159 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16160 StopSound(sound_effect);
16163 static void PlayLevelMusic()
16165 if (levelset.music[level_nr] != MUS_UNDEFINED)
16166 PlayMusic(levelset.music[level_nr]); /* from config file */
16168 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16171 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16173 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16174 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16175 int x = xx - 1 - offset;
16176 int y = yy - 1 - offset;
16181 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16185 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16189 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16193 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16197 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16201 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16205 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16208 case SAMPLE_android_clone:
16209 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16212 case SAMPLE_android_move:
16213 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16216 case SAMPLE_spring:
16217 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16221 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16225 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16228 case SAMPLE_eater_eat:
16229 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16233 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16236 case SAMPLE_collect:
16237 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16240 case SAMPLE_diamond:
16241 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16244 case SAMPLE_squash:
16245 /* !!! CHECK THIS !!! */
16247 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16249 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16253 case SAMPLE_wonderfall:
16254 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16258 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16262 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16266 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16270 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16274 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16278 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16281 case SAMPLE_wonder:
16282 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16286 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16289 case SAMPLE_exit_open:
16290 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16293 case SAMPLE_exit_leave:
16294 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16297 case SAMPLE_dynamite:
16298 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16302 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16306 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16310 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16314 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16318 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16322 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16326 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16331 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16333 int element = map_element_SP_to_RND(element_sp);
16334 int action = map_action_SP_to_RND(action_sp);
16335 int offset = (setup.sp_show_border_elements ? 0 : 1);
16336 int x = xx - offset;
16337 int y = yy - offset;
16340 printf("::: %d -> %d\n", element_sp, action_sp);
16343 PlayLevelSoundElementAction(x, y, element, action);
16346 void RaiseScore(int value)
16348 local_player->score += value;
16351 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16353 DisplayGameControlValues();
16355 DrawGameValue_Score(local_player->score);
16359 void RaiseScoreElement(int element)
16364 case EL_BD_DIAMOND:
16365 case EL_EMERALD_YELLOW:
16366 case EL_EMERALD_RED:
16367 case EL_EMERALD_PURPLE:
16368 case EL_SP_INFOTRON:
16369 RaiseScore(level.score[SC_EMERALD]);
16372 RaiseScore(level.score[SC_DIAMOND]);
16375 RaiseScore(level.score[SC_CRYSTAL]);
16378 RaiseScore(level.score[SC_PEARL]);
16381 case EL_BD_BUTTERFLY:
16382 case EL_SP_ELECTRON:
16383 RaiseScore(level.score[SC_BUG]);
16386 case EL_BD_FIREFLY:
16387 case EL_SP_SNIKSNAK:
16388 RaiseScore(level.score[SC_SPACESHIP]);
16391 case EL_DARK_YAMYAM:
16392 RaiseScore(level.score[SC_YAMYAM]);
16395 RaiseScore(level.score[SC_ROBOT]);
16398 RaiseScore(level.score[SC_PACMAN]);
16401 RaiseScore(level.score[SC_NUT]);
16404 case EL_EM_DYNAMITE:
16405 case EL_SP_DISK_RED:
16406 case EL_DYNABOMB_INCREASE_NUMBER:
16407 case EL_DYNABOMB_INCREASE_SIZE:
16408 case EL_DYNABOMB_INCREASE_POWER:
16409 RaiseScore(level.score[SC_DYNAMITE]);
16411 case EL_SHIELD_NORMAL:
16412 case EL_SHIELD_DEADLY:
16413 RaiseScore(level.score[SC_SHIELD]);
16415 case EL_EXTRA_TIME:
16416 RaiseScore(level.extra_time_score);
16430 case EL_DC_KEY_WHITE:
16431 RaiseScore(level.score[SC_KEY]);
16434 RaiseScore(element_info[element].collect_score);
16439 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16441 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16443 #if defined(NETWORK_AVALIABLE)
16444 if (options.network)
16445 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16454 FadeSkipNextFadeIn();
16456 fading = fading_none;
16460 OpenDoor(DOOR_CLOSE_1);
16463 game_status = GAME_MODE_MAIN;
16466 DrawAndFadeInMainMenu(REDRAW_FIELD);
16474 FadeOut(REDRAW_FIELD);
16477 game_status = GAME_MODE_MAIN;
16479 DrawAndFadeInMainMenu(REDRAW_FIELD);
16483 else /* continue playing the game */
16485 if (tape.playing && tape.deactivate_display)
16486 TapeDeactivateDisplayOff(TRUE);
16488 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16490 if (tape.playing && tape.deactivate_display)
16491 TapeDeactivateDisplayOn();
16495 void RequestQuitGame(boolean ask_if_really_quit)
16497 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16498 boolean skip_request = AllPlayersGone || quick_quit;
16500 RequestQuitGameExt(skip_request, quick_quit,
16501 "Do you really want to quit the game?");
16505 /* ------------------------------------------------------------------------- */
16506 /* random generator functions */
16507 /* ------------------------------------------------------------------------- */
16509 unsigned int InitEngineRandom_RND(int seed)
16511 game.num_random_calls = 0;
16514 unsigned int rnd_seed = InitEngineRandom(seed);
16516 printf("::: START RND: %d\n", rnd_seed);
16521 return InitEngineRandom(seed);
16527 unsigned int RND(int max)
16531 game.num_random_calls++;
16533 return GetEngineRandom(max);
16540 /* ------------------------------------------------------------------------- */
16541 /* game engine snapshot handling functions */
16542 /* ------------------------------------------------------------------------- */
16544 struct EngineSnapshotInfo
16546 /* runtime values for custom element collect score */
16547 int collect_score[NUM_CUSTOM_ELEMENTS];
16549 /* runtime values for group element choice position */
16550 int choice_pos[NUM_GROUP_ELEMENTS];
16552 /* runtime values for belt position animations */
16553 int belt_graphic[4][NUM_BELT_PARTS];
16554 int belt_anim_mode[4][NUM_BELT_PARTS];
16557 static struct EngineSnapshotInfo engine_snapshot_rnd;
16558 static char *snapshot_level_identifier = NULL;
16559 static int snapshot_level_nr = -1;
16561 static void SaveEngineSnapshotValues_RND()
16563 static int belt_base_active_element[4] =
16565 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16566 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16567 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16568 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16572 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16574 int element = EL_CUSTOM_START + i;
16576 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16579 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16581 int element = EL_GROUP_START + i;
16583 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16586 for (i = 0; i < 4; i++)
16588 for (j = 0; j < NUM_BELT_PARTS; j++)
16590 int element = belt_base_active_element[i] + j;
16591 int graphic = el2img(element);
16592 int anim_mode = graphic_info[graphic].anim_mode;
16594 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16595 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16600 static void LoadEngineSnapshotValues_RND()
16602 unsigned int num_random_calls = game.num_random_calls;
16605 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16607 int element = EL_CUSTOM_START + i;
16609 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16612 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16614 int element = EL_GROUP_START + i;
16616 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16619 for (i = 0; i < 4; i++)
16621 for (j = 0; j < NUM_BELT_PARTS; j++)
16623 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16624 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16626 graphic_info[graphic].anim_mode = anim_mode;
16630 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16632 InitRND(tape.random_seed);
16633 for (i = 0; i < num_random_calls; i++)
16637 if (game.num_random_calls != num_random_calls)
16639 Error(ERR_INFO, "number of random calls out of sync");
16640 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16641 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16642 Error(ERR_EXIT, "this should not happen -- please debug");
16646 void SaveEngineSnapshot()
16648 /* do not save snapshots from editor */
16649 if (level_editor_test_game)
16652 /* free previous snapshot buffers, if needed */
16653 FreeEngineSnapshotBuffers();
16655 /* copy some special values to a structure better suited for the snapshot */
16657 SaveEngineSnapshotValues_RND();
16658 SaveEngineSnapshotValues_EM();
16659 SaveEngineSnapshotValues_SP();
16661 /* save values stored in special snapshot structure */
16663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16665 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16667 /* save further RND engine values */
16669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16682 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16688 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16715 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16733 /* save level identification information */
16735 setString(&snapshot_level_identifier, leveldir_current->identifier);
16736 snapshot_level_nr = level_nr;
16739 ListNode *node = engine_snapshot_list_rnd;
16742 while (node != NULL)
16744 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16749 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16753 void LoadEngineSnapshot()
16755 /* restore generically stored snapshot buffers */
16757 LoadEngineSnapshotBuffers();
16759 /* restore special values from snapshot structure */
16761 LoadEngineSnapshotValues_RND();
16762 LoadEngineSnapshotValues_EM();
16763 LoadEngineSnapshotValues_SP();
16766 boolean CheckEngineSnapshot()
16768 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16769 snapshot_level_nr == level_nr);
16773 /* ---------- new game button stuff ---------------------------------------- */
16781 } gamebutton_info[NUM_GAME_BUTTONS] =
16784 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16785 GAME_CTRL_ID_STOP, "stop game"
16788 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16789 GAME_CTRL_ID_PAUSE, "pause game"
16792 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16793 GAME_CTRL_ID_PLAY, "play game"
16796 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16797 SOUND_CTRL_ID_MUSIC, "background music on/off"
16800 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16801 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16804 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16805 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16809 void CreateGameButtons()
16813 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16815 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16816 struct Rect *pos = gamebutton_info[i].pos;
16817 struct GadgetInfo *gi;
16820 unsigned int event_mask;
16821 int gd_x = gfx->src_x;
16822 int gd_y = gfx->src_y;
16823 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16824 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16825 int gd_xa = gfx->src_x + gfx->active_xoffset;
16826 int gd_ya = gfx->src_y + gfx->active_yoffset;
16827 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16828 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16831 if (id == GAME_CTRL_ID_STOP ||
16832 id == GAME_CTRL_ID_PAUSE ||
16833 id == GAME_CTRL_ID_PLAY)
16835 button_type = GD_TYPE_NORMAL_BUTTON;
16837 event_mask = GD_EVENT_RELEASED;
16841 button_type = GD_TYPE_CHECK_BUTTON;
16843 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16844 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16845 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16846 event_mask = GD_EVENT_PRESSED;
16849 gi = CreateGadget(GDI_CUSTOM_ID, id,
16850 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16851 GDI_X, DX + pos->x,
16852 GDI_Y, DY + pos->y,
16853 GDI_WIDTH, gfx->width,
16854 GDI_HEIGHT, gfx->height,
16855 GDI_TYPE, button_type,
16856 GDI_STATE, GD_BUTTON_UNPRESSED,
16857 GDI_CHECKED, checked,
16858 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16859 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16860 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16861 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16862 GDI_DIRECT_DRAW, FALSE,
16863 GDI_EVENT_MASK, event_mask,
16864 GDI_CALLBACK_ACTION, HandleGameButtons,
16868 Error(ERR_EXIT, "cannot create gadget");
16870 game_gadget[id] = gi;
16874 void FreeGameButtons()
16878 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16879 FreeGadget(game_gadget[i]);
16882 static void MapGameButtons()
16886 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16887 MapGadget(game_gadget[i]);
16890 void UnmapGameButtons()
16894 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16895 UnmapGadget(game_gadget[i]);
16898 void RedrawGameButtons()
16902 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16903 RedrawGadget(game_gadget[i]);
16906 static void HandleGameButtonsExt(int id)
16908 if (game_status != GAME_MODE_PLAYING)
16913 case GAME_CTRL_ID_STOP:
16917 RequestQuitGame(TRUE);
16920 case GAME_CTRL_ID_PAUSE:
16921 if (options.network)
16923 #if defined(NETWORK_AVALIABLE)
16925 SendToServer_ContinuePlaying();
16927 SendToServer_PausePlaying();
16931 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16934 case GAME_CTRL_ID_PLAY:
16937 #if defined(NETWORK_AVALIABLE)
16938 if (options.network)
16939 SendToServer_ContinuePlaying();
16943 tape.pausing = FALSE;
16944 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16949 case SOUND_CTRL_ID_MUSIC:
16950 if (setup.sound_music)
16952 setup.sound_music = FALSE;
16956 else if (audio.music_available)
16958 setup.sound = setup.sound_music = TRUE;
16960 SetAudioMode(setup.sound);
16966 case SOUND_CTRL_ID_LOOPS:
16967 if (setup.sound_loops)
16968 setup.sound_loops = FALSE;
16969 else if (audio.loops_available)
16971 setup.sound = setup.sound_loops = TRUE;
16973 SetAudioMode(setup.sound);
16977 case SOUND_CTRL_ID_SIMPLE:
16978 if (setup.sound_simple)
16979 setup.sound_simple = FALSE;
16980 else if (audio.sound_available)
16982 setup.sound = setup.sound_simple = TRUE;
16984 SetAudioMode(setup.sound);
16993 static void HandleGameButtons(struct GadgetInfo *gi)
16995 HandleGameButtonsExt(gi->custom_id);
16998 void HandleSoundButtonKeys(Key key)
17001 if (key == setup.shortcut.sound_simple)
17002 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17003 else if (key == setup.shortcut.sound_loops)
17004 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17005 else if (key == setup.shortcut.sound_music)
17006 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17008 if (key == setup.shortcut.sound_simple)
17009 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17010 else if (key == setup.shortcut.sound_loops)
17011 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17012 else if (key == setup.shortcut.sound_music)
17013 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);