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];
1145 static boolean TEST_game_team_mode;
1148 /* ------------------------------------------------------------------------- */
1149 /* definition of elements that automatically change to other elements after */
1150 /* a specified time, eventually calling a function when changing */
1151 /* ------------------------------------------------------------------------- */
1153 /* forward declaration for changer functions */
1154 static void InitBuggyBase(int, int);
1155 static void WarnBuggyBase(int, int);
1157 static void InitTrap(int, int);
1158 static void ActivateTrap(int, int);
1159 static void ChangeActiveTrap(int, int);
1161 static void InitRobotWheel(int, int);
1162 static void RunRobotWheel(int, int);
1163 static void StopRobotWheel(int, int);
1165 static void InitTimegateWheel(int, int);
1166 static void RunTimegateWheel(int, int);
1168 static void InitMagicBallDelay(int, int);
1169 static void ActivateMagicBall(int, int);
1171 struct ChangingElementInfo
1176 void (*pre_change_function)(int x, int y);
1177 void (*change_function)(int x, int y);
1178 void (*post_change_function)(int x, int y);
1181 static struct ChangingElementInfo change_delay_list[] =
1216 EL_STEEL_EXIT_OPENING,
1224 EL_STEEL_EXIT_CLOSING,
1225 EL_STEEL_EXIT_CLOSED,
1252 EL_EM_STEEL_EXIT_OPENING,
1253 EL_EM_STEEL_EXIT_OPEN,
1260 EL_EM_STEEL_EXIT_CLOSING,
1264 EL_EM_STEEL_EXIT_CLOSED,
1288 EL_SWITCHGATE_OPENING,
1296 EL_SWITCHGATE_CLOSING,
1297 EL_SWITCHGATE_CLOSED,
1304 EL_TIMEGATE_OPENING,
1312 EL_TIMEGATE_CLOSING,
1321 EL_ACID_SPLASH_LEFT,
1329 EL_ACID_SPLASH_RIGHT,
1338 EL_SP_BUGGY_BASE_ACTIVATING,
1345 EL_SP_BUGGY_BASE_ACTIVATING,
1346 EL_SP_BUGGY_BASE_ACTIVE,
1353 EL_SP_BUGGY_BASE_ACTIVE,
1377 EL_ROBOT_WHEEL_ACTIVE,
1385 EL_TIMEGATE_SWITCH_ACTIVE,
1393 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1394 EL_DC_TIMEGATE_SWITCH,
1401 EL_EMC_MAGIC_BALL_ACTIVE,
1402 EL_EMC_MAGIC_BALL_ACTIVE,
1409 EL_EMC_SPRING_BUMPER_ACTIVE,
1410 EL_EMC_SPRING_BUMPER,
1417 EL_DIAGONAL_SHRINKING,
1425 EL_DIAGONAL_GROWING,
1446 int push_delay_fixed, push_delay_random;
1450 { EL_SPRING, 0, 0 },
1451 { EL_BALLOON, 0, 0 },
1453 { EL_SOKOBAN_OBJECT, 2, 0 },
1454 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1455 { EL_SATELLITE, 2, 0 },
1456 { EL_SP_DISK_YELLOW, 2, 0 },
1458 { EL_UNDEFINED, 0, 0 },
1466 move_stepsize_list[] =
1468 { EL_AMOEBA_DROP, 2 },
1469 { EL_AMOEBA_DROPPING, 2 },
1470 { EL_QUICKSAND_FILLING, 1 },
1471 { EL_QUICKSAND_EMPTYING, 1 },
1472 { EL_QUICKSAND_FAST_FILLING, 2 },
1473 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1474 { EL_MAGIC_WALL_FILLING, 2 },
1475 { EL_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_BD_MAGIC_WALL_FILLING, 2 },
1477 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1478 { EL_DC_MAGIC_WALL_FILLING, 2 },
1479 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1481 { EL_UNDEFINED, 0 },
1489 collect_count_list[] =
1492 { EL_BD_DIAMOND, 1 },
1493 { EL_EMERALD_YELLOW, 1 },
1494 { EL_EMERALD_RED, 1 },
1495 { EL_EMERALD_PURPLE, 1 },
1497 { EL_SP_INFOTRON, 1 },
1501 { EL_UNDEFINED, 0 },
1509 access_direction_list[] =
1511 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1512 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1513 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1514 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1515 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1516 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1517 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1518 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1519 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1520 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1521 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1523 { EL_SP_PORT_LEFT, MV_RIGHT },
1524 { EL_SP_PORT_RIGHT, MV_LEFT },
1525 { EL_SP_PORT_UP, MV_DOWN },
1526 { EL_SP_PORT_DOWN, MV_UP },
1527 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1528 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1529 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1530 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1531 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1532 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1533 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1534 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1535 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1536 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1537 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1538 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1539 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1540 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1541 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1543 { EL_UNDEFINED, MV_NONE }
1546 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1548 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1549 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1550 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1551 IS_JUST_CHANGING(x, y))
1553 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1555 /* static variables for playfield scan mode (scanning forward or backward) */
1556 static int playfield_scan_start_x = 0;
1557 static int playfield_scan_start_y = 0;
1558 static int playfield_scan_delta_x = 1;
1559 static int playfield_scan_delta_y = 1;
1561 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1562 (y) >= 0 && (y) <= lev_fieldy - 1; \
1563 (y) += playfield_scan_delta_y) \
1564 for ((x) = playfield_scan_start_x; \
1565 (x) >= 0 && (x) <= lev_fieldx - 1; \
1566 (x) += playfield_scan_delta_x)
1569 void DEBUG_SetMaximumDynamite()
1573 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1574 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1575 local_player->inventory_element[local_player->inventory_size++] =
1580 static void InitPlayfieldScanModeVars()
1582 if (game.use_reverse_scan_direction)
1584 playfield_scan_start_x = lev_fieldx - 1;
1585 playfield_scan_start_y = lev_fieldy - 1;
1587 playfield_scan_delta_x = -1;
1588 playfield_scan_delta_y = -1;
1592 playfield_scan_start_x = 0;
1593 playfield_scan_start_y = 0;
1595 playfield_scan_delta_x = 1;
1596 playfield_scan_delta_y = 1;
1600 static void InitPlayfieldScanMode(int mode)
1602 game.use_reverse_scan_direction =
1603 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1605 InitPlayfieldScanModeVars();
1608 static int get_move_delay_from_stepsize(int move_stepsize)
1611 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1613 /* make sure that stepsize value is always a power of 2 */
1614 move_stepsize = (1 << log_2(move_stepsize));
1616 return TILEX / move_stepsize;
1619 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1622 int player_nr = player->index_nr;
1623 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1624 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1626 /* do no immediately change move delay -- the player might just be moving */
1627 player->move_delay_value_next = move_delay;
1629 /* information if player can move must be set separately */
1630 player->cannot_move = cannot_move;
1634 player->move_delay = game.initial_move_delay[player_nr];
1635 player->move_delay_value = game.initial_move_delay_value[player_nr];
1637 player->move_delay_value_next = -1;
1639 player->move_delay_reset_counter = 0;
1643 void GetPlayerConfig()
1645 GameFrameDelay = setup.game_frame_delay;
1647 if (!audio.sound_available)
1648 setup.sound_simple = FALSE;
1650 if (!audio.loops_available)
1651 setup.sound_loops = FALSE;
1653 if (!audio.music_available)
1654 setup.sound_music = FALSE;
1656 if (!video.fullscreen_available)
1657 setup.fullscreen = FALSE;
1659 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1661 SetAudioMode(setup.sound);
1665 int GetElementFromGroupElement(int element)
1667 if (IS_GROUP_ELEMENT(element))
1669 struct ElementGroupInfo *group = element_info[element].group;
1670 int last_anim_random_frame = gfx.anim_random_frame;
1673 if (group->choice_mode == ANIM_RANDOM)
1674 gfx.anim_random_frame = RND(group->num_elements_resolved);
1676 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1677 group->choice_mode, 0,
1680 if (group->choice_mode == ANIM_RANDOM)
1681 gfx.anim_random_frame = last_anim_random_frame;
1683 group->choice_pos++;
1685 element = group->element_resolved[element_pos];
1691 static void InitPlayerField(int x, int y, int element, boolean init_game)
1693 if (element == EL_SP_MURPHY)
1697 if (stored_player[0].present)
1699 Feld[x][y] = EL_SP_MURPHY_CLONE;
1705 stored_player[0].initial_element = element;
1706 stored_player[0].use_murphy = TRUE;
1708 if (!level.use_artwork_element[0])
1709 stored_player[0].artwork_element = EL_SP_MURPHY;
1712 Feld[x][y] = EL_PLAYER_1;
1718 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1719 int jx = player->jx, jy = player->jy;
1721 player->present = TRUE;
1723 player->block_last_field = (element == EL_SP_MURPHY ?
1724 level.sp_block_last_field :
1725 level.block_last_field);
1727 /* ---------- initialize player's last field block delay --------------- */
1729 /* always start with reliable default value (no adjustment needed) */
1730 player->block_delay_adjustment = 0;
1732 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1733 if (player->block_last_field && element == EL_SP_MURPHY)
1734 player->block_delay_adjustment = 1;
1736 /* special case 2: in game engines before 3.1.1, blocking was different */
1737 if (game.use_block_last_field_bug)
1738 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1740 if (!options.network || player->connected)
1742 player->active = TRUE;
1744 /* remove potentially duplicate players */
1745 if (StorePlayer[jx][jy] == Feld[x][y])
1746 StorePlayer[jx][jy] = 0;
1748 StorePlayer[x][y] = Feld[x][y];
1750 #if DEBUG_INIT_PLAYER
1753 printf("- player element %d activated", player->element_nr);
1754 printf(" (local player is %d and currently %s)\n",
1755 local_player->element_nr,
1756 local_player->active ? "active" : "not active");
1761 Feld[x][y] = EL_EMPTY;
1763 player->jx = player->last_jx = x;
1764 player->jy = player->last_jy = y;
1767 #if USE_PLAYER_REANIMATION
1770 int player_nr = GET_PLAYER_NR(element);
1771 struct PlayerInfo *player = &stored_player[player_nr];
1773 if (player->active && player->killed)
1774 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1779 static void InitField(int x, int y, boolean init_game)
1781 int element = Feld[x][y];
1790 InitPlayerField(x, y, element, init_game);
1793 case EL_SOKOBAN_FIELD_PLAYER:
1794 element = Feld[x][y] = EL_PLAYER_1;
1795 InitField(x, y, init_game);
1797 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1798 InitField(x, y, init_game);
1801 case EL_SOKOBAN_FIELD_EMPTY:
1802 local_player->sokobanfields_still_needed++;
1806 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1807 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1808 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1809 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1810 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1811 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1812 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1813 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1814 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1815 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1824 case EL_SPACESHIP_RIGHT:
1825 case EL_SPACESHIP_UP:
1826 case EL_SPACESHIP_LEFT:
1827 case EL_SPACESHIP_DOWN:
1828 case EL_BD_BUTTERFLY:
1829 case EL_BD_BUTTERFLY_RIGHT:
1830 case EL_BD_BUTTERFLY_UP:
1831 case EL_BD_BUTTERFLY_LEFT:
1832 case EL_BD_BUTTERFLY_DOWN:
1834 case EL_BD_FIREFLY_RIGHT:
1835 case EL_BD_FIREFLY_UP:
1836 case EL_BD_FIREFLY_LEFT:
1837 case EL_BD_FIREFLY_DOWN:
1838 case EL_PACMAN_RIGHT:
1840 case EL_PACMAN_LEFT:
1841 case EL_PACMAN_DOWN:
1843 case EL_YAMYAM_LEFT:
1844 case EL_YAMYAM_RIGHT:
1846 case EL_YAMYAM_DOWN:
1847 case EL_DARK_YAMYAM:
1850 case EL_SP_SNIKSNAK:
1851 case EL_SP_ELECTRON:
1860 case EL_AMOEBA_FULL:
1865 case EL_AMOEBA_DROP:
1866 if (y == lev_fieldy - 1)
1868 Feld[x][y] = EL_AMOEBA_GROWING;
1869 Store[x][y] = EL_AMOEBA_WET;
1873 case EL_DYNAMITE_ACTIVE:
1874 case EL_SP_DISK_RED_ACTIVE:
1875 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1876 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1877 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1878 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1879 MovDelay[x][y] = 96;
1882 case EL_EM_DYNAMITE_ACTIVE:
1883 MovDelay[x][y] = 32;
1887 local_player->lights_still_needed++;
1891 local_player->friends_still_needed++;
1896 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1899 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1900 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1901 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1902 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1903 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1904 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1905 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1906 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1907 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1908 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1909 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1910 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1913 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1914 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1915 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1917 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1919 game.belt_dir[belt_nr] = belt_dir;
1920 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1922 else /* more than one switch -- set it like the first switch */
1924 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1929 #if !USE_BOTH_SWITCHGATE_SWITCHES
1930 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1932 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1935 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1937 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1941 case EL_LIGHT_SWITCH_ACTIVE:
1943 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1946 case EL_INVISIBLE_STEELWALL:
1947 case EL_INVISIBLE_WALL:
1948 case EL_INVISIBLE_SAND:
1949 if (game.light_time_left > 0 ||
1950 game.lenses_time_left > 0)
1951 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1954 case EL_EMC_MAGIC_BALL:
1955 if (game.ball_state)
1956 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1959 case EL_EMC_MAGIC_BALL_SWITCH:
1960 if (game.ball_state)
1961 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1964 case EL_TRIGGER_PLAYER:
1965 case EL_TRIGGER_ELEMENT:
1966 case EL_TRIGGER_CE_VALUE:
1967 case EL_TRIGGER_CE_SCORE:
1969 case EL_ANY_ELEMENT:
1970 case EL_CURRENT_CE_VALUE:
1971 case EL_CURRENT_CE_SCORE:
1988 /* reference elements should not be used on the playfield */
1989 Feld[x][y] = EL_EMPTY;
1993 if (IS_CUSTOM_ELEMENT(element))
1995 if (CAN_MOVE(element))
1998 #if USE_NEW_CUSTOM_VALUE
1999 if (!element_info[element].use_last_ce_value || init_game)
2000 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2003 else if (IS_GROUP_ELEMENT(element))
2005 Feld[x][y] = GetElementFromGroupElement(element);
2007 InitField(x, y, init_game);
2014 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2017 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2019 InitField(x, y, init_game);
2021 /* not needed to call InitMovDir() -- already done by InitField()! */
2022 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2023 CAN_MOVE(Feld[x][y]))
2027 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2029 int old_element = Feld[x][y];
2031 InitField(x, y, init_game);
2033 /* not needed to call InitMovDir() -- already done by InitField()! */
2034 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2035 CAN_MOVE(old_element) &&
2036 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2039 /* this case is in fact a combination of not less than three bugs:
2040 first, it calls InitMovDir() for elements that can move, although this is
2041 already done by InitField(); then, it checks the element that was at this
2042 field _before_ the call to InitField() (which can change it); lastly, it
2043 was not called for "mole with direction" elements, which were treated as
2044 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2048 static int get_key_element_from_nr(int key_nr)
2050 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2051 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2052 EL_EM_KEY_1 : EL_KEY_1);
2054 return key_base_element + key_nr;
2057 static int get_next_dropped_element(struct PlayerInfo *player)
2059 return (player->inventory_size > 0 ?
2060 player->inventory_element[player->inventory_size - 1] :
2061 player->inventory_infinite_element != EL_UNDEFINED ?
2062 player->inventory_infinite_element :
2063 player->dynabombs_left > 0 ?
2064 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2068 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2070 /* pos >= 0: get element from bottom of the stack;
2071 pos < 0: get element from top of the stack */
2075 int min_inventory_size = -pos;
2076 int inventory_pos = player->inventory_size - min_inventory_size;
2077 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2079 return (player->inventory_size >= min_inventory_size ?
2080 player->inventory_element[inventory_pos] :
2081 player->inventory_infinite_element != EL_UNDEFINED ?
2082 player->inventory_infinite_element :
2083 player->dynabombs_left >= min_dynabombs_left ?
2084 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2089 int min_dynabombs_left = pos + 1;
2090 int min_inventory_size = pos + 1 - player->dynabombs_left;
2091 int inventory_pos = pos - player->dynabombs_left;
2093 return (player->inventory_infinite_element != EL_UNDEFINED ?
2094 player->inventory_infinite_element :
2095 player->dynabombs_left >= min_dynabombs_left ?
2096 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2097 player->inventory_size >= min_inventory_size ?
2098 player->inventory_element[inventory_pos] :
2103 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2105 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2106 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2109 if (gpo1->sort_priority != gpo2->sort_priority)
2110 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2112 compare_result = gpo1->nr - gpo2->nr;
2114 return compare_result;
2117 void InitGameControlValues()
2121 for (i = 0; game_panel_controls[i].nr != -1; i++)
2123 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2124 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2125 struct TextPosInfo *pos = gpc->pos;
2127 int type = gpc->type;
2131 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2132 Error(ERR_EXIT, "this should not happen -- please debug");
2135 /* force update of game controls after initialization */
2136 gpc->value = gpc->last_value = -1;
2137 gpc->frame = gpc->last_frame = -1;
2138 gpc->gfx_frame = -1;
2140 /* determine panel value width for later calculation of alignment */
2141 if (type == TYPE_INTEGER || type == TYPE_STRING)
2143 pos->width = pos->size * getFontWidth(pos->font);
2144 pos->height = getFontHeight(pos->font);
2146 else if (type == TYPE_ELEMENT)
2148 pos->width = pos->size;
2149 pos->height = pos->size;
2152 /* fill structure for game panel draw order */
2154 gpo->sort_priority = pos->sort_priority;
2157 /* sort game panel controls according to sort_priority and control number */
2158 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2159 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2162 void UpdatePlayfieldElementCount()
2164 boolean use_element_count = FALSE;
2167 /* first check if it is needed at all to calculate playfield element count */
2168 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2169 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2170 use_element_count = TRUE;
2172 if (!use_element_count)
2175 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2176 element_info[i].element_count = 0;
2178 SCAN_PLAYFIELD(x, y)
2180 element_info[Feld[x][y]].element_count++;
2183 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2184 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2185 if (IS_IN_GROUP(j, i))
2186 element_info[EL_GROUP_START + i].element_count +=
2187 element_info[j].element_count;
2190 void UpdateGameControlValues()
2193 int time = (local_player->LevelSolved ?
2194 local_player->LevelSolved_CountingTime :
2195 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2196 level.native_em_level->lev->time :
2197 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2198 level.native_sp_level->game_sp->time_played :
2199 game.no_time_limit ? TimePlayed : TimeLeft);
2200 int score = (local_player->LevelSolved ?
2201 local_player->LevelSolved_CountingScore :
2202 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2203 level.native_em_level->lev->score :
2204 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2205 level.native_sp_level->game_sp->score :
2206 local_player->score);
2207 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208 level.native_em_level->lev->required :
2209 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210 level.native_sp_level->game_sp->infotrons_still_needed :
2211 local_player->gems_still_needed);
2212 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213 level.native_em_level->lev->required > 0 :
2214 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2216 local_player->gems_still_needed > 0 ||
2217 local_player->sokobanfields_still_needed > 0 ||
2218 local_player->lights_still_needed > 0);
2220 UpdatePlayfieldElementCount();
2222 /* update game panel control values */
2224 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2225 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2227 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2228 for (i = 0; i < MAX_NUM_KEYS; i++)
2229 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2230 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2231 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2233 if (game.centered_player_nr == -1)
2235 for (i = 0; i < MAX_PLAYERS; i++)
2237 /* only one player in Supaplex game engine */
2238 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241 for (k = 0; k < MAX_NUM_KEYS; k++)
2243 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2245 if (level.native_em_level->ply[i]->keys & (1 << k))
2246 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247 get_key_element_from_nr(k);
2249 else if (stored_player[i].key[k])
2250 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251 get_key_element_from_nr(k);
2254 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2255 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256 level.native_em_level->ply[i]->dynamite;
2257 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2258 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259 level.native_sp_level->game_sp->red_disk_count;
2261 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2262 stored_player[i].inventory_size;
2264 if (stored_player[i].num_white_keys > 0)
2265 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2268 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2269 stored_player[i].num_white_keys;
2274 int player_nr = game.centered_player_nr;
2276 for (k = 0; k < MAX_NUM_KEYS; k++)
2278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2280 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2281 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282 get_key_element_from_nr(k);
2284 else if (stored_player[player_nr].key[k])
2285 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286 get_key_element_from_nr(k);
2289 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2290 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291 level.native_em_level->ply[player_nr]->dynamite;
2292 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2293 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294 level.native_sp_level->game_sp->red_disk_count;
2296 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2297 stored_player[player_nr].inventory_size;
2299 if (stored_player[player_nr].num_white_keys > 0)
2300 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2302 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2303 stored_player[player_nr].num_white_keys;
2306 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2308 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2309 get_inventory_element_from_pos(local_player, i);
2310 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2311 get_inventory_element_from_pos(local_player, -i - 1);
2314 game_panel_controls[GAME_PANEL_SCORE].value = score;
2315 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2317 game_panel_controls[GAME_PANEL_TIME].value = time;
2319 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2320 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2321 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2323 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2325 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2326 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2328 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2329 local_player->shield_normal_time_left;
2330 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2331 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2333 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2334 local_player->shield_deadly_time_left;
2336 game_panel_controls[GAME_PANEL_EXIT].value =
2337 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2339 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2340 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2341 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2342 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2343 EL_EMC_MAGIC_BALL_SWITCH);
2345 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2346 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2347 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2348 game.light_time_left;
2350 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2351 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2352 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2353 game.timegate_time_left;
2355 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2356 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2358 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2359 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2360 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2361 game.lenses_time_left;
2363 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2364 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2365 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2366 game.magnify_time_left;
2368 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2369 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2370 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2371 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2372 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2373 EL_BALLOON_SWITCH_NONE);
2375 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2376 local_player->dynabomb_count;
2377 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2378 local_player->dynabomb_size;
2379 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2380 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2382 game_panel_controls[GAME_PANEL_PENGUINS].value =
2383 local_player->friends_still_needed;
2385 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2386 local_player->sokobanfields_still_needed;
2387 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2388 local_player->sokobanfields_still_needed;
2390 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2391 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2393 for (i = 0; i < NUM_BELTS; i++)
2395 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2396 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2397 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2398 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2399 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2402 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2403 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2404 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2405 game.magic_wall_time_left;
2407 #if USE_PLAYER_GRAVITY
2408 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2409 local_player->gravity;
2411 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2414 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2415 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2417 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2418 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2419 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2420 game.panel.element[i].id : EL_UNDEFINED);
2422 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2423 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2424 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2425 element_info[game.panel.element_count[i].id].element_count : 0);
2427 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2428 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2429 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2430 element_info[game.panel.ce_score[i].id].collect_score : 0);
2432 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2433 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2434 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2435 element_info[game.panel.ce_score_element[i].id].collect_score :
2438 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2439 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2440 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2442 /* update game panel control frames */
2444 for (i = 0; game_panel_controls[i].nr != -1; i++)
2446 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2448 if (gpc->type == TYPE_ELEMENT)
2450 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2452 int last_anim_random_frame = gfx.anim_random_frame;
2453 int element = gpc->value;
2454 int graphic = el2panelimg(element);
2456 if (gpc->value != gpc->last_value)
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2465 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2466 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2467 gpc->gfx_random = INIT_GFX_RANDOM();
2470 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471 gfx.anim_random_frame = gpc->gfx_random;
2473 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2474 gpc->gfx_frame = element_info[element].collect_score;
2476 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2479 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2480 gfx.anim_random_frame = last_anim_random_frame;
2486 void DisplayGameControlValues()
2488 boolean redraw_panel = FALSE;
2491 for (i = 0; game_panel_controls[i].nr != -1; i++)
2493 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2495 if (PANEL_DEACTIVATED(gpc->pos))
2498 if (gpc->value == gpc->last_value &&
2499 gpc->frame == gpc->last_frame)
2502 redraw_panel = TRUE;
2508 /* copy default game door content to main double buffer */
2510 /* !!! CHECK AGAIN !!! */
2511 SetPanelBackground();
2512 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2513 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2515 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2516 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2519 /* redraw game control buttons */
2521 RedrawGameButtons();
2527 game_status = GAME_MODE_PSEUDO_PANEL;
2530 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2532 for (i = 0; game_panel_controls[i].nr != -1; i++)
2536 int nr = game_panel_order[i].nr;
2537 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2539 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2542 struct TextPosInfo *pos = gpc->pos;
2543 int type = gpc->type;
2544 int value = gpc->value;
2545 int frame = gpc->frame;
2547 int last_value = gpc->last_value;
2548 int last_frame = gpc->last_frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2559 if (value == last_value && frame == last_frame)
2563 gpc->last_value = value;
2564 gpc->last_frame = frame;
2567 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2570 if (type == TYPE_INTEGER)
2572 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573 nr == GAME_PANEL_TIME)
2575 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2577 if (use_dynamic_size) /* use dynamic number of digits */
2579 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581 int size2 = size1 + 1;
2582 int font1 = pos->font;
2583 int font2 = pos->font_alt;
2585 size = (value < value_change ? size1 : size2);
2586 font = (value < value_change ? font1 : font2);
2589 /* clear background if value just changed its size (dynamic digits) */
2590 if ((last_value < value_change) != (value < value_change))
2592 int width1 = size1 * getFontWidth(font1);
2593 int width2 = size2 * getFontWidth(font2);
2594 int max_width = MAX(width1, width2);
2595 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2597 pos->width = max_width;
2599 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600 max_width, max_height);
2607 /* correct text size if "digits" is zero or less */
2609 size = strlen(int2str(value, size));
2611 /* dynamically correct text alignment */
2612 pos->width = size * getFontWidth(font);
2615 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616 int2str(value, size), font, mask_mode);
2618 else if (type == TYPE_ELEMENT)
2620 int element, graphic;
2624 int dst_x = PANEL_XPOS(pos);
2625 int dst_y = PANEL_YPOS(pos);
2628 if (value != EL_UNDEFINED && value != EL_EMPTY)
2631 graphic = el2panelimg(value);
2633 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2636 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2640 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2643 width = graphic_info[graphic].width * size / TILESIZE;
2644 height = graphic_info[graphic].height * size / TILESIZE;
2648 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649 dst_x - src_x, dst_y - src_y);
2650 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2655 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2660 if (value == EL_UNDEFINED || value == EL_EMPTY)
2662 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663 graphic = el2panelimg(element);
2665 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2672 graphic = el2panelimg(value);
2674 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2677 width = graphic_info[graphic].width * size / TILESIZE;
2678 height = graphic_info[graphic].height * size / TILESIZE;
2680 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2683 else if (type == TYPE_STRING)
2685 boolean active = (value != 0);
2686 char *state_normal = "off";
2687 char *state_active = "on";
2688 char *state = (active ? state_active : state_normal);
2689 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2691 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2692 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2694 if (nr == GAME_PANEL_GRAVITY_STATE)
2696 int font1 = pos->font; /* (used for normal state) */
2697 int font2 = pos->font_alt; /* (used for active state) */
2699 int size1 = strlen(state_normal);
2700 int size2 = strlen(state_active);
2701 int width1 = size1 * getFontWidth(font1);
2702 int width2 = size2 * getFontWidth(font2);
2703 int max_width = MAX(width1, width2);
2704 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2706 pos->width = max_width;
2708 /* clear background for values that may have changed its size */
2709 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710 max_width, max_height);
2713 font = (active ? font2 : font1);
2723 /* don't truncate output if "chars" is zero or less */
2726 /* dynamically correct text alignment */
2727 pos->width = size * getFontWidth(font);
2731 s_cut = getStringCopyN(s, size);
2733 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734 s_cut, font, mask_mode);
2740 redraw_mask |= REDRAW_DOOR_1;
2743 game_status = GAME_MODE_PLAYING;
2746 void UpdateAndDisplayGameControlValues()
2748 if (tape.warp_forward)
2751 UpdateGameControlValues();
2752 DisplayGameControlValues();
2755 void DrawGameValue_Emeralds(int value)
2757 struct TextPosInfo *pos = &game.panel.gems;
2758 int font_nr = pos->font;
2759 int font_width = getFontWidth(font_nr);
2760 int chars = pos->size;
2763 return; /* !!! USE NEW STUFF !!! */
2766 if (PANEL_DEACTIVATED(pos))
2769 pos->width = chars * font_width;
2771 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2774 void DrawGameValue_Dynamite(int value)
2776 struct TextPosInfo *pos = &game.panel.inventory_count;
2777 int font_nr = pos->font;
2778 int font_width = getFontWidth(font_nr);
2779 int chars = pos->size;
2782 return; /* !!! USE NEW STUFF !!! */
2785 if (PANEL_DEACTIVATED(pos))
2788 pos->width = chars * font_width;
2790 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2793 void DrawGameValue_Score(int value)
2795 struct TextPosInfo *pos = &game.panel.score;
2796 int font_nr = pos->font;
2797 int font_width = getFontWidth(font_nr);
2798 int chars = pos->size;
2801 return; /* !!! USE NEW STUFF !!! */
2804 if (PANEL_DEACTIVATED(pos))
2807 pos->width = chars * font_width;
2809 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2812 void DrawGameValue_Time(int value)
2814 struct TextPosInfo *pos = &game.panel.time;
2815 static int last_value = -1;
2818 int chars = pos->size;
2819 int font1_nr = pos->font;
2820 int font2_nr = pos->font_alt;
2821 int font_nr = font1_nr;
2822 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2825 return; /* !!! USE NEW STUFF !!! */
2828 if (PANEL_DEACTIVATED(pos))
2831 if (use_dynamic_chars) /* use dynamic number of chars */
2833 chars = (value < 1000 ? chars1 : chars2);
2834 font_nr = (value < 1000 ? font1_nr : font2_nr);
2837 /* clear background if value just changed its size (dynamic chars only) */
2838 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2840 int width1 = chars1 * getFontWidth(font1_nr);
2841 int width2 = chars2 * getFontWidth(font2_nr);
2842 int max_width = MAX(width1, width2);
2843 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2845 pos->width = max_width;
2847 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2848 max_width, max_height);
2851 pos->width = chars * getFontWidth(font_nr);
2853 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2858 void DrawGameValue_Level(int value)
2860 struct TextPosInfo *pos = &game.panel.level_number;
2863 int chars = pos->size;
2864 int font1_nr = pos->font;
2865 int font2_nr = pos->font_alt;
2866 int font_nr = font1_nr;
2867 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2870 return; /* !!! USE NEW STUFF !!! */
2873 if (PANEL_DEACTIVATED(pos))
2876 if (use_dynamic_chars) /* use dynamic number of chars */
2878 chars = (level_nr < 100 ? chars1 : chars2);
2879 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2882 pos->width = chars * getFontWidth(font_nr);
2884 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2887 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2892 return; /* !!! USE NEW STUFF !!! */
2895 for (i = 0; i < MAX_NUM_KEYS; i++)
2897 struct TextPosInfo *pos = &game.panel.key[i];
2898 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2899 int src_y = DOOR_GFX_PAGEY1 + 123;
2900 int dst_x = PANEL_XPOS(pos);
2901 int dst_y = PANEL_YPOS(pos);
2903 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2904 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2906 int graphic = el2edimg(element);
2908 if (PANEL_DEACTIVATED(pos))
2912 /* masked blit with tiles from half-size scaled bitmap does not work yet
2913 (no mask bitmap created for these sizes after loading and scaling) --
2914 solution: load without creating mask, scale, then create final mask */
2916 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2917 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2924 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2926 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2927 dst_x - src_x, dst_y - src_y);
2928 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2933 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2935 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2936 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2941 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2944 int key[MAX_NUM_KEYS];
2947 /* prevent EM engine from updating time/score values parallel to GameWon() */
2948 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2949 local_player->LevelSolved)
2952 for (i = 0; i < MAX_NUM_KEYS; i++)
2953 key[i] = key_bits & (1 << i);
2955 DrawGameValue_Level(level_nr);
2957 DrawGameValue_Emeralds(emeralds);
2958 DrawGameValue_Dynamite(dynamite);
2959 DrawGameValue_Score(score);
2960 DrawGameValue_Time(time);
2962 DrawGameValue_Keys(key);
2965 void UpdateGameDoorValues()
2967 UpdateGameControlValues();
2970 void DrawGameDoorValues()
2972 DisplayGameControlValues();
2975 void DrawGameDoorValues_OLD()
2977 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2978 int dynamite_value = 0;
2979 int score_value = (local_player->LevelSolved ? local_player->score_final :
2980 local_player->score);
2981 int gems_value = local_player->gems_still_needed;
2985 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2987 DrawGameDoorValues_EM();
2992 if (game.centered_player_nr == -1)
2994 for (i = 0; i < MAX_PLAYERS; i++)
2996 for (j = 0; j < MAX_NUM_KEYS; j++)
2997 if (stored_player[i].key[j])
2998 key_bits |= (1 << j);
3000 dynamite_value += stored_player[i].inventory_size;
3005 int player_nr = game.centered_player_nr;
3007 for (i = 0; i < MAX_NUM_KEYS; i++)
3008 if (stored_player[player_nr].key[i])
3009 key_bits |= (1 << i);
3011 dynamite_value = stored_player[player_nr].inventory_size;
3014 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3020 =============================================================================
3022 -----------------------------------------------------------------------------
3023 initialize game engine due to level / tape version number
3024 =============================================================================
3027 static void InitGameEngine()
3029 int i, j, k, l, x, y;
3031 /* set game engine from tape file when re-playing, else from level file */
3032 game.engine_version = (tape.playing ? tape.engine_version :
3033 level.game_version);
3035 /* ---------------------------------------------------------------------- */
3036 /* set flags for bugs and changes according to active game engine version */
3037 /* ---------------------------------------------------------------------- */
3040 Summary of bugfix/change:
3041 Fixed handling for custom elements that change when pushed by the player.
3043 Fixed/changed in version:
3047 Before 3.1.0, custom elements that "change when pushing" changed directly
3048 after the player started pushing them (until then handled in "DigField()").
3049 Since 3.1.0, these custom elements are not changed until the "pushing"
3050 move of the element is finished (now handled in "ContinueMoving()").
3052 Affected levels/tapes:
3053 The first condition is generally needed for all levels/tapes before version
3054 3.1.0, which might use the old behaviour before it was changed; known tapes
3055 that are affected are some tapes from the level set "Walpurgis Gardens" by
3057 The second condition is an exception from the above case and is needed for
3058 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3059 above (including some development versions of 3.1.0), but before it was
3060 known that this change would break tapes like the above and was fixed in
3061 3.1.1, so that the changed behaviour was active although the engine version
3062 while recording maybe was before 3.1.0. There is at least one tape that is
3063 affected by this exception, which is the tape for the one-level set "Bug
3064 Machine" by Juergen Bonhagen.
3067 game.use_change_when_pushing_bug =
3068 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3070 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3071 tape.game_version < VERSION_IDENT(3,1,1,0)));
3074 Summary of bugfix/change:
3075 Fixed handling for blocking the field the player leaves when moving.
3077 Fixed/changed in version:
3081 Before 3.1.1, when "block last field when moving" was enabled, the field
3082 the player is leaving when moving was blocked for the time of the move,
3083 and was directly unblocked afterwards. This resulted in the last field
3084 being blocked for exactly one less than the number of frames of one player
3085 move. Additionally, even when blocking was disabled, the last field was
3086 blocked for exactly one frame.
3087 Since 3.1.1, due to changes in player movement handling, the last field
3088 is not blocked at all when blocking is disabled. When blocking is enabled,
3089 the last field is blocked for exactly the number of frames of one player
3090 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3091 last field is blocked for exactly one more than the number of frames of
3094 Affected levels/tapes:
3095 (!!! yet to be determined -- probably many !!!)
3098 game.use_block_last_field_bug =
3099 (game.engine_version < VERSION_IDENT(3,1,1,0));
3102 Summary of bugfix/change:
3103 Changed behaviour of CE changes with multiple changes per single frame.
3105 Fixed/changed in version:
3109 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3110 This resulted in race conditions where CEs seem to behave strange in some
3111 situations (where triggered CE changes were just skipped because there was
3112 already a CE change on that tile in the playfield in that engine frame).
3113 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3114 (The number of changes per frame must be limited in any case, because else
3115 it is easily possible to define CE changes that would result in an infinite
3116 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3117 should be set large enough so that it would only be reached in cases where
3118 the corresponding CE change conditions run into a loop. Therefore, it seems
3119 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3120 maximal number of change pages for custom elements.)
3122 Affected levels/tapes:
3126 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3127 game.max_num_changes_per_frame = 1;
3129 game.max_num_changes_per_frame =
3130 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3133 /* ---------------------------------------------------------------------- */
3135 /* default scan direction: scan playfield from top/left to bottom/right */
3136 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3138 /* dynamically adjust element properties according to game engine version */
3139 InitElementPropertiesEngine(game.engine_version);
3142 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3143 printf(" tape version == %06d [%s] [file: %06d]\n",
3144 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3146 printf(" => game.engine_version == %06d\n", game.engine_version);
3149 /* ---------- initialize player's initial move delay --------------------- */
3151 /* dynamically adjust player properties according to level information */
3152 for (i = 0; i < MAX_PLAYERS; i++)
3153 game.initial_move_delay_value[i] =
3154 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3156 /* dynamically adjust player properties according to game engine version */
3157 for (i = 0; i < MAX_PLAYERS; i++)
3158 game.initial_move_delay[i] =
3159 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3160 game.initial_move_delay_value[i] : 0);
3162 /* ---------- initialize player's initial push delay --------------------- */
3164 /* dynamically adjust player properties according to game engine version */
3165 game.initial_push_delay_value =
3166 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3168 /* ---------- initialize changing elements ------------------------------- */
3170 /* initialize changing elements information */
3171 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173 struct ElementInfo *ei = &element_info[i];
3175 /* this pointer might have been changed in the level editor */
3176 ei->change = &ei->change_page[0];
3178 if (!IS_CUSTOM_ELEMENT(i))
3180 ei->change->target_element = EL_EMPTY_SPACE;
3181 ei->change->delay_fixed = 0;
3182 ei->change->delay_random = 0;
3183 ei->change->delay_frames = 1;
3186 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3188 ei->has_change_event[j] = FALSE;
3190 ei->event_page_nr[j] = 0;
3191 ei->event_page[j] = &ei->change_page[0];
3195 /* add changing elements from pre-defined list */
3196 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3198 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3199 struct ElementInfo *ei = &element_info[ch_delay->element];
3201 ei->change->target_element = ch_delay->target_element;
3202 ei->change->delay_fixed = ch_delay->change_delay;
3204 ei->change->pre_change_function = ch_delay->pre_change_function;
3205 ei->change->change_function = ch_delay->change_function;
3206 ei->change->post_change_function = ch_delay->post_change_function;
3208 ei->change->can_change = TRUE;
3209 ei->change->can_change_or_has_action = TRUE;
3211 ei->has_change_event[CE_DELAY] = TRUE;
3213 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3214 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3217 /* ---------- initialize internal run-time variables --------------------- */
3219 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3221 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3223 for (j = 0; j < ei->num_change_pages; j++)
3225 ei->change_page[j].can_change_or_has_action =
3226 (ei->change_page[j].can_change |
3227 ei->change_page[j].has_action);
3231 /* add change events from custom element configuration */
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 if (!ei->change_page[j].can_change_or_has_action)
3241 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3243 /* only add event page for the first page found with this event */
3244 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3246 ei->has_change_event[k] = TRUE;
3248 ei->event_page_nr[k] = j;
3249 ei->event_page[k] = &ei->change_page[j];
3256 /* ---------- initialize reference elements in change conditions --------- */
3258 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3260 int element = EL_CUSTOM_START + i;
3261 struct ElementInfo *ei = &element_info[element];
3263 for (j = 0; j < ei->num_change_pages; j++)
3265 int trigger_element = ei->change_page[j].initial_trigger_element;
3267 if (trigger_element >= EL_PREV_CE_8 &&
3268 trigger_element <= EL_NEXT_CE_8)
3269 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3271 ei->change_page[j].trigger_element = trigger_element;
3276 /* ---------- initialize run-time trigger player and element ------------- */
3278 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3280 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3282 for (j = 0; j < ei->num_change_pages; j++)
3284 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3285 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3286 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3287 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3288 ei->change_page[j].actual_trigger_ce_value = 0;
3289 ei->change_page[j].actual_trigger_ce_score = 0;
3293 /* ---------- initialize trigger events ---------------------------------- */
3295 /* initialize trigger events information */
3296 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3297 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3298 trigger_events[i][j] = FALSE;
3300 /* add trigger events from element change event properties */
3301 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3303 struct ElementInfo *ei = &element_info[i];
3305 for (j = 0; j < ei->num_change_pages; j++)
3307 if (!ei->change_page[j].can_change_or_has_action)
3310 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3312 int trigger_element = ei->change_page[j].trigger_element;
3314 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3316 if (ei->change_page[j].has_event[k])
3318 if (IS_GROUP_ELEMENT(trigger_element))
3320 struct ElementGroupInfo *group =
3321 element_info[trigger_element].group;
3323 for (l = 0; l < group->num_elements_resolved; l++)
3324 trigger_events[group->element_resolved[l]][k] = TRUE;
3326 else if (trigger_element == EL_ANY_ELEMENT)
3327 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3328 trigger_events[l][k] = TRUE;
3330 trigger_events[trigger_element][k] = TRUE;
3337 /* ---------- initialize push delay -------------------------------------- */
3339 /* initialize push delay values to default */
3340 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3342 if (!IS_CUSTOM_ELEMENT(i))
3344 /* set default push delay values (corrected since version 3.0.7-1) */
3345 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3347 element_info[i].push_delay_fixed = 2;
3348 element_info[i].push_delay_random = 8;
3352 element_info[i].push_delay_fixed = 8;
3353 element_info[i].push_delay_random = 8;
3358 /* set push delay value for certain elements from pre-defined list */
3359 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3361 int e = push_delay_list[i].element;
3363 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3364 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3367 /* set push delay value for Supaplex elements for newer engine versions */
3368 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3370 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3372 if (IS_SP_ELEMENT(i))
3374 /* set SP push delay to just enough to push under a falling zonk */
3375 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3377 element_info[i].push_delay_fixed = delay;
3378 element_info[i].push_delay_random = 0;
3383 /* ---------- initialize move stepsize ----------------------------------- */
3385 /* initialize move stepsize values to default */
3386 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387 if (!IS_CUSTOM_ELEMENT(i))
3388 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3390 /* set move stepsize value for certain elements from pre-defined list */
3391 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3393 int e = move_stepsize_list[i].element;
3395 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3398 /* ---------- initialize collect score ----------------------------------- */
3400 /* initialize collect score values for custom elements from initial value */
3401 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402 if (IS_CUSTOM_ELEMENT(i))
3403 element_info[i].collect_score = element_info[i].collect_score_initial;
3405 /* ---------- initialize collect count ----------------------------------- */
3407 /* initialize collect count values for non-custom elements */
3408 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3409 if (!IS_CUSTOM_ELEMENT(i))
3410 element_info[i].collect_count_initial = 0;
3412 /* add collect count values for all elements from pre-defined list */
3413 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3414 element_info[collect_count_list[i].element].collect_count_initial =
3415 collect_count_list[i].count;
3417 /* ---------- initialize access direction -------------------------------- */
3419 /* initialize access direction values to default (access from every side) */
3420 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421 if (!IS_CUSTOM_ELEMENT(i))
3422 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3424 /* set access direction value for certain elements from pre-defined list */
3425 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3426 element_info[access_direction_list[i].element].access_direction =
3427 access_direction_list[i].direction;
3429 /* ---------- initialize explosion content ------------------------------- */
3430 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432 if (IS_CUSTOM_ELEMENT(i))
3435 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3437 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3439 element_info[i].content.e[x][y] =
3440 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3441 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3442 i == EL_PLAYER_3 ? EL_EMERALD :
3443 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3444 i == EL_MOLE ? EL_EMERALD_RED :
3445 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3446 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3447 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3448 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3449 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3450 i == EL_WALL_EMERALD ? EL_EMERALD :
3451 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3452 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3453 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3454 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3455 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3456 i == EL_WALL_PEARL ? EL_PEARL :
3457 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3462 /* ---------- initialize recursion detection ------------------------------ */
3463 recursion_loop_depth = 0;
3464 recursion_loop_detected = FALSE;
3465 recursion_loop_element = EL_UNDEFINED;
3467 /* ---------- initialize graphics engine ---------------------------------- */
3468 game.scroll_delay_value =
3469 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3470 setup.scroll_delay ? setup.scroll_delay_value : 0);
3471 game.scroll_delay_value =
3472 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3475 int get_num_special_action(int element, int action_first, int action_last)
3477 int num_special_action = 0;
3480 for (i = action_first; i <= action_last; i++)
3482 boolean found = FALSE;
3484 for (j = 0; j < NUM_DIRECTIONS; j++)
3485 if (el_act_dir2img(element, i, j) !=
3486 el_act_dir2img(element, ACTION_DEFAULT, j))
3490 num_special_action++;
3495 return num_special_action;
3500 =============================================================================
3502 -----------------------------------------------------------------------------
3503 initialize and start new game
3504 =============================================================================
3509 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3510 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3511 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3513 boolean do_fading = (game_status == GAME_MODE_MAIN);
3516 int initial_move_dir = MV_DOWN;
3518 int initial_move_dir = MV_NONE;
3522 game_status = GAME_MODE_PLAYING;
3525 /* needed if different viewport properties defined for playing */
3526 ChangeViewportPropertiesIfNeeded();
3530 DrawCompleteVideoDisplay();
3534 InitGameControlValues();
3536 /* don't play tapes over network */
3537 network_playing = (options.network && !tape.playing);
3539 for (i = 0; i < MAX_PLAYERS; i++)
3541 struct PlayerInfo *player = &stored_player[i];
3543 player->index_nr = i;
3544 player->index_bit = (1 << i);
3545 player->element_nr = EL_PLAYER_1 + i;
3547 player->present = FALSE;
3548 player->active = FALSE;
3549 player->mapped = FALSE;
3551 player->killed = FALSE;
3552 player->reanimated = FALSE;
3555 player->effective_action = 0;
3556 player->programmed_action = 0;
3559 player->score_final = 0;
3561 player->gems_still_needed = level.gems_needed;
3562 player->sokobanfields_still_needed = 0;
3563 player->lights_still_needed = 0;
3564 player->friends_still_needed = 0;
3566 for (j = 0; j < MAX_NUM_KEYS; j++)
3567 player->key[j] = FALSE;
3569 player->num_white_keys = 0;
3571 player->dynabomb_count = 0;
3572 player->dynabomb_size = 1;
3573 player->dynabombs_left = 0;
3574 player->dynabomb_xl = FALSE;
3576 player->MovDir = initial_move_dir;
3579 player->GfxDir = initial_move_dir;
3580 player->GfxAction = ACTION_DEFAULT;
3582 player->StepFrame = 0;
3584 player->initial_element = player->element_nr;
3585 player->artwork_element =
3586 (level.use_artwork_element[i] ? level.artwork_element[i] :
3587 player->element_nr);
3588 player->use_murphy = FALSE;
3590 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3591 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3593 player->gravity = level.initial_player_gravity[i];
3595 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3597 player->actual_frame_counter = 0;
3599 player->step_counter = 0;
3601 player->last_move_dir = initial_move_dir;
3603 player->is_active = FALSE;
3605 player->is_waiting = FALSE;
3606 player->is_moving = FALSE;
3607 player->is_auto_moving = FALSE;
3608 player->is_digging = FALSE;
3609 player->is_snapping = FALSE;
3610 player->is_collecting = FALSE;
3611 player->is_pushing = FALSE;
3612 player->is_switching = FALSE;
3613 player->is_dropping = FALSE;
3614 player->is_dropping_pressed = FALSE;
3616 player->is_bored = FALSE;
3617 player->is_sleeping = FALSE;
3619 player->frame_counter_bored = -1;
3620 player->frame_counter_sleeping = -1;
3622 player->anim_delay_counter = 0;
3623 player->post_delay_counter = 0;
3625 player->dir_waiting = initial_move_dir;
3626 player->action_waiting = ACTION_DEFAULT;
3627 player->last_action_waiting = ACTION_DEFAULT;
3628 player->special_action_bored = ACTION_DEFAULT;
3629 player->special_action_sleeping = ACTION_DEFAULT;
3631 player->switch_x = -1;
3632 player->switch_y = -1;
3634 player->drop_x = -1;
3635 player->drop_y = -1;
3637 player->show_envelope = 0;
3639 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3641 player->push_delay = -1; /* initialized when pushing starts */
3642 player->push_delay_value = game.initial_push_delay_value;
3644 player->drop_delay = 0;
3645 player->drop_pressed_delay = 0;
3647 player->last_jx = -1;
3648 player->last_jy = -1;
3652 player->shield_normal_time_left = 0;
3653 player->shield_deadly_time_left = 0;
3655 player->inventory_infinite_element = EL_UNDEFINED;
3656 player->inventory_size = 0;
3658 if (level.use_initial_inventory[i])
3660 for (j = 0; j < level.initial_inventory_size[i]; j++)
3662 int element = level.initial_inventory_content[i][j];
3663 int collect_count = element_info[element].collect_count_initial;
3666 if (!IS_CUSTOM_ELEMENT(element))
3669 if (collect_count == 0)
3670 player->inventory_infinite_element = element;
3672 for (k = 0; k < collect_count; k++)
3673 if (player->inventory_size < MAX_INVENTORY_SIZE)
3674 player->inventory_element[player->inventory_size++] = element;
3678 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3679 SnapField(player, 0, 0);
3681 player->LevelSolved = FALSE;
3682 player->GameOver = FALSE;
3684 player->LevelSolved_GameWon = FALSE;
3685 player->LevelSolved_GameEnd = FALSE;
3686 player->LevelSolved_PanelOff = FALSE;
3687 player->LevelSolved_SaveTape = FALSE;
3688 player->LevelSolved_SaveScore = FALSE;
3689 player->LevelSolved_CountingTime = 0;
3690 player->LevelSolved_CountingScore = 0;
3692 map_player_action[i] = i;
3695 network_player_action_received = FALSE;
3697 #if defined(NETWORK_AVALIABLE)
3698 /* initial null action */
3699 if (network_playing)
3700 SendToServer_MovePlayer(MV_NONE);
3709 TimeLeft = level.time;
3712 ScreenMovDir = MV_NONE;
3716 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3718 AllPlayersGone = FALSE;
3720 game.no_time_limit = (level.time == 0);
3722 game.yamyam_content_nr = 0;
3723 game.robot_wheel_active = FALSE;
3724 game.magic_wall_active = FALSE;
3725 game.magic_wall_time_left = 0;
3726 game.light_time_left = 0;
3727 game.timegate_time_left = 0;
3728 game.switchgate_pos = 0;
3729 game.wind_direction = level.wind_direction_initial;
3731 #if !USE_PLAYER_GRAVITY
3732 game.gravity = FALSE;
3733 game.explosions_delayed = TRUE;
3736 game.lenses_time_left = 0;
3737 game.magnify_time_left = 0;
3739 game.ball_state = level.ball_state_initial;
3740 game.ball_content_nr = 0;
3742 game.envelope_active = FALSE;
3744 /* set focus to local player for network games, else to all players */
3745 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3746 game.centered_player_nr_next = game.centered_player_nr;
3747 game.set_centered_player = FALSE;
3749 if (network_playing && tape.recording)
3751 /* store client dependent player focus when recording network games */
3752 tape.centered_player_nr_next = game.centered_player_nr_next;
3753 tape.set_centered_player = TRUE;
3756 for (i = 0; i < NUM_BELTS; i++)
3758 game.belt_dir[i] = MV_NONE;
3759 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3762 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3763 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3765 #if DEBUG_INIT_PLAYER
3768 printf("Player status at level initialization:\n");
3772 SCAN_PLAYFIELD(x, y)
3774 Feld[x][y] = level.field[x][y];
3775 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3776 ChangeDelay[x][y] = 0;
3777 ChangePage[x][y] = -1;
3778 #if USE_NEW_CUSTOM_VALUE
3779 CustomValue[x][y] = 0; /* initialized in InitField() */
3781 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3783 WasJustMoving[x][y] = 0;
3784 WasJustFalling[x][y] = 0;
3785 CheckCollision[x][y] = 0;
3786 CheckImpact[x][y] = 0;
3788 Pushed[x][y] = FALSE;
3790 ChangeCount[x][y] = 0;
3791 ChangeEvent[x][y] = -1;
3793 ExplodePhase[x][y] = 0;
3794 ExplodeDelay[x][y] = 0;
3795 ExplodeField[x][y] = EX_TYPE_NONE;
3797 RunnerVisit[x][y] = 0;
3798 PlayerVisit[x][y] = 0;
3801 GfxRandom[x][y] = INIT_GFX_RANDOM();
3802 GfxElement[x][y] = EL_UNDEFINED;
3803 GfxAction[x][y] = ACTION_DEFAULT;
3804 GfxDir[x][y] = MV_NONE;
3805 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3808 SCAN_PLAYFIELD(x, y)
3810 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3812 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3814 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3817 InitField(x, y, TRUE);
3819 ResetGfxAnimation(x, y);
3824 for (i = 0; i < MAX_PLAYERS; i++)
3826 struct PlayerInfo *player = &stored_player[i];
3828 /* set number of special actions for bored and sleeping animation */
3829 player->num_special_action_bored =
3830 get_num_special_action(player->artwork_element,
3831 ACTION_BORING_1, ACTION_BORING_LAST);
3832 player->num_special_action_sleeping =
3833 get_num_special_action(player->artwork_element,
3834 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3837 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3838 emulate_sb ? EMU_SOKOBAN :
3839 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3841 #if USE_NEW_ALL_SLIPPERY
3842 /* initialize type of slippery elements */
3843 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3845 if (!IS_CUSTOM_ELEMENT(i))
3847 /* default: elements slip down either to the left or right randomly */
3848 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3850 /* SP style elements prefer to slip down on the left side */
3851 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3852 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3854 /* BD style elements prefer to slip down on the left side */
3855 if (game.emulation == EMU_BOULDERDASH)
3856 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3861 /* initialize explosion and ignition delay */
3862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3864 if (!IS_CUSTOM_ELEMENT(i))
3867 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3868 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3869 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3870 int last_phase = (num_phase + 1) * delay;
3871 int half_phase = (num_phase / 2) * delay;
3873 element_info[i].explosion_delay = last_phase - 1;
3874 element_info[i].ignition_delay = half_phase;
3876 if (i == EL_BLACK_ORB)
3877 element_info[i].ignition_delay = 1;
3881 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3882 element_info[i].explosion_delay = 1;
3884 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3885 element_info[i].ignition_delay = 1;
3889 /* correct non-moving belts to start moving left */
3890 for (i = 0; i < NUM_BELTS; i++)
3891 if (game.belt_dir[i] == MV_NONE)
3892 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3894 #if USE_NEW_PLAYER_ASSIGNMENTS
3895 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3896 /* choose default local player */
3897 local_player = &stored_player[0];
3899 for (i = 0; i < MAX_PLAYERS; i++)
3900 stored_player[i].connected = FALSE;
3902 local_player->connected = TRUE;
3903 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3905 TEST_game_team_mode = setup.team_mode;
3910 int num_players = 0;
3912 for (i = 0; i < MAX_PLAYERS; i++)
3913 if (tape.player_participates[i])
3916 TEST_game_team_mode = (num_players > 1);
3918 printf("::: TAPE TEAM MODE: %s (%d)\n",
3919 (TEST_game_team_mode ? "true" : "false"), num_players);
3923 for (i = 0; i < MAX_PLAYERS; i++)
3924 stored_player[i].connected = tape.player_participates[i];
3926 /* try to guess locally connected team mode players (needed for correct
3927 assignment of player figures from level to locally playing players) */
3929 for (i = 0; i < MAX_PLAYERS; i++)
3930 if (tape.player_participates[i])
3931 stored_player[i].connected = TRUE;
3934 else if (setup.team_mode && !options.network)
3936 /* try to guess locally connected team mode players (needed for correct
3937 assignment of player figures from level to locally playing players) */
3939 for (i = 0; i < MAX_PLAYERS; i++)
3940 if (setup.input[i].use_joystick ||
3941 setup.input[i].key.left != KSYM_UNDEFINED)
3942 stored_player[i].connected = TRUE;
3945 #if DEBUG_INIT_PLAYER
3948 printf("Player status after level initialization:\n");
3950 for (i = 0; i < MAX_PLAYERS; i++)
3952 struct PlayerInfo *player = &stored_player[i];
3954 printf("- player %d: present == %d, connected == %d, active == %d",
3960 if (local_player == player)
3961 printf(" (local player)");
3968 #if DEBUG_INIT_PLAYER
3970 printf("Reassigning players ...\n");
3973 /* check if any connected player was not found in playfield */
3974 for (i = 0; i < MAX_PLAYERS; i++)
3976 struct PlayerInfo *player = &stored_player[i];
3978 if (player->connected && !player->present)
3980 struct PlayerInfo *field_player = NULL;
3982 #if DEBUG_INIT_PLAYER
3984 printf("- looking for field player for player %d ...\n", i + 1);
3987 /* assign first free player found that is present in the playfield */
3990 /* first try: look for unmapped playfield player that is not connected */
3991 for (j = 0; j < MAX_PLAYERS; j++)
3992 if (field_player == NULL &&
3993 stored_player[j].present &&
3994 !stored_player[j].mapped &&
3995 !stored_player[j].connected)
3996 field_player = &stored_player[j];
3998 /* second try: look for *any* unmapped playfield player */
3999 for (j = 0; j < MAX_PLAYERS; j++)
4000 if (field_player == NULL &&
4001 stored_player[j].present &&
4002 !stored_player[j].mapped)
4003 field_player = &stored_player[j];
4005 /* first try: look for unmapped playfield player that is not connected */
4006 if (field_player == NULL)
4007 for (j = 0; j < MAX_PLAYERS; j++)
4008 if (stored_player[j].present &&
4009 !stored_player[j].mapped &&
4010 !stored_player[j].connected)
4011 field_player = &stored_player[j];
4013 /* second try: look for *any* unmapped playfield player */
4014 if (field_player == NULL)
4015 for (j = 0; j < MAX_PLAYERS; j++)
4016 if (stored_player[j].present &&
4017 !stored_player[j].mapped)
4018 field_player = &stored_player[j];
4021 if (field_player != NULL)
4023 int jx = field_player->jx, jy = field_player->jy;
4025 #if DEBUG_INIT_PLAYER
4027 printf("- found player %d\n", field_player->index_nr + 1);
4030 player->present = FALSE;
4031 player->active = FALSE;
4033 field_player->present = TRUE;
4034 field_player->active = TRUE;
4037 player->initial_element = field_player->initial_element;
4038 player->artwork_element = field_player->artwork_element;
4040 player->block_last_field = field_player->block_last_field;
4041 player->block_delay_adjustment = field_player->block_delay_adjustment;
4044 StorePlayer[jx][jy] = field_player->element_nr;
4046 field_player->jx = field_player->last_jx = jx;
4047 field_player->jy = field_player->last_jy = jy;
4049 if (local_player == player)
4050 local_player = field_player;
4052 map_player_action[field_player->index_nr] = i;
4054 field_player->mapped = TRUE;
4056 #if DEBUG_INIT_PLAYER
4058 printf("- map_player_action[%d] == %d\n",
4059 field_player->index_nr + 1, i + 1);
4064 if (player->connected && player->present)
4065 player->mapped = TRUE;
4068 #if DEBUG_INIT_PLAYER
4071 printf("Player status after player assignment (first stage):\n");
4073 for (i = 0; i < MAX_PLAYERS; i++)
4075 struct PlayerInfo *player = &stored_player[i];
4077 printf("- player %d: present == %d, connected == %d, active == %d",
4083 if (local_player == player)
4084 printf(" (local player)");
4093 /* check if any connected player was not found in playfield */
4094 for (i = 0; i < MAX_PLAYERS; i++)
4096 struct PlayerInfo *player = &stored_player[i];
4098 if (player->connected && !player->present)
4100 for (j = 0; j < MAX_PLAYERS; j++)
4102 struct PlayerInfo *field_player = &stored_player[j];
4103 int jx = field_player->jx, jy = field_player->jy;
4105 /* assign first free player found that is present in the playfield */
4106 if (field_player->present && !field_player->connected)
4108 player->present = TRUE;
4109 player->active = TRUE;
4111 field_player->present = FALSE;
4112 field_player->active = FALSE;
4114 player->initial_element = field_player->initial_element;
4115 player->artwork_element = field_player->artwork_element;
4117 player->block_last_field = field_player->block_last_field;
4118 player->block_delay_adjustment = field_player->block_delay_adjustment;
4120 StorePlayer[jx][jy] = player->element_nr;
4122 player->jx = player->last_jx = jx;
4123 player->jy = player->last_jy = jy;
4133 printf("::: local_player->present == %d\n", local_player->present);
4138 /* when playing a tape, eliminate all players who do not participate */
4140 #if USE_NEW_PLAYER_ASSIGNMENTS
4143 // if (!setup.team_mode)
4144 if (!TEST_game_team_mode)
4147 for (i = 0; i < MAX_PLAYERS; i++)
4149 if (stored_player[i].active &&
4150 !tape.player_participates[map_player_action[i]])
4152 struct PlayerInfo *player = &stored_player[i];
4153 int jx = player->jx, jy = player->jy;
4155 #if DEBUG_INIT_PLAYER
4157 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4160 player->active = FALSE;
4161 StorePlayer[jx][jy] = 0;
4162 Feld[jx][jy] = EL_EMPTY;
4166 for (i = 0; i < MAX_PLAYERS; i++)
4168 if (stored_player[i].active &&
4169 !tape.player_participates[i])
4171 struct PlayerInfo *player = &stored_player[i];
4172 int jx = player->jx, jy = player->jy;
4174 player->active = FALSE;
4175 StorePlayer[jx][jy] = 0;
4176 Feld[jx][jy] = EL_EMPTY;
4181 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4183 /* when in single player mode, eliminate all but the first active player */
4185 for (i = 0; i < MAX_PLAYERS; i++)
4187 if (stored_player[i].active)
4189 for (j = i + 1; j < MAX_PLAYERS; j++)
4191 if (stored_player[j].active)
4193 struct PlayerInfo *player = &stored_player[j];
4194 int jx = player->jx, jy = player->jy;
4196 player->active = FALSE;
4197 player->present = FALSE;
4199 StorePlayer[jx][jy] = 0;
4200 Feld[jx][jy] = EL_EMPTY;
4207 /* when recording the game, store which players take part in the game */
4210 #if USE_NEW_PLAYER_ASSIGNMENTS
4211 for (i = 0; i < MAX_PLAYERS; i++)
4212 if (stored_player[i].connected)
4213 tape.player_participates[i] = TRUE;
4215 for (i = 0; i < MAX_PLAYERS; i++)
4216 if (stored_player[i].active)
4217 tape.player_participates[i] = TRUE;
4221 #if DEBUG_INIT_PLAYER
4224 printf("Player status after player assignment (final stage):\n");
4226 for (i = 0; i < MAX_PLAYERS; i++)
4228 struct PlayerInfo *player = &stored_player[i];
4230 printf("- player %d: present == %d, connected == %d, active == %d",
4236 if (local_player == player)
4237 printf(" (local player)");
4244 if (BorderElement == EL_EMPTY)
4247 SBX_Right = lev_fieldx - SCR_FIELDX;
4249 SBY_Lower = lev_fieldy - SCR_FIELDY;
4254 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4256 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4261 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4262 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4264 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4265 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4267 if (EVEN(SCR_FIELDX))
4269 if (EVEN(SCR_FIELDY))
4274 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4275 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4277 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4278 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4281 /* if local player not found, look for custom element that might create
4282 the player (make some assumptions about the right custom element) */
4283 if (!local_player->present)
4285 int start_x = 0, start_y = 0;
4286 int found_rating = 0;
4287 int found_element = EL_UNDEFINED;
4288 int player_nr = local_player->index_nr;
4290 SCAN_PLAYFIELD(x, y)
4292 int element = Feld[x][y];
4297 if (level.use_start_element[player_nr] &&
4298 level.start_element[player_nr] == element &&
4305 found_element = element;
4308 if (!IS_CUSTOM_ELEMENT(element))
4311 if (CAN_CHANGE(element))
4313 for (i = 0; i < element_info[element].num_change_pages; i++)
4315 /* check for player created from custom element as single target */
4316 content = element_info[element].change_page[i].target_element;
4317 is_player = ELEM_IS_PLAYER(content);
4319 if (is_player && (found_rating < 3 ||
4320 (found_rating == 3 && element < found_element)))
4326 found_element = element;
4331 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4333 /* check for player created from custom element as explosion content */
4334 content = element_info[element].content.e[xx][yy];
4335 is_player = ELEM_IS_PLAYER(content);
4337 if (is_player && (found_rating < 2 ||
4338 (found_rating == 2 && element < found_element)))
4340 start_x = x + xx - 1;
4341 start_y = y + yy - 1;
4344 found_element = element;
4347 if (!CAN_CHANGE(element))
4350 for (i = 0; i < element_info[element].num_change_pages; i++)
4352 /* check for player created from custom element as extended target */
4354 element_info[element].change_page[i].target_content.e[xx][yy];
4356 is_player = ELEM_IS_PLAYER(content);
4358 if (is_player && (found_rating < 1 ||
4359 (found_rating == 1 && element < found_element)))
4361 start_x = x + xx - 1;
4362 start_y = y + yy - 1;
4365 found_element = element;
4371 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4372 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4375 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4376 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4381 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4382 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4383 local_player->jx - MIDPOSX);
4385 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4386 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4387 local_player->jy - MIDPOSY);
4391 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4395 /* do not use PLAYING mask for fading out from main screen */
4396 game_status = GAME_MODE_MAIN;
4401 if (!game.restart_level)
4402 CloseDoor(DOOR_CLOSE_1);
4405 if (level_editor_test_game)
4406 FadeSkipNextFadeIn();
4408 FadeSetEnterScreen();
4410 if (level_editor_test_game)
4411 fading = fading_none;
4413 fading = menu.destination;
4417 FadeOut(REDRAW_FIELD);
4420 FadeOut(REDRAW_FIELD);
4424 game_status = GAME_MODE_PLAYING;
4427 /* !!! FIX THIS (START) !!! */
4428 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4430 InitGameEngine_EM();
4432 /* blit playfield from scroll buffer to normal back buffer for fading in */
4433 BlitScreenToBitmap_EM(backbuffer);
4435 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4437 InitGameEngine_SP();
4439 /* blit playfield from scroll buffer to normal back buffer for fading in */
4440 BlitScreenToBitmap_SP(backbuffer);
4447 /* after drawing the level, correct some elements */
4448 if (game.timegate_time_left == 0)
4449 CloseAllOpenTimegates();
4452 BlitScreenToBitmap(backbuffer);
4454 /* blit playfield from scroll buffer to normal back buffer for fading in */
4455 if (setup.soft_scrolling)
4456 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4459 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4461 /* !!! FIX THIS (END) !!! */
4464 FadeIn(REDRAW_FIELD);
4467 FadeIn(REDRAW_FIELD);
4472 if (!game.restart_level)
4474 /* copy default game door content to main double buffer */
4477 /* !!! CHECK AGAIN !!! */
4478 SetPanelBackground();
4479 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4480 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4482 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4484 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4485 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4486 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4487 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4490 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4491 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4495 SetPanelBackground();
4496 SetDrawBackgroundMask(REDRAW_DOOR_1);
4499 UpdateAndDisplayGameControlValues();
4501 UpdateGameDoorValues();
4502 DrawGameDoorValues();
4505 if (!game.restart_level)
4509 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4510 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4511 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4515 /* copy actual game door content to door double buffer for OpenDoor() */
4516 BlitBitmap(drawto, bitmap_db_door,
4517 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4519 OpenDoor(DOOR_OPEN_ALL);
4521 PlaySound(SND_GAME_STARTING);
4523 if (setup.sound_music)
4526 KeyboardAutoRepeatOffUnlessAutoplay();
4528 #if DEBUG_INIT_PLAYER
4531 printf("Player status (final):\n");
4533 for (i = 0; i < MAX_PLAYERS; i++)
4535 struct PlayerInfo *player = &stored_player[i];
4537 printf("- player %d: present == %d, connected == %d, active == %d",
4543 if (local_player == player)
4544 printf(" (local player)");
4559 if (!game.restart_level && !tape.playing)
4561 LevelStats_incPlayed(level_nr);
4563 SaveLevelSetup_SeriesInfo();
4566 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4570 game.restart_level = FALSE;
4573 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4575 /* this is used for non-R'n'D game engines to update certain engine values */
4577 /* needed to determine if sounds are played within the visible screen area */
4578 scroll_x = actual_scroll_x;
4579 scroll_y = actual_scroll_y;
4582 void InitMovDir(int x, int y)
4584 int i, element = Feld[x][y];
4585 static int xy[4][2] =
4592 static int direction[3][4] =
4594 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4595 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4596 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4605 Feld[x][y] = EL_BUG;
4606 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4609 case EL_SPACESHIP_RIGHT:
4610 case EL_SPACESHIP_UP:
4611 case EL_SPACESHIP_LEFT:
4612 case EL_SPACESHIP_DOWN:
4613 Feld[x][y] = EL_SPACESHIP;
4614 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4617 case EL_BD_BUTTERFLY_RIGHT:
4618 case EL_BD_BUTTERFLY_UP:
4619 case EL_BD_BUTTERFLY_LEFT:
4620 case EL_BD_BUTTERFLY_DOWN:
4621 Feld[x][y] = EL_BD_BUTTERFLY;
4622 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4625 case EL_BD_FIREFLY_RIGHT:
4626 case EL_BD_FIREFLY_UP:
4627 case EL_BD_FIREFLY_LEFT:
4628 case EL_BD_FIREFLY_DOWN:
4629 Feld[x][y] = EL_BD_FIREFLY;
4630 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4633 case EL_PACMAN_RIGHT:
4635 case EL_PACMAN_LEFT:
4636 case EL_PACMAN_DOWN:
4637 Feld[x][y] = EL_PACMAN;
4638 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4641 case EL_YAMYAM_LEFT:
4642 case EL_YAMYAM_RIGHT:
4644 case EL_YAMYAM_DOWN:
4645 Feld[x][y] = EL_YAMYAM;
4646 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4649 case EL_SP_SNIKSNAK:
4650 MovDir[x][y] = MV_UP;
4653 case EL_SP_ELECTRON:
4654 MovDir[x][y] = MV_LEFT;
4661 Feld[x][y] = EL_MOLE;
4662 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4666 if (IS_CUSTOM_ELEMENT(element))
4668 struct ElementInfo *ei = &element_info[element];
4669 int move_direction_initial = ei->move_direction_initial;
4670 int move_pattern = ei->move_pattern;
4672 if (move_direction_initial == MV_START_PREVIOUS)
4674 if (MovDir[x][y] != MV_NONE)
4677 move_direction_initial = MV_START_AUTOMATIC;
4680 if (move_direction_initial == MV_START_RANDOM)
4681 MovDir[x][y] = 1 << RND(4);
4682 else if (move_direction_initial & MV_ANY_DIRECTION)
4683 MovDir[x][y] = move_direction_initial;
4684 else if (move_pattern == MV_ALL_DIRECTIONS ||
4685 move_pattern == MV_TURNING_LEFT ||
4686 move_pattern == MV_TURNING_RIGHT ||
4687 move_pattern == MV_TURNING_LEFT_RIGHT ||
4688 move_pattern == MV_TURNING_RIGHT_LEFT ||
4689 move_pattern == MV_TURNING_RANDOM)
4690 MovDir[x][y] = 1 << RND(4);
4691 else if (move_pattern == MV_HORIZONTAL)
4692 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4693 else if (move_pattern == MV_VERTICAL)
4694 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4695 else if (move_pattern & MV_ANY_DIRECTION)
4696 MovDir[x][y] = element_info[element].move_pattern;
4697 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4698 move_pattern == MV_ALONG_RIGHT_SIDE)
4700 /* use random direction as default start direction */
4701 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4702 MovDir[x][y] = 1 << RND(4);
4704 for (i = 0; i < NUM_DIRECTIONS; i++)
4706 int x1 = x + xy[i][0];
4707 int y1 = y + xy[i][1];
4709 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4711 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4712 MovDir[x][y] = direction[0][i];
4714 MovDir[x][y] = direction[1][i];
4723 MovDir[x][y] = 1 << RND(4);
4725 if (element != EL_BUG &&
4726 element != EL_SPACESHIP &&
4727 element != EL_BD_BUTTERFLY &&
4728 element != EL_BD_FIREFLY)
4731 for (i = 0; i < NUM_DIRECTIONS; i++)
4733 int x1 = x + xy[i][0];
4734 int y1 = y + xy[i][1];
4736 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4738 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4740 MovDir[x][y] = direction[0][i];
4743 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4744 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4746 MovDir[x][y] = direction[1][i];
4755 GfxDir[x][y] = MovDir[x][y];
4758 void InitAmoebaNr(int x, int y)
4761 int group_nr = AmoebeNachbarNr(x, y);
4765 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4767 if (AmoebaCnt[i] == 0)
4775 AmoebaNr[x][y] = group_nr;
4776 AmoebaCnt[group_nr]++;
4777 AmoebaCnt2[group_nr]++;
4780 static void PlayerWins(struct PlayerInfo *player)
4782 player->LevelSolved = TRUE;
4783 player->GameOver = TRUE;
4785 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4786 level.native_em_level->lev->score : player->score);
4788 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4790 player->LevelSolved_CountingScore = player->score_final;
4795 static int time, time_final;
4796 static int score, score_final;
4797 static int game_over_delay_1 = 0;
4798 static int game_over_delay_2 = 0;
4799 int game_over_delay_value_1 = 50;
4800 int game_over_delay_value_2 = 50;
4802 if (!local_player->LevelSolved_GameWon)
4806 /* do not start end game actions before the player stops moving (to exit) */
4807 if (local_player->MovPos)
4810 local_player->LevelSolved_GameWon = TRUE;
4811 local_player->LevelSolved_SaveTape = tape.recording;
4812 local_player->LevelSolved_SaveScore = !tape.playing;
4816 LevelStats_incSolved(level_nr);
4818 SaveLevelSetup_SeriesInfo();
4821 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4825 if (tape.auto_play) /* tape might already be stopped here */
4826 tape.auto_play_level_solved = TRUE;
4832 game_over_delay_1 = game_over_delay_value_1;
4833 game_over_delay_2 = game_over_delay_value_2;
4835 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4836 score = score_final = local_player->score_final;
4841 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4843 else if (game.no_time_limit && TimePlayed < 999)
4846 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4849 local_player->score_final = score_final;
4851 if (level_editor_test_game)
4854 score = score_final;
4857 local_player->LevelSolved_CountingTime = time;
4858 local_player->LevelSolved_CountingScore = score;
4860 game_panel_controls[GAME_PANEL_TIME].value = time;
4861 game_panel_controls[GAME_PANEL_SCORE].value = score;
4863 DisplayGameControlValues();
4865 DrawGameValue_Time(time);
4866 DrawGameValue_Score(score);
4870 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4872 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4874 /* close exit door after last player */
4875 if ((AllPlayersGone &&
4876 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4877 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4878 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4879 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4880 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4882 int element = Feld[ExitX][ExitY];
4885 if (element == EL_EM_EXIT_OPEN ||
4886 element == EL_EM_STEEL_EXIT_OPEN)
4893 Feld[ExitX][ExitY] =
4894 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4895 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4896 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4897 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4898 EL_EM_STEEL_EXIT_CLOSING);
4900 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4904 /* player disappears */
4905 DrawLevelField(ExitX, ExitY);
4908 for (i = 0; i < MAX_PLAYERS; i++)
4910 struct PlayerInfo *player = &stored_player[i];
4912 if (player->present)
4914 RemovePlayer(player);
4916 /* player disappears */
4917 DrawLevelField(player->jx, player->jy);
4922 PlaySound(SND_GAME_WINNING);
4925 if (game_over_delay_1 > 0)
4927 game_over_delay_1--;
4932 if (time != time_final)
4934 int time_to_go = ABS(time_final - time);
4935 int time_count_dir = (time < time_final ? +1 : -1);
4936 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4938 time += time_count_steps * time_count_dir;
4939 score += time_count_steps * level.score[SC_TIME_BONUS];
4942 local_player->LevelSolved_CountingTime = time;
4943 local_player->LevelSolved_CountingScore = score;
4945 game_panel_controls[GAME_PANEL_TIME].value = time;
4946 game_panel_controls[GAME_PANEL_SCORE].value = score;
4948 DisplayGameControlValues();
4950 DrawGameValue_Time(time);
4951 DrawGameValue_Score(score);
4954 if (time == time_final)
4955 StopSound(SND_GAME_LEVELTIME_BONUS);
4956 else if (setup.sound_loops)
4957 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4959 PlaySound(SND_GAME_LEVELTIME_BONUS);
4964 local_player->LevelSolved_PanelOff = TRUE;
4966 if (game_over_delay_2 > 0)
4968 game_over_delay_2--;
4981 boolean raise_level = FALSE;
4983 local_player->LevelSolved_GameEnd = TRUE;
4985 CloseDoor(DOOR_CLOSE_1);
4987 if (local_player->LevelSolved_SaveTape)
4994 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4996 SaveTape(tape.level_nr); /* ask to save tape */
5000 if (level_editor_test_game)
5002 game_status = GAME_MODE_MAIN;
5005 DrawAndFadeInMainMenu(REDRAW_FIELD);
5013 if (!local_player->LevelSolved_SaveScore)
5016 FadeOut(REDRAW_FIELD);
5019 game_status = GAME_MODE_MAIN;
5021 DrawAndFadeInMainMenu(REDRAW_FIELD);
5026 if (level_nr == leveldir_current->handicap_level)
5028 leveldir_current->handicap_level++;
5030 SaveLevelSetup_SeriesInfo();
5033 if (level_nr < leveldir_current->last_level)
5034 raise_level = TRUE; /* advance to next level */
5036 if ((hi_pos = NewHiScore()) >= 0)
5038 game_status = GAME_MODE_SCORES;
5040 DrawHallOfFame(hi_pos);
5051 FadeOut(REDRAW_FIELD);
5054 game_status = GAME_MODE_MAIN;
5062 DrawAndFadeInMainMenu(REDRAW_FIELD);
5071 LoadScore(level_nr);
5073 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5074 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5077 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5079 if (local_player->score_final > highscore[k].Score)
5081 /* player has made it to the hall of fame */
5083 if (k < MAX_SCORE_ENTRIES - 1)
5085 int m = MAX_SCORE_ENTRIES - 1;
5088 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5089 if (strEqual(setup.player_name, highscore[l].Name))
5091 if (m == k) /* player's new highscore overwrites his old one */
5095 for (l = m; l > k; l--)
5097 strcpy(highscore[l].Name, highscore[l - 1].Name);
5098 highscore[l].Score = highscore[l - 1].Score;
5105 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5106 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5107 highscore[k].Score = local_player->score_final;
5113 else if (!strncmp(setup.player_name, highscore[k].Name,
5114 MAX_PLAYER_NAME_LEN))
5115 break; /* player already there with a higher score */
5121 SaveScore(level_nr);
5126 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5128 int element = Feld[x][y];
5129 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5130 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5131 int horiz_move = (dx != 0);
5132 int sign = (horiz_move ? dx : dy);
5133 int step = sign * element_info[element].move_stepsize;
5135 /* special values for move stepsize for spring and things on conveyor belt */
5138 if (CAN_FALL(element) &&
5139 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5140 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5141 else if (element == EL_SPRING)
5142 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5148 inline static int getElementMoveStepsize(int x, int y)
5150 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5153 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5155 if (player->GfxAction != action || player->GfxDir != dir)
5158 printf("Player frame reset! (%d => %d, %d => %d)\n",
5159 player->GfxAction, action, player->GfxDir, dir);
5162 player->GfxAction = action;
5163 player->GfxDir = dir;
5165 player->StepFrame = 0;
5169 #if USE_GFX_RESET_GFX_ANIMATION
5170 static void ResetGfxFrame(int x, int y, boolean redraw)
5172 int element = Feld[x][y];
5173 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5174 int last_gfx_frame = GfxFrame[x][y];
5176 if (graphic_info[graphic].anim_global_sync)
5177 GfxFrame[x][y] = FrameCounter;
5178 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5179 GfxFrame[x][y] = CustomValue[x][y];
5180 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5181 GfxFrame[x][y] = element_info[element].collect_score;
5182 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5183 GfxFrame[x][y] = ChangeDelay[x][y];
5185 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5186 DrawLevelGraphicAnimation(x, y, graphic);
5190 static void ResetGfxAnimation(int x, int y)
5192 GfxAction[x][y] = ACTION_DEFAULT;
5193 GfxDir[x][y] = MovDir[x][y];
5196 #if USE_GFX_RESET_GFX_ANIMATION
5197 ResetGfxFrame(x, y, FALSE);
5201 static void ResetRandomAnimationValue(int x, int y)
5203 GfxRandom[x][y] = INIT_GFX_RANDOM();
5206 void InitMovingField(int x, int y, int direction)
5208 int element = Feld[x][y];
5209 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5210 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5213 boolean is_moving_before, is_moving_after;
5215 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5218 /* check if element was/is moving or being moved before/after mode change */
5221 is_moving_before = (WasJustMoving[x][y] != 0);
5223 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5224 is_moving_before = WasJustMoving[x][y];
5227 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5229 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5231 /* reset animation only for moving elements which change direction of moving
5232 or which just started or stopped moving
5233 (else CEs with property "can move" / "not moving" are reset each frame) */
5234 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5236 if (is_moving_before != is_moving_after ||
5237 direction != MovDir[x][y])
5238 ResetGfxAnimation(x, y);
5240 if ((is_moving_before || is_moving_after) && !continues_moving)
5241 ResetGfxAnimation(x, y);
5244 if (!continues_moving)
5245 ResetGfxAnimation(x, y);
5248 MovDir[x][y] = direction;
5249 GfxDir[x][y] = direction;
5251 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5252 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5253 direction == MV_DOWN && CAN_FALL(element) ?
5254 ACTION_FALLING : ACTION_MOVING);
5256 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5257 ACTION_FALLING : ACTION_MOVING);
5260 /* this is needed for CEs with property "can move" / "not moving" */
5262 if (is_moving_after)
5264 if (Feld[newx][newy] == EL_EMPTY)
5265 Feld[newx][newy] = EL_BLOCKED;
5267 MovDir[newx][newy] = MovDir[x][y];
5269 #if USE_NEW_CUSTOM_VALUE
5270 CustomValue[newx][newy] = CustomValue[x][y];
5273 GfxFrame[newx][newy] = GfxFrame[x][y];
5274 GfxRandom[newx][newy] = GfxRandom[x][y];
5275 GfxAction[newx][newy] = GfxAction[x][y];
5276 GfxDir[newx][newy] = GfxDir[x][y];
5280 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5282 int direction = MovDir[x][y];
5283 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5284 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5290 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5292 int oldx = x, oldy = y;
5293 int direction = MovDir[x][y];
5295 if (direction == MV_LEFT)
5297 else if (direction == MV_RIGHT)
5299 else if (direction == MV_UP)
5301 else if (direction == MV_DOWN)
5304 *comes_from_x = oldx;
5305 *comes_from_y = oldy;
5308 int MovingOrBlocked2Element(int x, int y)
5310 int element = Feld[x][y];
5312 if (element == EL_BLOCKED)
5316 Blocked2Moving(x, y, &oldx, &oldy);
5317 return Feld[oldx][oldy];
5323 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5325 /* like MovingOrBlocked2Element(), but if element is moving
5326 and (x,y) is the field the moving element is just leaving,
5327 return EL_BLOCKED instead of the element value */
5328 int element = Feld[x][y];
5330 if (IS_MOVING(x, y))
5332 if (element == EL_BLOCKED)
5336 Blocked2Moving(x, y, &oldx, &oldy);
5337 return Feld[oldx][oldy];
5346 static void RemoveField(int x, int y)
5348 Feld[x][y] = EL_EMPTY;
5354 #if USE_NEW_CUSTOM_VALUE
5355 CustomValue[x][y] = 0;
5359 ChangeDelay[x][y] = 0;
5360 ChangePage[x][y] = -1;
5361 Pushed[x][y] = FALSE;
5364 ExplodeField[x][y] = EX_TYPE_NONE;
5367 GfxElement[x][y] = EL_UNDEFINED;
5368 GfxAction[x][y] = ACTION_DEFAULT;
5369 GfxDir[x][y] = MV_NONE;
5371 /* !!! this would prevent the removed tile from being redrawn !!! */
5372 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5376 void RemoveMovingField(int x, int y)
5378 int oldx = x, oldy = y, newx = x, newy = y;
5379 int element = Feld[x][y];
5380 int next_element = EL_UNDEFINED;
5382 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5385 if (IS_MOVING(x, y))
5387 Moving2Blocked(x, y, &newx, &newy);
5389 if (Feld[newx][newy] != EL_BLOCKED)
5391 /* element is moving, but target field is not free (blocked), but
5392 already occupied by something different (example: acid pool);
5393 in this case, only remove the moving field, but not the target */
5395 RemoveField(oldx, oldy);
5397 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5399 TEST_DrawLevelField(oldx, oldy);
5404 else if (element == EL_BLOCKED)
5406 Blocked2Moving(x, y, &oldx, &oldy);
5407 if (!IS_MOVING(oldx, oldy))
5411 if (element == EL_BLOCKED &&
5412 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5413 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5414 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5415 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5416 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5417 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5418 next_element = get_next_element(Feld[oldx][oldy]);
5420 RemoveField(oldx, oldy);
5421 RemoveField(newx, newy);
5423 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5425 if (next_element != EL_UNDEFINED)
5426 Feld[oldx][oldy] = next_element;
5428 TEST_DrawLevelField(oldx, oldy);
5429 TEST_DrawLevelField(newx, newy);
5432 void DrawDynamite(int x, int y)
5434 int sx = SCREENX(x), sy = SCREENY(y);
5435 int graphic = el2img(Feld[x][y]);
5438 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5441 if (IS_WALKABLE_INSIDE(Back[x][y]))
5445 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5446 else if (Store[x][y])
5447 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5449 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5451 if (Back[x][y] || Store[x][y])
5452 DrawGraphicThruMask(sx, sy, graphic, frame);
5454 DrawGraphic(sx, sy, graphic, frame);
5457 void CheckDynamite(int x, int y)
5459 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5463 if (MovDelay[x][y] != 0)
5466 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5472 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5477 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5479 boolean num_checked_players = 0;
5482 for (i = 0; i < MAX_PLAYERS; i++)
5484 if (stored_player[i].active)
5486 int sx = stored_player[i].jx;
5487 int sy = stored_player[i].jy;
5489 if (num_checked_players == 0)
5496 *sx1 = MIN(*sx1, sx);
5497 *sy1 = MIN(*sy1, sy);
5498 *sx2 = MAX(*sx2, sx);
5499 *sy2 = MAX(*sy2, sy);
5502 num_checked_players++;
5507 static boolean checkIfAllPlayersFitToScreen_RND()
5509 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5511 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5513 return (sx2 - sx1 < SCR_FIELDX &&
5514 sy2 - sy1 < SCR_FIELDY);
5517 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5519 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5521 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5523 *sx = (sx1 + sx2) / 2;
5524 *sy = (sy1 + sy2) / 2;
5527 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5528 boolean center_screen, boolean quick_relocation)
5530 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5531 boolean no_delay = (tape.warp_forward);
5532 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5533 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5535 if (quick_relocation)
5537 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5539 if (!level.shifted_relocation || center_screen)
5541 /* quick relocation (without scrolling), with centering of screen */
5543 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5544 x > SBX_Right + MIDPOSX ? SBX_Right :
5547 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5548 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5553 /* quick relocation (without scrolling), but do not center screen */
5555 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5556 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5559 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5560 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5563 int offset_x = x + (scroll_x - center_scroll_x);
5564 int offset_y = y + (scroll_y - center_scroll_y);
5566 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5567 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5568 offset_x - MIDPOSX);
5570 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5571 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5572 offset_y - MIDPOSY);
5578 if (!level.shifted_relocation || center_screen)
5580 /* quick relocation (without scrolling), with centering of screen */
5582 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5583 x > SBX_Right + MIDPOSX ? SBX_Right :
5586 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5587 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5592 /* quick relocation (without scrolling), but do not center screen */
5594 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5595 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5598 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5599 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5602 int offset_x = x + (scroll_x - center_scroll_x);
5603 int offset_y = y + (scroll_y - center_scroll_y);
5605 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5606 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5607 offset_x - MIDPOSX);
5609 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5610 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5611 offset_y - MIDPOSY);
5614 /* quick relocation (without scrolling), inside visible screen area */
5616 int offset = game.scroll_delay_value;
5618 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5619 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5620 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5622 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5623 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5624 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5626 /* don't scroll over playfield boundaries */
5627 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5628 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5630 /* don't scroll over playfield boundaries */
5631 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5632 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5636 RedrawPlayfield(TRUE, 0,0,0,0);
5641 int scroll_xx, scroll_yy;
5643 if (!level.shifted_relocation || center_screen)
5645 /* visible relocation (with scrolling), with centering of screen */
5647 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5648 x > SBX_Right + MIDPOSX ? SBX_Right :
5651 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5652 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5657 /* visible relocation (with scrolling), but do not center screen */
5659 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5660 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5663 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5667 int offset_x = x + (scroll_x - center_scroll_x);
5668 int offset_y = y + (scroll_y - center_scroll_y);
5670 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5671 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5672 offset_x - MIDPOSX);
5674 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5675 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5676 offset_y - MIDPOSY);
5681 /* visible relocation (with scrolling), with centering of screen */
5683 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5684 x > SBX_Right + MIDPOSX ? SBX_Right :
5687 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5688 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5692 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5694 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5697 int fx = FX, fy = FY;
5699 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5700 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5702 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5708 fx += dx * TILEX / 2;
5709 fy += dy * TILEY / 2;
5711 ScrollLevel(dx, dy);
5714 /* scroll in two steps of half tile size to make things smoother */
5715 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5717 Delay(wait_delay_value);
5719 /* scroll second step to align at full tile size */
5721 Delay(wait_delay_value);
5726 Delay(wait_delay_value);
5730 void RelocatePlayer(int jx, int jy, int el_player_raw)
5732 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5733 int player_nr = GET_PLAYER_NR(el_player);
5734 struct PlayerInfo *player = &stored_player[player_nr];
5735 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5736 boolean no_delay = (tape.warp_forward);
5737 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5738 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5739 int old_jx = player->jx;
5740 int old_jy = player->jy;
5741 int old_element = Feld[old_jx][old_jy];
5742 int element = Feld[jx][jy];
5743 boolean player_relocated = (old_jx != jx || old_jy != jy);
5745 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5746 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5747 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5748 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5749 int leave_side_horiz = move_dir_horiz;
5750 int leave_side_vert = move_dir_vert;
5751 int enter_side = enter_side_horiz | enter_side_vert;
5752 int leave_side = leave_side_horiz | leave_side_vert;
5754 if (player->GameOver) /* do not reanimate dead player */
5757 if (!player_relocated) /* no need to relocate the player */
5760 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5762 RemoveField(jx, jy); /* temporarily remove newly placed player */
5763 DrawLevelField(jx, jy);
5766 if (player->present)
5768 while (player->MovPos)
5770 ScrollPlayer(player, SCROLL_GO_ON);
5771 ScrollScreen(NULL, SCROLL_GO_ON);
5773 AdvanceFrameAndPlayerCounters(player->index_nr);
5778 Delay(wait_delay_value);
5781 DrawPlayer(player); /* needed here only to cleanup last field */
5782 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5784 player->is_moving = FALSE;
5787 if (IS_CUSTOM_ELEMENT(old_element))
5788 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5790 player->index_bit, leave_side);
5792 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5794 player->index_bit, leave_side);
5796 Feld[jx][jy] = el_player;
5797 InitPlayerField(jx, jy, el_player, TRUE);
5799 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5800 possible that the relocation target field did not contain a player element,
5801 but a walkable element, to which the new player was relocated -- in this
5802 case, restore that (already initialized!) element on the player field */
5803 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5805 Feld[jx][jy] = element; /* restore previously existing element */
5807 /* !!! do not initialize already initialized element a second time !!! */
5808 /* (this causes at least problems with "element creation" CE trigger for
5809 already existing elements, and existing Sokoban fields counted twice) */
5810 InitField(jx, jy, FALSE);
5814 /* only visually relocate centered player */
5815 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5816 FALSE, level.instant_relocation);
5818 TestIfPlayerTouchesBadThing(jx, jy);
5819 TestIfPlayerTouchesCustomElement(jx, jy);
5821 if (IS_CUSTOM_ELEMENT(element))
5822 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5823 player->index_bit, enter_side);
5825 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5826 player->index_bit, enter_side);
5829 if (player->is_switching)
5831 /* ensure that relocation while still switching an element does not cause
5832 a new element to be treated as also switched directly after relocation
5833 (this is important for teleporter switches that teleport the player to
5834 a place where another teleporter switch is in the same direction, which
5835 would then incorrectly be treated as immediately switched before the
5836 direction key that caused the switch was released) */
5838 player->switch_x += jx - old_jx;
5839 player->switch_y += jy - old_jy;
5844 void Explode(int ex, int ey, int phase, int mode)
5850 /* !!! eliminate this variable !!! */
5851 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5853 if (game.explosions_delayed)
5855 ExplodeField[ex][ey] = mode;
5859 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5861 int center_element = Feld[ex][ey];
5862 int artwork_element, explosion_element; /* set these values later */
5865 /* --- This is only really needed (and now handled) in "Impact()". --- */
5866 /* do not explode moving elements that left the explode field in time */
5867 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5868 center_element == EL_EMPTY &&
5869 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5874 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5875 if (mode == EX_TYPE_NORMAL ||
5876 mode == EX_TYPE_CENTER ||
5877 mode == EX_TYPE_CROSS)
5878 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5881 /* remove things displayed in background while burning dynamite */
5882 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5885 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5887 /* put moving element to center field (and let it explode there) */
5888 center_element = MovingOrBlocked2Element(ex, ey);
5889 RemoveMovingField(ex, ey);
5890 Feld[ex][ey] = center_element;
5893 /* now "center_element" is finally determined -- set related values now */
5894 artwork_element = center_element; /* for custom player artwork */
5895 explosion_element = center_element; /* for custom player artwork */
5897 if (IS_PLAYER(ex, ey))
5899 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5901 artwork_element = stored_player[player_nr].artwork_element;
5903 if (level.use_explosion_element[player_nr])
5905 explosion_element = level.explosion_element[player_nr];
5906 artwork_element = explosion_element;
5911 if (mode == EX_TYPE_NORMAL ||
5912 mode == EX_TYPE_CENTER ||
5913 mode == EX_TYPE_CROSS)
5914 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5917 last_phase = element_info[explosion_element].explosion_delay + 1;
5919 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5921 int xx = x - ex + 1;
5922 int yy = y - ey + 1;
5925 if (!IN_LEV_FIELD(x, y) ||
5926 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5927 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5930 element = Feld[x][y];
5932 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5934 element = MovingOrBlocked2Element(x, y);
5936 if (!IS_EXPLOSION_PROOF(element))
5937 RemoveMovingField(x, y);
5940 /* indestructible elements can only explode in center (but not flames) */
5941 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5942 mode == EX_TYPE_BORDER)) ||
5943 element == EL_FLAMES)
5946 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5947 behaviour, for example when touching a yamyam that explodes to rocks
5948 with active deadly shield, a rock is created under the player !!! */
5949 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5951 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5952 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5953 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5955 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5958 if (IS_ACTIVE_BOMB(element))
5960 /* re-activate things under the bomb like gate or penguin */
5961 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5968 /* save walkable background elements while explosion on same tile */
5969 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5970 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5971 Back[x][y] = element;
5973 /* ignite explodable elements reached by other explosion */
5974 if (element == EL_EXPLOSION)
5975 element = Store2[x][y];
5977 if (AmoebaNr[x][y] &&
5978 (element == EL_AMOEBA_FULL ||
5979 element == EL_BD_AMOEBA ||
5980 element == EL_AMOEBA_GROWING))
5982 AmoebaCnt[AmoebaNr[x][y]]--;
5983 AmoebaCnt2[AmoebaNr[x][y]]--;
5988 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5990 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5992 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5994 if (PLAYERINFO(ex, ey)->use_murphy)
5995 Store[x][y] = EL_EMPTY;
5998 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5999 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6000 else if (ELEM_IS_PLAYER(center_element))
6001 Store[x][y] = EL_EMPTY;
6002 else if (center_element == EL_YAMYAM)
6003 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6004 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6005 Store[x][y] = element_info[center_element].content.e[xx][yy];
6007 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6008 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6009 otherwise) -- FIX THIS !!! */
6010 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6011 Store[x][y] = element_info[element].content.e[1][1];
6013 else if (!CAN_EXPLODE(element))
6014 Store[x][y] = element_info[element].content.e[1][1];
6017 Store[x][y] = EL_EMPTY;
6019 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6020 center_element == EL_AMOEBA_TO_DIAMOND)
6021 Store2[x][y] = element;
6023 Feld[x][y] = EL_EXPLOSION;
6024 GfxElement[x][y] = artwork_element;
6026 ExplodePhase[x][y] = 1;
6027 ExplodeDelay[x][y] = last_phase;
6032 if (center_element == EL_YAMYAM)
6033 game.yamyam_content_nr =
6034 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6046 GfxFrame[x][y] = 0; /* restart explosion animation */
6048 last_phase = ExplodeDelay[x][y];
6050 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6054 /* activate this even in non-DEBUG version until cause for crash in
6055 getGraphicAnimationFrame() (see below) is found and eliminated */
6061 /* this can happen if the player leaves an explosion just in time */
6062 if (GfxElement[x][y] == EL_UNDEFINED)
6063 GfxElement[x][y] = EL_EMPTY;
6065 if (GfxElement[x][y] == EL_UNDEFINED)
6068 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6069 printf("Explode(): This should never happen!\n");
6072 GfxElement[x][y] = EL_EMPTY;
6078 border_element = Store2[x][y];
6079 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6080 border_element = StorePlayer[x][y];
6082 if (phase == element_info[border_element].ignition_delay ||
6083 phase == last_phase)
6085 boolean border_explosion = FALSE;
6087 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6088 !PLAYER_EXPLOSION_PROTECTED(x, y))
6090 KillPlayerUnlessExplosionProtected(x, y);
6091 border_explosion = TRUE;
6093 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6095 Feld[x][y] = Store2[x][y];
6098 border_explosion = TRUE;
6100 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6102 AmoebeUmwandeln(x, y);
6104 border_explosion = TRUE;
6107 /* if an element just explodes due to another explosion (chain-reaction),
6108 do not immediately end the new explosion when it was the last frame of
6109 the explosion (as it would be done in the following "if"-statement!) */
6110 if (border_explosion && phase == last_phase)
6114 if (phase == last_phase)
6118 element = Feld[x][y] = Store[x][y];
6119 Store[x][y] = Store2[x][y] = 0;
6120 GfxElement[x][y] = EL_UNDEFINED;
6122 /* player can escape from explosions and might therefore be still alive */
6123 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6124 element <= EL_PLAYER_IS_EXPLODING_4)
6126 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6127 int explosion_element = EL_PLAYER_1 + player_nr;
6128 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6129 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6131 if (level.use_explosion_element[player_nr])
6132 explosion_element = level.explosion_element[player_nr];
6134 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6135 element_info[explosion_element].content.e[xx][yy]);
6138 /* restore probably existing indestructible background element */
6139 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6140 element = Feld[x][y] = Back[x][y];
6143 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6144 GfxDir[x][y] = MV_NONE;
6145 ChangeDelay[x][y] = 0;
6146 ChangePage[x][y] = -1;
6148 #if USE_NEW_CUSTOM_VALUE
6149 CustomValue[x][y] = 0;
6152 InitField_WithBug2(x, y, FALSE);
6154 TEST_DrawLevelField(x, y);
6156 TestIfElementTouchesCustomElement(x, y);
6158 if (GFX_CRUMBLED(element))
6159 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6161 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6162 StorePlayer[x][y] = 0;
6164 if (ELEM_IS_PLAYER(element))
6165 RelocatePlayer(x, y, element);
6167 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6169 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6170 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6173 TEST_DrawLevelFieldCrumbled(x, y);
6175 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6177 DrawLevelElement(x, y, Back[x][y]);
6178 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6180 else if (IS_WALKABLE_UNDER(Back[x][y]))
6182 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6183 DrawLevelElementThruMask(x, y, Back[x][y]);
6185 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6186 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6190 void DynaExplode(int ex, int ey)
6193 int dynabomb_element = Feld[ex][ey];
6194 int dynabomb_size = 1;
6195 boolean dynabomb_xl = FALSE;
6196 struct PlayerInfo *player;
6197 static int xy[4][2] =
6205 if (IS_ACTIVE_BOMB(dynabomb_element))
6207 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6208 dynabomb_size = player->dynabomb_size;
6209 dynabomb_xl = player->dynabomb_xl;
6210 player->dynabombs_left++;
6213 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6215 for (i = 0; i < NUM_DIRECTIONS; i++)
6217 for (j = 1; j <= dynabomb_size; j++)
6219 int x = ex + j * xy[i][0];
6220 int y = ey + j * xy[i][1];
6223 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6226 element = Feld[x][y];
6228 /* do not restart explosions of fields with active bombs */
6229 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6232 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6234 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6235 !IS_DIGGABLE(element) && !dynabomb_xl)
6241 void Bang(int x, int y)
6243 int element = MovingOrBlocked2Element(x, y);
6244 int explosion_type = EX_TYPE_NORMAL;
6246 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6248 struct PlayerInfo *player = PLAYERINFO(x, y);
6250 #if USE_FIX_CE_ACTION_WITH_PLAYER
6251 element = Feld[x][y] = player->initial_element;
6253 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6254 player->element_nr);
6257 if (level.use_explosion_element[player->index_nr])
6259 int explosion_element = level.explosion_element[player->index_nr];
6261 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6262 explosion_type = EX_TYPE_CROSS;
6263 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6264 explosion_type = EX_TYPE_CENTER;
6272 case EL_BD_BUTTERFLY:
6275 case EL_DARK_YAMYAM:
6279 RaiseScoreElement(element);
6282 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6283 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6284 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6285 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6286 case EL_DYNABOMB_INCREASE_NUMBER:
6287 case EL_DYNABOMB_INCREASE_SIZE:
6288 case EL_DYNABOMB_INCREASE_POWER:
6289 explosion_type = EX_TYPE_DYNA;
6292 case EL_DC_LANDMINE:
6294 case EL_EM_EXIT_OPEN:
6295 case EL_EM_STEEL_EXIT_OPEN:
6297 explosion_type = EX_TYPE_CENTER;
6302 case EL_LAMP_ACTIVE:
6303 case EL_AMOEBA_TO_DIAMOND:
6304 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6305 explosion_type = EX_TYPE_CENTER;
6309 if (element_info[element].explosion_type == EXPLODES_CROSS)
6310 explosion_type = EX_TYPE_CROSS;
6311 else if (element_info[element].explosion_type == EXPLODES_1X1)
6312 explosion_type = EX_TYPE_CENTER;
6316 if (explosion_type == EX_TYPE_DYNA)
6319 Explode(x, y, EX_PHASE_START, explosion_type);
6321 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6324 void SplashAcid(int x, int y)
6326 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6327 (!IN_LEV_FIELD(x - 1, y - 2) ||
6328 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6329 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6331 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6332 (!IN_LEV_FIELD(x + 1, y - 2) ||
6333 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6334 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6336 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6339 static void InitBeltMovement()
6341 static int belt_base_element[4] =
6343 EL_CONVEYOR_BELT_1_LEFT,
6344 EL_CONVEYOR_BELT_2_LEFT,
6345 EL_CONVEYOR_BELT_3_LEFT,
6346 EL_CONVEYOR_BELT_4_LEFT
6348 static int belt_base_active_element[4] =
6350 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6351 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6352 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6353 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6358 /* set frame order for belt animation graphic according to belt direction */
6359 for (i = 0; i < NUM_BELTS; i++)
6363 for (j = 0; j < NUM_BELT_PARTS; j++)
6365 int element = belt_base_active_element[belt_nr] + j;
6366 int graphic_1 = el2img(element);
6367 int graphic_2 = el2panelimg(element);
6369 if (game.belt_dir[i] == MV_LEFT)
6371 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6372 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6376 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6377 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6382 SCAN_PLAYFIELD(x, y)
6384 int element = Feld[x][y];
6386 for (i = 0; i < NUM_BELTS; i++)
6388 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6390 int e_belt_nr = getBeltNrFromBeltElement(element);
6393 if (e_belt_nr == belt_nr)
6395 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6397 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6404 static void ToggleBeltSwitch(int x, int y)
6406 static int belt_base_element[4] =
6408 EL_CONVEYOR_BELT_1_LEFT,
6409 EL_CONVEYOR_BELT_2_LEFT,
6410 EL_CONVEYOR_BELT_3_LEFT,
6411 EL_CONVEYOR_BELT_4_LEFT
6413 static int belt_base_active_element[4] =
6415 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6416 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6417 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6418 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6420 static int belt_base_switch_element[4] =
6422 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6423 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6424 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6425 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6427 static int belt_move_dir[4] =
6435 int element = Feld[x][y];
6436 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6437 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6438 int belt_dir = belt_move_dir[belt_dir_nr];
6441 if (!IS_BELT_SWITCH(element))
6444 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6445 game.belt_dir[belt_nr] = belt_dir;
6447 if (belt_dir_nr == 3)
6450 /* set frame order for belt animation graphic according to belt direction */
6451 for (i = 0; i < NUM_BELT_PARTS; i++)
6453 int element = belt_base_active_element[belt_nr] + i;
6454 int graphic_1 = el2img(element);
6455 int graphic_2 = el2panelimg(element);
6457 if (belt_dir == MV_LEFT)
6459 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6460 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6464 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6465 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6469 SCAN_PLAYFIELD(xx, yy)
6471 int element = Feld[xx][yy];
6473 if (IS_BELT_SWITCH(element))
6475 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6477 if (e_belt_nr == belt_nr)
6479 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6480 TEST_DrawLevelField(xx, yy);
6483 else if (IS_BELT(element) && belt_dir != MV_NONE)
6485 int e_belt_nr = getBeltNrFromBeltElement(element);
6487 if (e_belt_nr == belt_nr)
6489 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6491 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6492 TEST_DrawLevelField(xx, yy);
6495 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6497 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6499 if (e_belt_nr == belt_nr)
6501 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6503 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6504 TEST_DrawLevelField(xx, yy);
6510 static void ToggleSwitchgateSwitch(int x, int y)
6514 game.switchgate_pos = !game.switchgate_pos;
6516 SCAN_PLAYFIELD(xx, yy)
6518 int element = Feld[xx][yy];
6520 #if !USE_BOTH_SWITCHGATE_SWITCHES
6521 if (element == EL_SWITCHGATE_SWITCH_UP ||
6522 element == EL_SWITCHGATE_SWITCH_DOWN)
6524 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6525 TEST_DrawLevelField(xx, yy);
6527 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6528 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6530 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6531 TEST_DrawLevelField(xx, yy);
6534 if (element == EL_SWITCHGATE_SWITCH_UP)
6536 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6537 TEST_DrawLevelField(xx, yy);
6539 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6541 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6542 TEST_DrawLevelField(xx, yy);
6544 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6546 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6547 TEST_DrawLevelField(xx, yy);
6549 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6551 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6552 TEST_DrawLevelField(xx, yy);
6555 else if (element == EL_SWITCHGATE_OPEN ||
6556 element == EL_SWITCHGATE_OPENING)
6558 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6560 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6562 else if (element == EL_SWITCHGATE_CLOSED ||
6563 element == EL_SWITCHGATE_CLOSING)
6565 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6567 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6572 static int getInvisibleActiveFromInvisibleElement(int element)
6574 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6575 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6576 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6580 static int getInvisibleFromInvisibleActiveElement(int element)
6582 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6583 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6584 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6588 static void RedrawAllLightSwitchesAndInvisibleElements()
6592 SCAN_PLAYFIELD(x, y)
6594 int element = Feld[x][y];
6596 if (element == EL_LIGHT_SWITCH &&
6597 game.light_time_left > 0)
6599 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6600 TEST_DrawLevelField(x, y);
6602 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6603 game.light_time_left == 0)
6605 Feld[x][y] = EL_LIGHT_SWITCH;
6606 TEST_DrawLevelField(x, y);
6608 else if (element == EL_EMC_DRIPPER &&
6609 game.light_time_left > 0)
6611 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6612 TEST_DrawLevelField(x, y);
6614 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6615 game.light_time_left == 0)
6617 Feld[x][y] = EL_EMC_DRIPPER;
6618 TEST_DrawLevelField(x, y);
6620 else if (element == EL_INVISIBLE_STEELWALL ||
6621 element == EL_INVISIBLE_WALL ||
6622 element == EL_INVISIBLE_SAND)
6624 if (game.light_time_left > 0)
6625 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6627 TEST_DrawLevelField(x, y);
6629 /* uncrumble neighbour fields, if needed */
6630 if (element == EL_INVISIBLE_SAND)
6631 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6633 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6634 element == EL_INVISIBLE_WALL_ACTIVE ||
6635 element == EL_INVISIBLE_SAND_ACTIVE)
6637 if (game.light_time_left == 0)
6638 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6640 TEST_DrawLevelField(x, y);
6642 /* re-crumble neighbour fields, if needed */
6643 if (element == EL_INVISIBLE_SAND)
6644 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6649 static void RedrawAllInvisibleElementsForLenses()
6653 SCAN_PLAYFIELD(x, y)
6655 int element = Feld[x][y];
6657 if (element == EL_EMC_DRIPPER &&
6658 game.lenses_time_left > 0)
6660 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6661 TEST_DrawLevelField(x, y);
6663 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6664 game.lenses_time_left == 0)
6666 Feld[x][y] = EL_EMC_DRIPPER;
6667 TEST_DrawLevelField(x, y);
6669 else if (element == EL_INVISIBLE_STEELWALL ||
6670 element == EL_INVISIBLE_WALL ||
6671 element == EL_INVISIBLE_SAND)
6673 if (game.lenses_time_left > 0)
6674 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6676 TEST_DrawLevelField(x, y);
6678 /* uncrumble neighbour fields, if needed */
6679 if (element == EL_INVISIBLE_SAND)
6680 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6682 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6683 element == EL_INVISIBLE_WALL_ACTIVE ||
6684 element == EL_INVISIBLE_SAND_ACTIVE)
6686 if (game.lenses_time_left == 0)
6687 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6689 TEST_DrawLevelField(x, y);
6691 /* re-crumble neighbour fields, if needed */
6692 if (element == EL_INVISIBLE_SAND)
6693 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6698 static void RedrawAllInvisibleElementsForMagnifier()
6702 SCAN_PLAYFIELD(x, y)
6704 int element = Feld[x][y];
6706 if (element == EL_EMC_FAKE_GRASS &&
6707 game.magnify_time_left > 0)
6709 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6710 TEST_DrawLevelField(x, y);
6712 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6713 game.magnify_time_left == 0)
6715 Feld[x][y] = EL_EMC_FAKE_GRASS;
6716 TEST_DrawLevelField(x, y);
6718 else if (IS_GATE_GRAY(element) &&
6719 game.magnify_time_left > 0)
6721 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6722 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6723 IS_EM_GATE_GRAY(element) ?
6724 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6725 IS_EMC_GATE_GRAY(element) ?
6726 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6727 IS_DC_GATE_GRAY(element) ?
6728 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6730 TEST_DrawLevelField(x, y);
6732 else if (IS_GATE_GRAY_ACTIVE(element) &&
6733 game.magnify_time_left == 0)
6735 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6736 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6737 IS_EM_GATE_GRAY_ACTIVE(element) ?
6738 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6739 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6740 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6741 IS_DC_GATE_GRAY_ACTIVE(element) ?
6742 EL_DC_GATE_WHITE_GRAY :
6744 TEST_DrawLevelField(x, y);
6749 static void ToggleLightSwitch(int x, int y)
6751 int element = Feld[x][y];
6753 game.light_time_left =
6754 (element == EL_LIGHT_SWITCH ?
6755 level.time_light * FRAMES_PER_SECOND : 0);
6757 RedrawAllLightSwitchesAndInvisibleElements();
6760 static void ActivateTimegateSwitch(int x, int y)
6764 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6766 SCAN_PLAYFIELD(xx, yy)
6768 int element = Feld[xx][yy];
6770 if (element == EL_TIMEGATE_CLOSED ||
6771 element == EL_TIMEGATE_CLOSING)
6773 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6774 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6778 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6780 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6781 TEST_DrawLevelField(xx, yy);
6788 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6789 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6791 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6795 void Impact(int x, int y)
6797 boolean last_line = (y == lev_fieldy - 1);
6798 boolean object_hit = FALSE;
6799 boolean impact = (last_line || object_hit);
6800 int element = Feld[x][y];
6801 int smashed = EL_STEELWALL;
6803 if (!last_line) /* check if element below was hit */
6805 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6808 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6809 MovDir[x][y + 1] != MV_DOWN ||
6810 MovPos[x][y + 1] <= TILEY / 2));
6812 /* do not smash moving elements that left the smashed field in time */
6813 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6814 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6817 #if USE_QUICKSAND_IMPACT_BUGFIX
6818 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6820 RemoveMovingField(x, y + 1);
6821 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6822 Feld[x][y + 2] = EL_ROCK;
6823 TEST_DrawLevelField(x, y + 2);
6828 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6830 RemoveMovingField(x, y + 1);
6831 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6832 Feld[x][y + 2] = EL_ROCK;
6833 TEST_DrawLevelField(x, y + 2);
6840 smashed = MovingOrBlocked2Element(x, y + 1);
6842 impact = (last_line || object_hit);
6845 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6847 SplashAcid(x, y + 1);
6851 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6852 /* only reset graphic animation if graphic really changes after impact */
6854 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6856 ResetGfxAnimation(x, y);
6857 TEST_DrawLevelField(x, y);
6860 if (impact && CAN_EXPLODE_IMPACT(element))
6865 else if (impact && element == EL_PEARL &&
6866 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6868 ResetGfxAnimation(x, y);
6870 Feld[x][y] = EL_PEARL_BREAKING;
6871 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6874 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6876 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6881 if (impact && element == EL_AMOEBA_DROP)
6883 if (object_hit && IS_PLAYER(x, y + 1))
6884 KillPlayerUnlessEnemyProtected(x, y + 1);
6885 else if (object_hit && smashed == EL_PENGUIN)
6889 Feld[x][y] = EL_AMOEBA_GROWING;
6890 Store[x][y] = EL_AMOEBA_WET;
6892 ResetRandomAnimationValue(x, y);
6897 if (object_hit) /* check which object was hit */
6899 if ((CAN_PASS_MAGIC_WALL(element) &&
6900 (smashed == EL_MAGIC_WALL ||
6901 smashed == EL_BD_MAGIC_WALL)) ||
6902 (CAN_PASS_DC_MAGIC_WALL(element) &&
6903 smashed == EL_DC_MAGIC_WALL))
6906 int activated_magic_wall =
6907 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6908 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6909 EL_DC_MAGIC_WALL_ACTIVE);
6911 /* activate magic wall / mill */
6912 SCAN_PLAYFIELD(xx, yy)
6914 if (Feld[xx][yy] == smashed)
6915 Feld[xx][yy] = activated_magic_wall;
6918 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6919 game.magic_wall_active = TRUE;
6921 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6922 SND_MAGIC_WALL_ACTIVATING :
6923 smashed == EL_BD_MAGIC_WALL ?
6924 SND_BD_MAGIC_WALL_ACTIVATING :
6925 SND_DC_MAGIC_WALL_ACTIVATING));
6928 if (IS_PLAYER(x, y + 1))
6930 if (CAN_SMASH_PLAYER(element))
6932 KillPlayerUnlessEnemyProtected(x, y + 1);
6936 else if (smashed == EL_PENGUIN)
6938 if (CAN_SMASH_PLAYER(element))
6944 else if (element == EL_BD_DIAMOND)
6946 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6952 else if (((element == EL_SP_INFOTRON ||
6953 element == EL_SP_ZONK) &&
6954 (smashed == EL_SP_SNIKSNAK ||
6955 smashed == EL_SP_ELECTRON ||
6956 smashed == EL_SP_DISK_ORANGE)) ||
6957 (element == EL_SP_INFOTRON &&
6958 smashed == EL_SP_DISK_YELLOW))
6963 else if (CAN_SMASH_EVERYTHING(element))
6965 if (IS_CLASSIC_ENEMY(smashed) ||
6966 CAN_EXPLODE_SMASHED(smashed))
6971 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6973 if (smashed == EL_LAMP ||
6974 smashed == EL_LAMP_ACTIVE)
6979 else if (smashed == EL_NUT)
6981 Feld[x][y + 1] = EL_NUT_BREAKING;
6982 PlayLevelSound(x, y, SND_NUT_BREAKING);
6983 RaiseScoreElement(EL_NUT);
6986 else if (smashed == EL_PEARL)
6988 ResetGfxAnimation(x, y);
6990 Feld[x][y + 1] = EL_PEARL_BREAKING;
6991 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6994 else if (smashed == EL_DIAMOND)
6996 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6997 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7000 else if (IS_BELT_SWITCH(smashed))
7002 ToggleBeltSwitch(x, y + 1);
7004 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7005 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7006 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7007 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7009 ToggleSwitchgateSwitch(x, y + 1);
7011 else if (smashed == EL_LIGHT_SWITCH ||
7012 smashed == EL_LIGHT_SWITCH_ACTIVE)
7014 ToggleLightSwitch(x, y + 1);
7019 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7022 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7024 CheckElementChangeBySide(x, y + 1, smashed, element,
7025 CE_SWITCHED, CH_SIDE_TOP);
7026 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7032 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7037 /* play sound of magic wall / mill */
7039 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7040 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7041 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7043 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7044 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7045 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7046 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7047 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7048 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7053 /* play sound of object that hits the ground */
7054 if (last_line || object_hit)
7055 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7058 inline static void TurnRoundExt(int x, int y)
7070 { 0, 0 }, { 0, 0 }, { 0, 0 },
7075 int left, right, back;
7079 { MV_DOWN, MV_UP, MV_RIGHT },
7080 { MV_UP, MV_DOWN, MV_LEFT },
7082 { MV_LEFT, MV_RIGHT, MV_DOWN },
7086 { MV_RIGHT, MV_LEFT, MV_UP }
7089 int element = Feld[x][y];
7090 int move_pattern = element_info[element].move_pattern;
7092 int old_move_dir = MovDir[x][y];
7093 int left_dir = turn[old_move_dir].left;
7094 int right_dir = turn[old_move_dir].right;
7095 int back_dir = turn[old_move_dir].back;
7097 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7098 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7099 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7100 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7102 int left_x = x + left_dx, left_y = y + left_dy;
7103 int right_x = x + right_dx, right_y = y + right_dy;
7104 int move_x = x + move_dx, move_y = y + move_dy;
7108 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7110 TestIfBadThingTouchesOtherBadThing(x, y);
7112 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7113 MovDir[x][y] = right_dir;
7114 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7115 MovDir[x][y] = left_dir;
7117 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7119 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7122 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7124 TestIfBadThingTouchesOtherBadThing(x, y);
7126 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7127 MovDir[x][y] = left_dir;
7128 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7129 MovDir[x][y] = right_dir;
7131 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7133 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7136 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7138 TestIfBadThingTouchesOtherBadThing(x, y);
7140 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7141 MovDir[x][y] = left_dir;
7142 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7143 MovDir[x][y] = right_dir;
7145 if (MovDir[x][y] != old_move_dir)
7148 else if (element == EL_YAMYAM)
7150 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7151 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7153 if (can_turn_left && can_turn_right)
7154 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7155 else if (can_turn_left)
7156 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7157 else if (can_turn_right)
7158 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7160 MovDir[x][y] = back_dir;
7162 MovDelay[x][y] = 16 + 16 * RND(3);
7164 else if (element == EL_DARK_YAMYAM)
7166 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7168 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7171 if (can_turn_left && can_turn_right)
7172 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7173 else if (can_turn_left)
7174 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7175 else if (can_turn_right)
7176 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7178 MovDir[x][y] = back_dir;
7180 MovDelay[x][y] = 16 + 16 * RND(3);
7182 else if (element == EL_PACMAN)
7184 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7185 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7187 if (can_turn_left && can_turn_right)
7188 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7189 else if (can_turn_left)
7190 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7191 else if (can_turn_right)
7192 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7194 MovDir[x][y] = back_dir;
7196 MovDelay[x][y] = 6 + RND(40);
7198 else if (element == EL_PIG)
7200 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7201 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7202 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7203 boolean should_turn_left, should_turn_right, should_move_on;
7205 int rnd = RND(rnd_value);
7207 should_turn_left = (can_turn_left &&
7209 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7210 y + back_dy + left_dy)));
7211 should_turn_right = (can_turn_right &&
7213 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7214 y + back_dy + right_dy)));
7215 should_move_on = (can_move_on &&
7218 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7219 y + move_dy + left_dy) ||
7220 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7221 y + move_dy + right_dy)));
7223 if (should_turn_left || should_turn_right || should_move_on)
7225 if (should_turn_left && should_turn_right && should_move_on)
7226 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7227 rnd < 2 * rnd_value / 3 ? right_dir :
7229 else if (should_turn_left && should_turn_right)
7230 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7231 else if (should_turn_left && should_move_on)
7232 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7233 else if (should_turn_right && should_move_on)
7234 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7235 else if (should_turn_left)
7236 MovDir[x][y] = left_dir;
7237 else if (should_turn_right)
7238 MovDir[x][y] = right_dir;
7239 else if (should_move_on)
7240 MovDir[x][y] = old_move_dir;
7242 else if (can_move_on && rnd > rnd_value / 8)
7243 MovDir[x][y] = old_move_dir;
7244 else if (can_turn_left && can_turn_right)
7245 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7246 else if (can_turn_left && rnd > rnd_value / 8)
7247 MovDir[x][y] = left_dir;
7248 else if (can_turn_right && rnd > rnd_value/8)
7249 MovDir[x][y] = right_dir;
7251 MovDir[x][y] = back_dir;
7253 xx = x + move_xy[MovDir[x][y]].dx;
7254 yy = y + move_xy[MovDir[x][y]].dy;
7256 if (!IN_LEV_FIELD(xx, yy) ||
7257 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7258 MovDir[x][y] = old_move_dir;
7262 else if (element == EL_DRAGON)
7264 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7265 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7266 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7268 int rnd = RND(rnd_value);
7270 if (can_move_on && rnd > rnd_value / 8)
7271 MovDir[x][y] = old_move_dir;
7272 else if (can_turn_left && can_turn_right)
7273 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7274 else if (can_turn_left && rnd > rnd_value / 8)
7275 MovDir[x][y] = left_dir;
7276 else if (can_turn_right && rnd > rnd_value / 8)
7277 MovDir[x][y] = right_dir;
7279 MovDir[x][y] = back_dir;
7281 xx = x + move_xy[MovDir[x][y]].dx;
7282 yy = y + move_xy[MovDir[x][y]].dy;
7284 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7285 MovDir[x][y] = old_move_dir;
7289 else if (element == EL_MOLE)
7291 boolean can_move_on =
7292 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7293 IS_AMOEBOID(Feld[move_x][move_y]) ||
7294 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7297 boolean can_turn_left =
7298 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7299 IS_AMOEBOID(Feld[left_x][left_y])));
7301 boolean can_turn_right =
7302 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7303 IS_AMOEBOID(Feld[right_x][right_y])));
7305 if (can_turn_left && can_turn_right)
7306 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7307 else if (can_turn_left)
7308 MovDir[x][y] = left_dir;
7310 MovDir[x][y] = right_dir;
7313 if (MovDir[x][y] != old_move_dir)
7316 else if (element == EL_BALLOON)
7318 MovDir[x][y] = game.wind_direction;
7321 else if (element == EL_SPRING)
7323 #if USE_NEW_SPRING_BUMPER
7324 if (MovDir[x][y] & MV_HORIZONTAL)
7326 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7327 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7329 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7330 ResetGfxAnimation(move_x, move_y);
7331 TEST_DrawLevelField(move_x, move_y);
7333 MovDir[x][y] = back_dir;
7335 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7336 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7337 MovDir[x][y] = MV_NONE;
7340 if (MovDir[x][y] & MV_HORIZONTAL &&
7341 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7342 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7343 MovDir[x][y] = MV_NONE;
7348 else if (element == EL_ROBOT ||
7349 element == EL_SATELLITE ||
7350 element == EL_PENGUIN ||
7351 element == EL_EMC_ANDROID)
7353 int attr_x = -1, attr_y = -1;
7364 for (i = 0; i < MAX_PLAYERS; i++)
7366 struct PlayerInfo *player = &stored_player[i];
7367 int jx = player->jx, jy = player->jy;
7369 if (!player->active)
7373 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7381 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7382 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7383 game.engine_version < VERSION_IDENT(3,1,0,0)))
7389 if (element == EL_PENGUIN)
7392 static int xy[4][2] =
7400 for (i = 0; i < NUM_DIRECTIONS; i++)
7402 int ex = x + xy[i][0];
7403 int ey = y + xy[i][1];
7405 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7406 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7407 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7408 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7417 MovDir[x][y] = MV_NONE;
7419 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7420 else if (attr_x > x)
7421 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7423 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7424 else if (attr_y > y)
7425 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7427 if (element == EL_ROBOT)
7431 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7432 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7433 Moving2Blocked(x, y, &newx, &newy);
7435 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7436 MovDelay[x][y] = 8 + 8 * !RND(3);
7438 MovDelay[x][y] = 16;
7440 else if (element == EL_PENGUIN)
7446 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7448 boolean first_horiz = RND(2);
7449 int new_move_dir = MovDir[x][y];
7452 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7453 Moving2Blocked(x, y, &newx, &newy);
7455 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7459 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7460 Moving2Blocked(x, y, &newx, &newy);
7462 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7465 MovDir[x][y] = old_move_dir;
7469 else if (element == EL_SATELLITE)
7475 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7477 boolean first_horiz = RND(2);
7478 int new_move_dir = MovDir[x][y];
7481 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7482 Moving2Blocked(x, y, &newx, &newy);
7484 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7488 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7489 Moving2Blocked(x, y, &newx, &newy);
7491 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7494 MovDir[x][y] = old_move_dir;
7498 else if (element == EL_EMC_ANDROID)
7500 static int check_pos[16] =
7502 -1, /* 0 => (invalid) */
7503 7, /* 1 => MV_LEFT */
7504 3, /* 2 => MV_RIGHT */
7505 -1, /* 3 => (invalid) */
7507 0, /* 5 => MV_LEFT | MV_UP */
7508 2, /* 6 => MV_RIGHT | MV_UP */
7509 -1, /* 7 => (invalid) */
7510 5, /* 8 => MV_DOWN */
7511 6, /* 9 => MV_LEFT | MV_DOWN */
7512 4, /* 10 => MV_RIGHT | MV_DOWN */
7513 -1, /* 11 => (invalid) */
7514 -1, /* 12 => (invalid) */
7515 -1, /* 13 => (invalid) */
7516 -1, /* 14 => (invalid) */
7517 -1, /* 15 => (invalid) */
7525 { -1, -1, MV_LEFT | MV_UP },
7527 { +1, -1, MV_RIGHT | MV_UP },
7528 { +1, 0, MV_RIGHT },
7529 { +1, +1, MV_RIGHT | MV_DOWN },
7531 { -1, +1, MV_LEFT | MV_DOWN },
7534 int start_pos, check_order;
7535 boolean can_clone = FALSE;
7538 /* check if there is any free field around current position */
7539 for (i = 0; i < 8; i++)
7541 int newx = x + check_xy[i].dx;
7542 int newy = y + check_xy[i].dy;
7544 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7552 if (can_clone) /* randomly find an element to clone */
7556 start_pos = check_pos[RND(8)];
7557 check_order = (RND(2) ? -1 : +1);
7559 for (i = 0; i < 8; i++)
7561 int pos_raw = start_pos + i * check_order;
7562 int pos = (pos_raw + 8) % 8;
7563 int newx = x + check_xy[pos].dx;
7564 int newy = y + check_xy[pos].dy;
7566 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7568 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7569 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7571 Store[x][y] = Feld[newx][newy];
7580 if (can_clone) /* randomly find a direction to move */
7584 start_pos = check_pos[RND(8)];
7585 check_order = (RND(2) ? -1 : +1);
7587 for (i = 0; i < 8; i++)
7589 int pos_raw = start_pos + i * check_order;
7590 int pos = (pos_raw + 8) % 8;
7591 int newx = x + check_xy[pos].dx;
7592 int newy = y + check_xy[pos].dy;
7593 int new_move_dir = check_xy[pos].dir;
7595 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7597 MovDir[x][y] = new_move_dir;
7598 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7607 if (can_clone) /* cloning and moving successful */
7610 /* cannot clone -- try to move towards player */
7612 start_pos = check_pos[MovDir[x][y] & 0x0f];
7613 check_order = (RND(2) ? -1 : +1);
7615 for (i = 0; i < 3; i++)
7617 /* first check start_pos, then previous/next or (next/previous) pos */
7618 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7619 int pos = (pos_raw + 8) % 8;
7620 int newx = x + check_xy[pos].dx;
7621 int newy = y + check_xy[pos].dy;
7622 int new_move_dir = check_xy[pos].dir;
7624 if (IS_PLAYER(newx, newy))
7627 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7629 MovDir[x][y] = new_move_dir;
7630 MovDelay[x][y] = level.android_move_time * 8 + 1;
7637 else if (move_pattern == MV_TURNING_LEFT ||
7638 move_pattern == MV_TURNING_RIGHT ||
7639 move_pattern == MV_TURNING_LEFT_RIGHT ||
7640 move_pattern == MV_TURNING_RIGHT_LEFT ||
7641 move_pattern == MV_TURNING_RANDOM ||
7642 move_pattern == MV_ALL_DIRECTIONS)
7644 boolean can_turn_left =
7645 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7646 boolean can_turn_right =
7647 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7649 if (element_info[element].move_stepsize == 0) /* "not moving" */
7652 if (move_pattern == MV_TURNING_LEFT)
7653 MovDir[x][y] = left_dir;
7654 else if (move_pattern == MV_TURNING_RIGHT)
7655 MovDir[x][y] = right_dir;
7656 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7657 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7658 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7659 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7660 else if (move_pattern == MV_TURNING_RANDOM)
7661 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7662 can_turn_right && !can_turn_left ? right_dir :
7663 RND(2) ? left_dir : right_dir);
7664 else if (can_turn_left && can_turn_right)
7665 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7666 else if (can_turn_left)
7667 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7668 else if (can_turn_right)
7669 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7671 MovDir[x][y] = back_dir;
7673 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7675 else if (move_pattern == MV_HORIZONTAL ||
7676 move_pattern == MV_VERTICAL)
7678 if (move_pattern & old_move_dir)
7679 MovDir[x][y] = back_dir;
7680 else if (move_pattern == MV_HORIZONTAL)
7681 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7682 else if (move_pattern == MV_VERTICAL)
7683 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7685 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7687 else if (move_pattern & MV_ANY_DIRECTION)
7689 MovDir[x][y] = move_pattern;
7690 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7692 else if (move_pattern & MV_WIND_DIRECTION)
7694 MovDir[x][y] = game.wind_direction;
7695 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7697 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7699 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7700 MovDir[x][y] = left_dir;
7701 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7702 MovDir[x][y] = right_dir;
7704 if (MovDir[x][y] != old_move_dir)
7705 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7707 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7709 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7710 MovDir[x][y] = right_dir;
7711 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7712 MovDir[x][y] = left_dir;
7714 if (MovDir[x][y] != old_move_dir)
7715 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7717 else if (move_pattern == MV_TOWARDS_PLAYER ||
7718 move_pattern == MV_AWAY_FROM_PLAYER)
7720 int attr_x = -1, attr_y = -1;
7722 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7733 for (i = 0; i < MAX_PLAYERS; i++)
7735 struct PlayerInfo *player = &stored_player[i];
7736 int jx = player->jx, jy = player->jy;
7738 if (!player->active)
7742 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7750 MovDir[x][y] = MV_NONE;
7752 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7753 else if (attr_x > x)
7754 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7756 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7757 else if (attr_y > y)
7758 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7760 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7762 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7764 boolean first_horiz = RND(2);
7765 int new_move_dir = MovDir[x][y];
7767 if (element_info[element].move_stepsize == 0) /* "not moving" */
7769 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7770 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7776 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7777 Moving2Blocked(x, y, &newx, &newy);
7779 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7783 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7784 Moving2Blocked(x, y, &newx, &newy);
7786 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7789 MovDir[x][y] = old_move_dir;
7792 else if (move_pattern == MV_WHEN_PUSHED ||
7793 move_pattern == MV_WHEN_DROPPED)
7795 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796 MovDir[x][y] = MV_NONE;
7800 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7802 static int test_xy[7][2] =
7812 static int test_dir[7] =
7822 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7823 int move_preference = -1000000; /* start with very low preference */
7824 int new_move_dir = MV_NONE;
7825 int start_test = RND(4);
7828 for (i = 0; i < NUM_DIRECTIONS; i++)
7830 int move_dir = test_dir[start_test + i];
7831 int move_dir_preference;
7833 xx = x + test_xy[start_test + i][0];
7834 yy = y + test_xy[start_test + i][1];
7836 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7837 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7839 new_move_dir = move_dir;
7844 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7847 move_dir_preference = -1 * RunnerVisit[xx][yy];
7848 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7849 move_dir_preference = PlayerVisit[xx][yy];
7851 if (move_dir_preference > move_preference)
7853 /* prefer field that has not been visited for the longest time */
7854 move_preference = move_dir_preference;
7855 new_move_dir = move_dir;
7857 else if (move_dir_preference == move_preference &&
7858 move_dir == old_move_dir)
7860 /* prefer last direction when all directions are preferred equally */
7861 move_preference = move_dir_preference;
7862 new_move_dir = move_dir;
7866 MovDir[x][y] = new_move_dir;
7867 if (old_move_dir != new_move_dir)
7868 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7872 static void TurnRound(int x, int y)
7874 int direction = MovDir[x][y];
7878 GfxDir[x][y] = MovDir[x][y];
7880 if (direction != MovDir[x][y])
7884 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7886 ResetGfxFrame(x, y, FALSE);
7889 static boolean JustBeingPushed(int x, int y)
7893 for (i = 0; i < MAX_PLAYERS; i++)
7895 struct PlayerInfo *player = &stored_player[i];
7897 if (player->active && player->is_pushing && player->MovPos)
7899 int next_jx = player->jx + (player->jx - player->last_jx);
7900 int next_jy = player->jy + (player->jy - player->last_jy);
7902 if (x == next_jx && y == next_jy)
7910 void StartMoving(int x, int y)
7912 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7913 int element = Feld[x][y];
7918 if (MovDelay[x][y] == 0)
7919 GfxAction[x][y] = ACTION_DEFAULT;
7921 if (CAN_FALL(element) && y < lev_fieldy - 1)
7923 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7924 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7925 if (JustBeingPushed(x, y))
7928 if (element == EL_QUICKSAND_FULL)
7930 if (IS_FREE(x, y + 1))
7932 InitMovingField(x, y, MV_DOWN);
7933 started_moving = TRUE;
7935 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7936 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7937 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7938 Store[x][y] = EL_ROCK;
7940 Store[x][y] = EL_ROCK;
7943 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7945 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7947 if (!MovDelay[x][y])
7949 MovDelay[x][y] = TILEY + 1;
7951 ResetGfxAnimation(x, y);
7952 ResetGfxAnimation(x, y + 1);
7957 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7958 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7965 Feld[x][y] = EL_QUICKSAND_EMPTY;
7966 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7967 Store[x][y + 1] = Store[x][y];
7970 PlayLevelSoundAction(x, y, ACTION_FILLING);
7972 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7974 if (!MovDelay[x][y])
7976 MovDelay[x][y] = TILEY + 1;
7978 ResetGfxAnimation(x, y);
7979 ResetGfxAnimation(x, y + 1);
7984 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7985 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7992 Feld[x][y] = EL_QUICKSAND_EMPTY;
7993 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7994 Store[x][y + 1] = Store[x][y];
7997 PlayLevelSoundAction(x, y, ACTION_FILLING);
8000 else if (element == EL_QUICKSAND_FAST_FULL)
8002 if (IS_FREE(x, y + 1))
8004 InitMovingField(x, y, MV_DOWN);
8005 started_moving = TRUE;
8007 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8008 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8009 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8010 Store[x][y] = EL_ROCK;
8012 Store[x][y] = EL_ROCK;
8015 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8017 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8019 if (!MovDelay[x][y])
8021 MovDelay[x][y] = TILEY + 1;
8023 ResetGfxAnimation(x, y);
8024 ResetGfxAnimation(x, y + 1);
8029 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8030 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8037 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8038 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8039 Store[x][y + 1] = Store[x][y];
8042 PlayLevelSoundAction(x, y, ACTION_FILLING);
8044 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8046 if (!MovDelay[x][y])
8048 MovDelay[x][y] = TILEY + 1;
8050 ResetGfxAnimation(x, y);
8051 ResetGfxAnimation(x, y + 1);
8056 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8057 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8064 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8065 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8066 Store[x][y + 1] = Store[x][y];
8069 PlayLevelSoundAction(x, y, ACTION_FILLING);
8072 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8073 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8075 InitMovingField(x, y, MV_DOWN);
8076 started_moving = TRUE;
8078 Feld[x][y] = EL_QUICKSAND_FILLING;
8079 Store[x][y] = element;
8081 PlayLevelSoundAction(x, y, ACTION_FILLING);
8083 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8084 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8086 InitMovingField(x, y, MV_DOWN);
8087 started_moving = TRUE;
8089 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8090 Store[x][y] = element;
8092 PlayLevelSoundAction(x, y, ACTION_FILLING);
8094 else if (element == EL_MAGIC_WALL_FULL)
8096 if (IS_FREE(x, y + 1))
8098 InitMovingField(x, y, MV_DOWN);
8099 started_moving = TRUE;
8101 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8102 Store[x][y] = EL_CHANGED(Store[x][y]);
8104 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8106 if (!MovDelay[x][y])
8107 MovDelay[x][y] = TILEY / 4 + 1;
8116 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8117 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8118 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8122 else if (element == EL_BD_MAGIC_WALL_FULL)
8124 if (IS_FREE(x, y + 1))
8126 InitMovingField(x, y, MV_DOWN);
8127 started_moving = TRUE;
8129 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8130 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8132 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8134 if (!MovDelay[x][y])
8135 MovDelay[x][y] = TILEY / 4 + 1;
8144 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8145 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8146 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8150 else if (element == EL_DC_MAGIC_WALL_FULL)
8152 if (IS_FREE(x, y + 1))
8154 InitMovingField(x, y, MV_DOWN);
8155 started_moving = TRUE;
8157 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8158 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8160 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8162 if (!MovDelay[x][y])
8163 MovDelay[x][y] = TILEY / 4 + 1;
8172 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8173 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8174 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8178 else if ((CAN_PASS_MAGIC_WALL(element) &&
8179 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8180 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8181 (CAN_PASS_DC_MAGIC_WALL(element) &&
8182 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8185 InitMovingField(x, y, MV_DOWN);
8186 started_moving = TRUE;
8189 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8190 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8191 EL_DC_MAGIC_WALL_FILLING);
8192 Store[x][y] = element;
8194 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8196 SplashAcid(x, y + 1);
8198 InitMovingField(x, y, MV_DOWN);
8199 started_moving = TRUE;
8201 Store[x][y] = EL_ACID;
8204 #if USE_FIX_IMPACT_COLLISION
8205 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8206 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8208 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8209 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8211 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8212 CAN_FALL(element) && WasJustFalling[x][y] &&
8213 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8215 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8216 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8217 (Feld[x][y + 1] == EL_BLOCKED)))
8219 /* this is needed for a special case not covered by calling "Impact()"
8220 from "ContinueMoving()": if an element moves to a tile directly below
8221 another element which was just falling on that tile (which was empty
8222 in the previous frame), the falling element above would just stop
8223 instead of smashing the element below (in previous version, the above
8224 element was just checked for "moving" instead of "falling", resulting
8225 in incorrect smashes caused by horizontal movement of the above
8226 element; also, the case of the player being the element to smash was
8227 simply not covered here... :-/ ) */
8229 CheckCollision[x][y] = 0;
8230 CheckImpact[x][y] = 0;
8234 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8236 if (MovDir[x][y] == MV_NONE)
8238 InitMovingField(x, y, MV_DOWN);
8239 started_moving = TRUE;
8242 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8244 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8245 MovDir[x][y] = MV_DOWN;
8247 InitMovingField(x, y, MV_DOWN);
8248 started_moving = TRUE;
8250 else if (element == EL_AMOEBA_DROP)
8252 Feld[x][y] = EL_AMOEBA_GROWING;
8253 Store[x][y] = EL_AMOEBA_WET;
8255 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8256 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8257 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8258 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8260 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8261 (IS_FREE(x - 1, y + 1) ||
8262 Feld[x - 1][y + 1] == EL_ACID));
8263 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8264 (IS_FREE(x + 1, y + 1) ||
8265 Feld[x + 1][y + 1] == EL_ACID));
8266 boolean can_fall_any = (can_fall_left || can_fall_right);
8267 boolean can_fall_both = (can_fall_left && can_fall_right);
8268 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8270 #if USE_NEW_ALL_SLIPPERY
8271 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8273 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8274 can_fall_right = FALSE;
8275 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8276 can_fall_left = FALSE;
8277 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8278 can_fall_right = FALSE;
8279 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8280 can_fall_left = FALSE;
8282 can_fall_any = (can_fall_left || can_fall_right);
8283 can_fall_both = FALSE;
8286 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8288 if (slippery_type == SLIPPERY_ONLY_LEFT)
8289 can_fall_right = FALSE;
8290 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8291 can_fall_left = FALSE;
8292 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8293 can_fall_right = FALSE;
8294 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8295 can_fall_left = FALSE;
8297 can_fall_any = (can_fall_left || can_fall_right);
8298 can_fall_both = (can_fall_left && can_fall_right);
8302 #if USE_NEW_ALL_SLIPPERY
8304 #if USE_NEW_SP_SLIPPERY
8305 /* !!! better use the same properties as for custom elements here !!! */
8306 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8307 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8309 can_fall_right = FALSE; /* slip down on left side */
8310 can_fall_both = FALSE;
8315 #if USE_NEW_ALL_SLIPPERY
8318 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8319 can_fall_right = FALSE; /* slip down on left side */
8321 can_fall_left = !(can_fall_right = RND(2));
8323 can_fall_both = FALSE;
8328 if (game.emulation == EMU_BOULDERDASH ||
8329 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8330 can_fall_right = FALSE; /* slip down on left side */
8332 can_fall_left = !(can_fall_right = RND(2));
8334 can_fall_both = FALSE;
8340 /* if not determined otherwise, prefer left side for slipping down */
8341 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8342 started_moving = TRUE;
8346 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8348 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8351 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8352 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8353 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8354 int belt_dir = game.belt_dir[belt_nr];
8356 if ((belt_dir == MV_LEFT && left_is_free) ||
8357 (belt_dir == MV_RIGHT && right_is_free))
8359 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8361 InitMovingField(x, y, belt_dir);
8362 started_moving = TRUE;
8364 Pushed[x][y] = TRUE;
8365 Pushed[nextx][y] = TRUE;
8367 GfxAction[x][y] = ACTION_DEFAULT;
8371 MovDir[x][y] = 0; /* if element was moving, stop it */
8376 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8378 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8380 if (CAN_MOVE(element) && !started_moving)
8383 int move_pattern = element_info[element].move_pattern;
8388 if (MovDir[x][y] == MV_NONE)
8390 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8391 x, y, element, element_info[element].token_name);
8392 printf("StartMoving(): This should never happen!\n");
8397 Moving2Blocked(x, y, &newx, &newy);
8399 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8402 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8403 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8405 WasJustMoving[x][y] = 0;
8406 CheckCollision[x][y] = 0;
8408 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8410 if (Feld[x][y] != element) /* element has changed */
8414 if (!MovDelay[x][y]) /* start new movement phase */
8416 /* all objects that can change their move direction after each step
8417 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8419 if (element != EL_YAMYAM &&
8420 element != EL_DARK_YAMYAM &&
8421 element != EL_PACMAN &&
8422 !(move_pattern & MV_ANY_DIRECTION) &&
8423 move_pattern != MV_TURNING_LEFT &&
8424 move_pattern != MV_TURNING_RIGHT &&
8425 move_pattern != MV_TURNING_LEFT_RIGHT &&
8426 move_pattern != MV_TURNING_RIGHT_LEFT &&
8427 move_pattern != MV_TURNING_RANDOM)
8431 if (MovDelay[x][y] && (element == EL_BUG ||
8432 element == EL_SPACESHIP ||
8433 element == EL_SP_SNIKSNAK ||
8434 element == EL_SP_ELECTRON ||
8435 element == EL_MOLE))
8436 TEST_DrawLevelField(x, y);
8440 if (MovDelay[x][y]) /* wait some time before next movement */
8444 if (element == EL_ROBOT ||
8445 element == EL_YAMYAM ||
8446 element == EL_DARK_YAMYAM)
8448 DrawLevelElementAnimationIfNeeded(x, y, element);
8449 PlayLevelSoundAction(x, y, ACTION_WAITING);
8451 else if (element == EL_SP_ELECTRON)
8452 DrawLevelElementAnimationIfNeeded(x, y, element);
8453 else if (element == EL_DRAGON)
8456 int dir = MovDir[x][y];
8457 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8458 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8459 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8460 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8461 dir == MV_UP ? IMG_FLAMES_1_UP :
8462 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8463 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8465 GfxAction[x][y] = ACTION_ATTACKING;
8467 if (IS_PLAYER(x, y))
8468 DrawPlayerField(x, y);
8470 TEST_DrawLevelField(x, y);
8472 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8474 for (i = 1; i <= 3; i++)
8476 int xx = x + i * dx;
8477 int yy = y + i * dy;
8478 int sx = SCREENX(xx);
8479 int sy = SCREENY(yy);
8480 int flame_graphic = graphic + (i - 1);
8482 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8487 int flamed = MovingOrBlocked2Element(xx, yy);
8491 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8493 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8494 RemoveMovingField(xx, yy);
8496 RemoveField(xx, yy);
8498 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8501 RemoveMovingField(xx, yy);
8504 ChangeDelay[xx][yy] = 0;
8506 Feld[xx][yy] = EL_FLAMES;
8508 if (IN_SCR_FIELD(sx, sy))
8510 TEST_DrawLevelFieldCrumbled(xx, yy);
8511 DrawGraphic(sx, sy, flame_graphic, frame);
8516 if (Feld[xx][yy] == EL_FLAMES)
8517 Feld[xx][yy] = EL_EMPTY;
8518 TEST_DrawLevelField(xx, yy);
8523 if (MovDelay[x][y]) /* element still has to wait some time */
8525 PlayLevelSoundAction(x, y, ACTION_WAITING);
8531 /* now make next step */
8533 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8535 if (DONT_COLLIDE_WITH(element) &&
8536 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8537 !PLAYER_ENEMY_PROTECTED(newx, newy))
8539 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8544 else if (CAN_MOVE_INTO_ACID(element) &&
8545 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8546 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8547 (MovDir[x][y] == MV_DOWN ||
8548 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8550 SplashAcid(newx, newy);
8551 Store[x][y] = EL_ACID;
8553 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8555 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8556 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8557 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8558 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8561 TEST_DrawLevelField(x, y);
8563 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8564 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8565 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8567 local_player->friends_still_needed--;
8568 if (!local_player->friends_still_needed &&
8569 !local_player->GameOver && AllPlayersGone)
8570 PlayerWins(local_player);
8574 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8576 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8577 TEST_DrawLevelField(newx, newy);
8579 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8581 else if (!IS_FREE(newx, newy))
8583 GfxAction[x][y] = ACTION_WAITING;
8585 if (IS_PLAYER(x, y))
8586 DrawPlayerField(x, y);
8588 TEST_DrawLevelField(x, y);
8593 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8595 if (IS_FOOD_PIG(Feld[newx][newy]))
8597 if (IS_MOVING(newx, newy))
8598 RemoveMovingField(newx, newy);
8601 Feld[newx][newy] = EL_EMPTY;
8602 TEST_DrawLevelField(newx, newy);
8605 PlayLevelSound(x, y, SND_PIG_DIGGING);
8607 else if (!IS_FREE(newx, newy))
8609 if (IS_PLAYER(x, y))
8610 DrawPlayerField(x, y);
8612 TEST_DrawLevelField(x, y);
8617 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8619 if (Store[x][y] != EL_EMPTY)
8621 boolean can_clone = FALSE;
8624 /* check if element to clone is still there */
8625 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8627 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8635 /* cannot clone or target field not free anymore -- do not clone */
8636 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8637 Store[x][y] = EL_EMPTY;
8640 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8642 if (IS_MV_DIAGONAL(MovDir[x][y]))
8644 int diagonal_move_dir = MovDir[x][y];
8645 int stored = Store[x][y];
8646 int change_delay = 8;
8649 /* android is moving diagonally */
8651 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8653 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8654 GfxElement[x][y] = EL_EMC_ANDROID;
8655 GfxAction[x][y] = ACTION_SHRINKING;
8656 GfxDir[x][y] = diagonal_move_dir;
8657 ChangeDelay[x][y] = change_delay;
8659 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8662 DrawLevelGraphicAnimation(x, y, graphic);
8663 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8665 if (Feld[newx][newy] == EL_ACID)
8667 SplashAcid(newx, newy);
8672 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8674 Store[newx][newy] = EL_EMC_ANDROID;
8675 GfxElement[newx][newy] = EL_EMC_ANDROID;
8676 GfxAction[newx][newy] = ACTION_GROWING;
8677 GfxDir[newx][newy] = diagonal_move_dir;
8678 ChangeDelay[newx][newy] = change_delay;
8680 graphic = el_act_dir2img(GfxElement[newx][newy],
8681 GfxAction[newx][newy], GfxDir[newx][newy]);
8683 DrawLevelGraphicAnimation(newx, newy, graphic);
8684 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8690 Feld[newx][newy] = EL_EMPTY;
8691 TEST_DrawLevelField(newx, newy);
8693 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8696 else if (!IS_FREE(newx, newy))
8699 if (IS_PLAYER(x, y))
8700 DrawPlayerField(x, y);
8702 TEST_DrawLevelField(x, y);
8708 else if (IS_CUSTOM_ELEMENT(element) &&
8709 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8712 if (!DigFieldByCE(newx, newy, element))
8715 int new_element = Feld[newx][newy];
8717 if (!IS_FREE(newx, newy))
8719 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8720 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8723 /* no element can dig solid indestructible elements */
8724 if (IS_INDESTRUCTIBLE(new_element) &&
8725 !IS_DIGGABLE(new_element) &&
8726 !IS_COLLECTIBLE(new_element))
8729 if (AmoebaNr[newx][newy] &&
8730 (new_element == EL_AMOEBA_FULL ||
8731 new_element == EL_BD_AMOEBA ||
8732 new_element == EL_AMOEBA_GROWING))
8734 AmoebaCnt[AmoebaNr[newx][newy]]--;
8735 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8738 if (IS_MOVING(newx, newy))
8739 RemoveMovingField(newx, newy);
8742 RemoveField(newx, newy);
8743 TEST_DrawLevelField(newx, newy);
8746 /* if digged element was about to explode, prevent the explosion */
8747 ExplodeField[newx][newy] = EX_TYPE_NONE;
8749 PlayLevelSoundAction(x, y, action);
8752 Store[newx][newy] = EL_EMPTY;
8755 /* this makes it possible to leave the removed element again */
8756 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8757 Store[newx][newy] = new_element;
8759 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8761 int move_leave_element = element_info[element].move_leave_element;
8763 /* this makes it possible to leave the removed element again */
8764 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8765 new_element : move_leave_element);
8771 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8773 RunnerVisit[x][y] = FrameCounter;
8774 PlayerVisit[x][y] /= 8; /* expire player visit path */
8777 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8779 if (!IS_FREE(newx, newy))
8781 if (IS_PLAYER(x, y))
8782 DrawPlayerField(x, y);
8784 TEST_DrawLevelField(x, y);
8790 boolean wanna_flame = !RND(10);
8791 int dx = newx - x, dy = newy - y;
8792 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8793 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8794 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8795 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8796 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8797 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8800 IS_CLASSIC_ENEMY(element1) ||
8801 IS_CLASSIC_ENEMY(element2)) &&
8802 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8803 element1 != EL_FLAMES && element2 != EL_FLAMES)
8805 ResetGfxAnimation(x, y);
8806 GfxAction[x][y] = ACTION_ATTACKING;
8808 if (IS_PLAYER(x, y))
8809 DrawPlayerField(x, y);
8811 TEST_DrawLevelField(x, y);
8813 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8815 MovDelay[x][y] = 50;
8819 RemoveField(newx, newy);
8821 Feld[newx][newy] = EL_FLAMES;
8822 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8825 RemoveField(newx1, newy1);
8827 Feld[newx1][newy1] = EL_FLAMES;
8829 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8832 RemoveField(newx2, newy2);
8834 Feld[newx2][newy2] = EL_FLAMES;
8841 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8842 Feld[newx][newy] == EL_DIAMOND)
8844 if (IS_MOVING(newx, newy))
8845 RemoveMovingField(newx, newy);
8848 Feld[newx][newy] = EL_EMPTY;
8849 TEST_DrawLevelField(newx, newy);
8852 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8854 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8855 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8857 if (AmoebaNr[newx][newy])
8859 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8860 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8861 Feld[newx][newy] == EL_BD_AMOEBA)
8862 AmoebaCnt[AmoebaNr[newx][newy]]--;
8867 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8869 RemoveMovingField(newx, newy);
8872 if (IS_MOVING(newx, newy))
8874 RemoveMovingField(newx, newy);
8879 Feld[newx][newy] = EL_EMPTY;
8880 TEST_DrawLevelField(newx, newy);
8883 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8885 else if ((element == EL_PACMAN || element == EL_MOLE)
8886 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8888 if (AmoebaNr[newx][newy])
8890 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8891 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8892 Feld[newx][newy] == EL_BD_AMOEBA)
8893 AmoebaCnt[AmoebaNr[newx][newy]]--;
8896 if (element == EL_MOLE)
8898 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8899 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8901 ResetGfxAnimation(x, y);
8902 GfxAction[x][y] = ACTION_DIGGING;
8903 TEST_DrawLevelField(x, y);
8905 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8907 return; /* wait for shrinking amoeba */
8909 else /* element == EL_PACMAN */
8911 Feld[newx][newy] = EL_EMPTY;
8912 TEST_DrawLevelField(newx, newy);
8913 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8916 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8917 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8918 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8920 /* wait for shrinking amoeba to completely disappear */
8923 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8925 /* object was running against a wall */
8930 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8931 if (move_pattern & MV_ANY_DIRECTION &&
8932 move_pattern == MovDir[x][y])
8934 int blocking_element =
8935 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8937 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8940 element = Feld[x][y]; /* element might have changed */
8944 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8945 DrawLevelElementAnimation(x, y, element);
8947 if (DONT_TOUCH(element))
8948 TestIfBadThingTouchesPlayer(x, y);
8953 InitMovingField(x, y, MovDir[x][y]);
8955 PlayLevelSoundAction(x, y, ACTION_MOVING);
8959 ContinueMoving(x, y);
8962 void ContinueMoving(int x, int y)
8964 int element = Feld[x][y];
8965 struct ElementInfo *ei = &element_info[element];
8966 int direction = MovDir[x][y];
8967 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8968 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8969 int newx = x + dx, newy = y + dy;
8970 int stored = Store[x][y];
8971 int stored_new = Store[newx][newy];
8972 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8973 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8974 boolean last_line = (newy == lev_fieldy - 1);
8976 MovPos[x][y] += getElementMoveStepsize(x, y);
8978 if (pushed_by_player) /* special case: moving object pushed by player */
8979 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8981 if (ABS(MovPos[x][y]) < TILEX)
8984 int ee = Feld[x][y];
8985 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8986 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8988 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8989 x, y, ABS(MovPos[x][y]),
8991 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8994 TEST_DrawLevelField(x, y);
8996 return; /* element is still moving */
8999 /* element reached destination field */
9001 Feld[x][y] = EL_EMPTY;
9002 Feld[newx][newy] = element;
9003 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9005 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9007 element = Feld[newx][newy] = EL_ACID;
9009 else if (element == EL_MOLE)
9011 Feld[x][y] = EL_SAND;
9013 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9015 else if (element == EL_QUICKSAND_FILLING)
9017 element = Feld[newx][newy] = get_next_element(element);
9018 Store[newx][newy] = Store[x][y];
9020 else if (element == EL_QUICKSAND_EMPTYING)
9022 Feld[x][y] = get_next_element(element);
9023 element = Feld[newx][newy] = Store[x][y];
9025 else if (element == EL_QUICKSAND_FAST_FILLING)
9027 element = Feld[newx][newy] = get_next_element(element);
9028 Store[newx][newy] = Store[x][y];
9030 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9032 Feld[x][y] = get_next_element(element);
9033 element = Feld[newx][newy] = Store[x][y];
9035 else if (element == EL_MAGIC_WALL_FILLING)
9037 element = Feld[newx][newy] = get_next_element(element);
9038 if (!game.magic_wall_active)
9039 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9040 Store[newx][newy] = Store[x][y];
9042 else if (element == EL_MAGIC_WALL_EMPTYING)
9044 Feld[x][y] = get_next_element(element);
9045 if (!game.magic_wall_active)
9046 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9047 element = Feld[newx][newy] = Store[x][y];
9049 #if USE_NEW_CUSTOM_VALUE
9050 InitField(newx, newy, FALSE);
9053 else if (element == EL_BD_MAGIC_WALL_FILLING)
9055 element = Feld[newx][newy] = get_next_element(element);
9056 if (!game.magic_wall_active)
9057 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9058 Store[newx][newy] = Store[x][y];
9060 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9062 Feld[x][y] = get_next_element(element);
9063 if (!game.magic_wall_active)
9064 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9065 element = Feld[newx][newy] = Store[x][y];
9067 #if USE_NEW_CUSTOM_VALUE
9068 InitField(newx, newy, FALSE);
9071 else if (element == EL_DC_MAGIC_WALL_FILLING)
9073 element = Feld[newx][newy] = get_next_element(element);
9074 if (!game.magic_wall_active)
9075 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9076 Store[newx][newy] = Store[x][y];
9078 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9080 Feld[x][y] = get_next_element(element);
9081 if (!game.magic_wall_active)
9082 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9083 element = Feld[newx][newy] = Store[x][y];
9085 #if USE_NEW_CUSTOM_VALUE
9086 InitField(newx, newy, FALSE);
9089 else if (element == EL_AMOEBA_DROPPING)
9091 Feld[x][y] = get_next_element(element);
9092 element = Feld[newx][newy] = Store[x][y];
9094 else if (element == EL_SOKOBAN_OBJECT)
9097 Feld[x][y] = Back[x][y];
9099 if (Back[newx][newy])
9100 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9102 Back[x][y] = Back[newx][newy] = 0;
9105 Store[x][y] = EL_EMPTY;
9110 MovDelay[newx][newy] = 0;
9112 if (CAN_CHANGE_OR_HAS_ACTION(element))
9114 /* copy element change control values to new field */
9115 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9116 ChangePage[newx][newy] = ChangePage[x][y];
9117 ChangeCount[newx][newy] = ChangeCount[x][y];
9118 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9121 #if USE_NEW_CUSTOM_VALUE
9122 CustomValue[newx][newy] = CustomValue[x][y];
9125 ChangeDelay[x][y] = 0;
9126 ChangePage[x][y] = -1;
9127 ChangeCount[x][y] = 0;
9128 ChangeEvent[x][y] = -1;
9130 #if USE_NEW_CUSTOM_VALUE
9131 CustomValue[x][y] = 0;
9134 /* copy animation control values to new field */
9135 GfxFrame[newx][newy] = GfxFrame[x][y];
9136 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9137 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9138 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9140 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9142 /* some elements can leave other elements behind after moving */
9144 if (ei->move_leave_element != EL_EMPTY &&
9145 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9146 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9148 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9149 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9150 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9153 int move_leave_element = ei->move_leave_element;
9157 /* this makes it possible to leave the removed element again */
9158 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9159 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9161 /* this makes it possible to leave the removed element again */
9162 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9163 move_leave_element = stored;
9166 /* this makes it possible to leave the removed element again */
9167 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9168 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9169 move_leave_element = stored;
9172 Feld[x][y] = move_leave_element;
9174 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9175 MovDir[x][y] = direction;
9177 InitField(x, y, FALSE);
9179 if (GFX_CRUMBLED(Feld[x][y]))
9180 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9182 if (ELEM_IS_PLAYER(move_leave_element))
9183 RelocatePlayer(x, y, move_leave_element);
9186 /* do this after checking for left-behind element */
9187 ResetGfxAnimation(x, y); /* reset animation values for old field */
9189 if (!CAN_MOVE(element) ||
9190 (CAN_FALL(element) && direction == MV_DOWN &&
9191 (element == EL_SPRING ||
9192 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9193 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9194 GfxDir[x][y] = MovDir[newx][newy] = 0;
9196 TEST_DrawLevelField(x, y);
9197 TEST_DrawLevelField(newx, newy);
9199 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9201 /* prevent pushed element from moving on in pushed direction */
9202 if (pushed_by_player && CAN_MOVE(element) &&
9203 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9204 !(element_info[element].move_pattern & direction))
9205 TurnRound(newx, newy);
9207 /* prevent elements on conveyor belt from moving on in last direction */
9208 if (pushed_by_conveyor && CAN_FALL(element) &&
9209 direction & MV_HORIZONTAL)
9210 MovDir[newx][newy] = 0;
9212 if (!pushed_by_player)
9214 int nextx = newx + dx, nexty = newy + dy;
9215 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9217 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9219 if (CAN_FALL(element) && direction == MV_DOWN)
9220 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9222 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9223 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9225 #if USE_FIX_IMPACT_COLLISION
9226 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9227 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9231 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9233 TestIfBadThingTouchesPlayer(newx, newy);
9234 TestIfBadThingTouchesFriend(newx, newy);
9236 if (!IS_CUSTOM_ELEMENT(element))
9237 TestIfBadThingTouchesOtherBadThing(newx, newy);
9239 else if (element == EL_PENGUIN)
9240 TestIfFriendTouchesBadThing(newx, newy);
9242 if (DONT_GET_HIT_BY(element))
9244 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9247 /* give the player one last chance (one more frame) to move away */
9248 if (CAN_FALL(element) && direction == MV_DOWN &&
9249 (last_line || (!IS_FREE(x, newy + 1) &&
9250 (!IS_PLAYER(x, newy + 1) ||
9251 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9254 if (pushed_by_player && !game.use_change_when_pushing_bug)
9256 int push_side = MV_DIR_OPPOSITE(direction);
9257 struct PlayerInfo *player = PLAYERINFO(x, y);
9259 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9260 player->index_bit, push_side);
9261 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9262 player->index_bit, push_side);
9265 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9266 MovDelay[newx][newy] = 1;
9268 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9270 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9273 if (ChangePage[newx][newy] != -1) /* delayed change */
9275 int page = ChangePage[newx][newy];
9276 struct ElementChangeInfo *change = &ei->change_page[page];
9278 ChangePage[newx][newy] = -1;
9280 if (change->can_change)
9282 if (ChangeElement(newx, newy, element, page))
9284 if (change->post_change_function)
9285 change->post_change_function(newx, newy);
9289 if (change->has_action)
9290 ExecuteCustomElementAction(newx, newy, element, page);
9294 TestIfElementHitsCustomElement(newx, newy, direction);
9295 TestIfPlayerTouchesCustomElement(newx, newy);
9296 TestIfElementTouchesCustomElement(newx, newy);
9298 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9299 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9300 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9301 MV_DIR_OPPOSITE(direction));
9304 int AmoebeNachbarNr(int ax, int ay)
9307 int element = Feld[ax][ay];
9309 static int xy[4][2] =
9317 for (i = 0; i < NUM_DIRECTIONS; i++)
9319 int x = ax + xy[i][0];
9320 int y = ay + xy[i][1];
9322 if (!IN_LEV_FIELD(x, y))
9325 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9326 group_nr = AmoebaNr[x][y];
9332 void AmoebenVereinigen(int ax, int ay)
9334 int i, x, y, xx, yy;
9335 int new_group_nr = AmoebaNr[ax][ay];
9336 static int xy[4][2] =
9344 if (new_group_nr == 0)
9347 for (i = 0; i < NUM_DIRECTIONS; i++)
9352 if (!IN_LEV_FIELD(x, y))
9355 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9356 Feld[x][y] == EL_BD_AMOEBA ||
9357 Feld[x][y] == EL_AMOEBA_DEAD) &&
9358 AmoebaNr[x][y] != new_group_nr)
9360 int old_group_nr = AmoebaNr[x][y];
9362 if (old_group_nr == 0)
9365 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9366 AmoebaCnt[old_group_nr] = 0;
9367 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9368 AmoebaCnt2[old_group_nr] = 0;
9370 SCAN_PLAYFIELD(xx, yy)
9372 if (AmoebaNr[xx][yy] == old_group_nr)
9373 AmoebaNr[xx][yy] = new_group_nr;
9379 void AmoebeUmwandeln(int ax, int ay)
9383 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9385 int group_nr = AmoebaNr[ax][ay];
9390 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9391 printf("AmoebeUmwandeln(): This should never happen!\n");
9396 SCAN_PLAYFIELD(x, y)
9398 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9401 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9405 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9406 SND_AMOEBA_TURNING_TO_GEM :
9407 SND_AMOEBA_TURNING_TO_ROCK));
9412 static int xy[4][2] =
9420 for (i = 0; i < NUM_DIRECTIONS; i++)
9425 if (!IN_LEV_FIELD(x, y))
9428 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9430 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9431 SND_AMOEBA_TURNING_TO_GEM :
9432 SND_AMOEBA_TURNING_TO_ROCK));
9439 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9442 int group_nr = AmoebaNr[ax][ay];
9443 boolean done = FALSE;
9448 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9449 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9454 SCAN_PLAYFIELD(x, y)
9456 if (AmoebaNr[x][y] == group_nr &&
9457 (Feld[x][y] == EL_AMOEBA_DEAD ||
9458 Feld[x][y] == EL_BD_AMOEBA ||
9459 Feld[x][y] == EL_AMOEBA_GROWING))
9462 Feld[x][y] = new_element;
9463 InitField(x, y, FALSE);
9464 TEST_DrawLevelField(x, y);
9470 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9471 SND_BD_AMOEBA_TURNING_TO_ROCK :
9472 SND_BD_AMOEBA_TURNING_TO_GEM));
9475 void AmoebeWaechst(int x, int y)
9477 static unsigned int sound_delay = 0;
9478 static unsigned int sound_delay_value = 0;
9480 if (!MovDelay[x][y]) /* start new growing cycle */
9484 if (DelayReached(&sound_delay, sound_delay_value))
9486 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9487 sound_delay_value = 30;
9491 if (MovDelay[x][y]) /* wait some time before growing bigger */
9494 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9496 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9497 6 - MovDelay[x][y]);
9499 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9502 if (!MovDelay[x][y])
9504 Feld[x][y] = Store[x][y];
9506 TEST_DrawLevelField(x, y);
9511 void AmoebaDisappearing(int x, int y)
9513 static unsigned int sound_delay = 0;
9514 static unsigned int sound_delay_value = 0;
9516 if (!MovDelay[x][y]) /* start new shrinking cycle */
9520 if (DelayReached(&sound_delay, sound_delay_value))
9521 sound_delay_value = 30;
9524 if (MovDelay[x][y]) /* wait some time before shrinking */
9527 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9529 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9530 6 - MovDelay[x][y]);
9532 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9535 if (!MovDelay[x][y])
9537 Feld[x][y] = EL_EMPTY;
9538 TEST_DrawLevelField(x, y);
9540 /* don't let mole enter this field in this cycle;
9541 (give priority to objects falling to this field from above) */
9547 void AmoebeAbleger(int ax, int ay)
9550 int element = Feld[ax][ay];
9551 int graphic = el2img(element);
9552 int newax = ax, neway = ay;
9553 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9554 static int xy[4][2] =
9562 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9564 Feld[ax][ay] = EL_AMOEBA_DEAD;
9565 TEST_DrawLevelField(ax, ay);
9569 if (IS_ANIMATED(graphic))
9570 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9572 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9573 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9575 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9578 if (MovDelay[ax][ay])
9582 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9585 int x = ax + xy[start][0];
9586 int y = ay + xy[start][1];
9588 if (!IN_LEV_FIELD(x, y))
9591 if (IS_FREE(x, y) ||
9592 CAN_GROW_INTO(Feld[x][y]) ||
9593 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9594 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9600 if (newax == ax && neway == ay)
9603 else /* normal or "filled" (BD style) amoeba */
9606 boolean waiting_for_player = FALSE;
9608 for (i = 0; i < NUM_DIRECTIONS; i++)
9610 int j = (start + i) % 4;
9611 int x = ax + xy[j][0];
9612 int y = ay + xy[j][1];
9614 if (!IN_LEV_FIELD(x, y))
9617 if (IS_FREE(x, y) ||
9618 CAN_GROW_INTO(Feld[x][y]) ||
9619 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9620 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9626 else if (IS_PLAYER(x, y))
9627 waiting_for_player = TRUE;
9630 if (newax == ax && neway == ay) /* amoeba cannot grow */
9632 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9634 Feld[ax][ay] = EL_AMOEBA_DEAD;
9635 TEST_DrawLevelField(ax, ay);
9636 AmoebaCnt[AmoebaNr[ax][ay]]--;
9638 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9640 if (element == EL_AMOEBA_FULL)
9641 AmoebeUmwandeln(ax, ay);
9642 else if (element == EL_BD_AMOEBA)
9643 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9648 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9650 /* amoeba gets larger by growing in some direction */
9652 int new_group_nr = AmoebaNr[ax][ay];
9655 if (new_group_nr == 0)
9657 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9658 printf("AmoebeAbleger(): This should never happen!\n");
9663 AmoebaNr[newax][neway] = new_group_nr;
9664 AmoebaCnt[new_group_nr]++;
9665 AmoebaCnt2[new_group_nr]++;
9667 /* if amoeba touches other amoeba(s) after growing, unify them */
9668 AmoebenVereinigen(newax, neway);
9670 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9672 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9678 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9679 (neway == lev_fieldy - 1 && newax != ax))
9681 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9682 Store[newax][neway] = element;
9684 else if (neway == ay || element == EL_EMC_DRIPPER)
9686 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9688 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9692 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9693 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9694 Store[ax][ay] = EL_AMOEBA_DROP;
9695 ContinueMoving(ax, ay);
9699 TEST_DrawLevelField(newax, neway);
9702 void Life(int ax, int ay)
9706 int element = Feld[ax][ay];
9707 int graphic = el2img(element);
9708 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9710 boolean changed = FALSE;
9712 if (IS_ANIMATED(graphic))
9713 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9718 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9719 MovDelay[ax][ay] = life_time;
9721 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9724 if (MovDelay[ax][ay])
9728 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9730 int xx = ax+x1, yy = ay+y1;
9733 if (!IN_LEV_FIELD(xx, yy))
9736 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9738 int x = xx+x2, y = yy+y2;
9740 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9743 if (((Feld[x][y] == element ||
9744 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9746 (IS_FREE(x, y) && Stop[x][y]))
9750 if (xx == ax && yy == ay) /* field in the middle */
9752 if (nachbarn < life_parameter[0] ||
9753 nachbarn > life_parameter[1])
9755 Feld[xx][yy] = EL_EMPTY;
9757 TEST_DrawLevelField(xx, yy);
9758 Stop[xx][yy] = TRUE;
9762 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9763 { /* free border field */
9764 if (nachbarn >= life_parameter[2] &&
9765 nachbarn <= life_parameter[3])
9767 Feld[xx][yy] = element;
9768 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9770 TEST_DrawLevelField(xx, yy);
9771 Stop[xx][yy] = TRUE;
9778 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9779 SND_GAME_OF_LIFE_GROWING);
9782 static void InitRobotWheel(int x, int y)
9784 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9787 static void RunRobotWheel(int x, int y)
9789 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9792 static void StopRobotWheel(int x, int y)
9794 if (ZX == x && ZY == y)
9798 game.robot_wheel_active = FALSE;
9802 static void InitTimegateWheel(int x, int y)
9804 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9807 static void RunTimegateWheel(int x, int y)
9809 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9812 static void InitMagicBallDelay(int x, int y)
9815 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9817 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9821 static void ActivateMagicBall(int bx, int by)
9825 if (level.ball_random)
9827 int pos_border = RND(8); /* select one of the eight border elements */
9828 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9829 int xx = pos_content % 3;
9830 int yy = pos_content / 3;
9835 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9836 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9840 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9842 int xx = x - bx + 1;
9843 int yy = y - by + 1;
9845 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9846 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9850 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9853 void CheckExit(int x, int y)
9855 if (local_player->gems_still_needed > 0 ||
9856 local_player->sokobanfields_still_needed > 0 ||
9857 local_player->lights_still_needed > 0)
9859 int element = Feld[x][y];
9860 int graphic = el2img(element);
9862 if (IS_ANIMATED(graphic))
9863 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9868 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9871 Feld[x][y] = EL_EXIT_OPENING;
9873 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9876 void CheckExitEM(int x, int y)
9878 if (local_player->gems_still_needed > 0 ||
9879 local_player->sokobanfields_still_needed > 0 ||
9880 local_player->lights_still_needed > 0)
9882 int element = Feld[x][y];
9883 int graphic = el2img(element);
9885 if (IS_ANIMATED(graphic))
9886 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9891 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9894 Feld[x][y] = EL_EM_EXIT_OPENING;
9896 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9899 void CheckExitSteel(int x, int y)
9901 if (local_player->gems_still_needed > 0 ||
9902 local_player->sokobanfields_still_needed > 0 ||
9903 local_player->lights_still_needed > 0)
9905 int element = Feld[x][y];
9906 int graphic = el2img(element);
9908 if (IS_ANIMATED(graphic))
9909 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9914 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9917 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9919 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9922 void CheckExitSteelEM(int x, int y)
9924 if (local_player->gems_still_needed > 0 ||
9925 local_player->sokobanfields_still_needed > 0 ||
9926 local_player->lights_still_needed > 0)
9928 int element = Feld[x][y];
9929 int graphic = el2img(element);
9931 if (IS_ANIMATED(graphic))
9932 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9937 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9940 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9942 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9945 void CheckExitSP(int x, int y)
9947 if (local_player->gems_still_needed > 0)
9949 int element = Feld[x][y];
9950 int graphic = el2img(element);
9952 if (IS_ANIMATED(graphic))
9953 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9958 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9961 Feld[x][y] = EL_SP_EXIT_OPENING;
9963 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9966 static void CloseAllOpenTimegates()
9970 SCAN_PLAYFIELD(x, y)
9972 int element = Feld[x][y];
9974 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9976 Feld[x][y] = EL_TIMEGATE_CLOSING;
9978 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9983 void DrawTwinkleOnField(int x, int y)
9985 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9988 if (Feld[x][y] == EL_BD_DIAMOND)
9991 if (MovDelay[x][y] == 0) /* next animation frame */
9992 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9994 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9998 DrawLevelElementAnimation(x, y, Feld[x][y]);
10000 if (MovDelay[x][y] != 0)
10002 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10003 10 - MovDelay[x][y]);
10005 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10010 void MauerWaechst(int x, int y)
10014 if (!MovDelay[x][y]) /* next animation frame */
10015 MovDelay[x][y] = 3 * delay;
10017 if (MovDelay[x][y]) /* wait some time before next frame */
10021 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10023 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10024 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10026 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10029 if (!MovDelay[x][y])
10031 if (MovDir[x][y] == MV_LEFT)
10033 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10034 TEST_DrawLevelField(x - 1, y);
10036 else if (MovDir[x][y] == MV_RIGHT)
10038 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10039 TEST_DrawLevelField(x + 1, y);
10041 else if (MovDir[x][y] == MV_UP)
10043 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10044 TEST_DrawLevelField(x, y - 1);
10048 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10049 TEST_DrawLevelField(x, y + 1);
10052 Feld[x][y] = Store[x][y];
10054 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10055 TEST_DrawLevelField(x, y);
10060 void MauerAbleger(int ax, int ay)
10062 int element = Feld[ax][ay];
10063 int graphic = el2img(element);
10064 boolean oben_frei = FALSE, unten_frei = FALSE;
10065 boolean links_frei = FALSE, rechts_frei = FALSE;
10066 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10067 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10068 boolean new_wall = FALSE;
10070 if (IS_ANIMATED(graphic))
10071 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10073 if (!MovDelay[ax][ay]) /* start building new wall */
10074 MovDelay[ax][ay] = 6;
10076 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10078 MovDelay[ax][ay]--;
10079 if (MovDelay[ax][ay])
10083 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10085 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10087 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10089 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10090 rechts_frei = TRUE;
10092 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10093 element == EL_EXPANDABLE_WALL_ANY)
10097 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10098 Store[ax][ay-1] = element;
10099 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10100 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10101 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10102 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10107 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10108 Store[ax][ay+1] = element;
10109 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10110 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10111 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10112 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10117 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10118 element == EL_EXPANDABLE_WALL_ANY ||
10119 element == EL_EXPANDABLE_WALL ||
10120 element == EL_BD_EXPANDABLE_WALL)
10124 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10125 Store[ax-1][ay] = element;
10126 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10127 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10128 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10129 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10135 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10136 Store[ax+1][ay] = element;
10137 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10138 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10139 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10140 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10145 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10146 TEST_DrawLevelField(ax, ay);
10148 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10149 oben_massiv = TRUE;
10150 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10151 unten_massiv = TRUE;
10152 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10153 links_massiv = TRUE;
10154 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10155 rechts_massiv = TRUE;
10157 if (((oben_massiv && unten_massiv) ||
10158 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10159 element == EL_EXPANDABLE_WALL) &&
10160 ((links_massiv && rechts_massiv) ||
10161 element == EL_EXPANDABLE_WALL_VERTICAL))
10162 Feld[ax][ay] = EL_WALL;
10165 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10168 void MauerAblegerStahl(int ax, int ay)
10170 int element = Feld[ax][ay];
10171 int graphic = el2img(element);
10172 boolean oben_frei = FALSE, unten_frei = FALSE;
10173 boolean links_frei = FALSE, rechts_frei = FALSE;
10174 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10175 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10176 boolean new_wall = FALSE;
10178 if (IS_ANIMATED(graphic))
10179 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10181 if (!MovDelay[ax][ay]) /* start building new wall */
10182 MovDelay[ax][ay] = 6;
10184 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10186 MovDelay[ax][ay]--;
10187 if (MovDelay[ax][ay])
10191 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10193 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10195 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10197 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10198 rechts_frei = TRUE;
10200 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10201 element == EL_EXPANDABLE_STEELWALL_ANY)
10205 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10206 Store[ax][ay-1] = element;
10207 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10208 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10209 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10210 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10215 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10216 Store[ax][ay+1] = element;
10217 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10218 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10219 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10220 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10225 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10226 element == EL_EXPANDABLE_STEELWALL_ANY)
10230 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10231 Store[ax-1][ay] = element;
10232 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10233 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10234 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10235 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10241 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10242 Store[ax+1][ay] = element;
10243 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10244 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10245 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10246 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10251 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10252 oben_massiv = TRUE;
10253 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10254 unten_massiv = TRUE;
10255 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10256 links_massiv = TRUE;
10257 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10258 rechts_massiv = TRUE;
10260 if (((oben_massiv && unten_massiv) ||
10261 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10262 ((links_massiv && rechts_massiv) ||
10263 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10264 Feld[ax][ay] = EL_STEELWALL;
10267 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10270 void CheckForDragon(int x, int y)
10273 boolean dragon_found = FALSE;
10274 static int xy[4][2] =
10282 for (i = 0; i < NUM_DIRECTIONS; i++)
10284 for (j = 0; j < 4; j++)
10286 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10288 if (IN_LEV_FIELD(xx, yy) &&
10289 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10291 if (Feld[xx][yy] == EL_DRAGON)
10292 dragon_found = TRUE;
10301 for (i = 0; i < NUM_DIRECTIONS; i++)
10303 for (j = 0; j < 3; j++)
10305 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10307 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10309 Feld[xx][yy] = EL_EMPTY;
10310 TEST_DrawLevelField(xx, yy);
10319 static void InitBuggyBase(int x, int y)
10321 int element = Feld[x][y];
10322 int activating_delay = FRAMES_PER_SECOND / 4;
10324 ChangeDelay[x][y] =
10325 (element == EL_SP_BUGGY_BASE ?
10326 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10327 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10329 element == EL_SP_BUGGY_BASE_ACTIVE ?
10330 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10333 static void WarnBuggyBase(int x, int y)
10336 static int xy[4][2] =
10344 for (i = 0; i < NUM_DIRECTIONS; i++)
10346 int xx = x + xy[i][0];
10347 int yy = y + xy[i][1];
10349 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10351 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10358 static void InitTrap(int x, int y)
10360 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10363 static void ActivateTrap(int x, int y)
10365 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10368 static void ChangeActiveTrap(int x, int y)
10370 int graphic = IMG_TRAP_ACTIVE;
10372 /* if new animation frame was drawn, correct crumbled sand border */
10373 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10374 TEST_DrawLevelFieldCrumbled(x, y);
10377 static int getSpecialActionElement(int element, int number, int base_element)
10379 return (element != EL_EMPTY ? element :
10380 number != -1 ? base_element + number - 1 :
10384 static int getModifiedActionNumber(int value_old, int operator, int operand,
10385 int value_min, int value_max)
10387 int value_new = (operator == CA_MODE_SET ? operand :
10388 operator == CA_MODE_ADD ? value_old + operand :
10389 operator == CA_MODE_SUBTRACT ? value_old - operand :
10390 operator == CA_MODE_MULTIPLY ? value_old * operand :
10391 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10392 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10395 return (value_new < value_min ? value_min :
10396 value_new > value_max ? value_max :
10400 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10402 struct ElementInfo *ei = &element_info[element];
10403 struct ElementChangeInfo *change = &ei->change_page[page];
10404 int target_element = change->target_element;
10405 int action_type = change->action_type;
10406 int action_mode = change->action_mode;
10407 int action_arg = change->action_arg;
10408 int action_element = change->action_element;
10411 if (!change->has_action)
10414 /* ---------- determine action paramater values -------------------------- */
10416 int level_time_value =
10417 (level.time > 0 ? TimeLeft :
10420 int action_arg_element_raw =
10421 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10422 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10423 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10424 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10425 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10426 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10427 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10429 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10432 if (action_arg_element_raw == EL_GROUP_START)
10433 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10436 int action_arg_direction =
10437 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10438 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10439 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10440 change->actual_trigger_side :
10441 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10442 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10445 int action_arg_number_min =
10446 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10449 int action_arg_number_max =
10450 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10451 action_type == CA_SET_LEVEL_GEMS ? 999 :
10452 action_type == CA_SET_LEVEL_TIME ? 9999 :
10453 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10454 action_type == CA_SET_CE_VALUE ? 9999 :
10455 action_type == CA_SET_CE_SCORE ? 9999 :
10458 int action_arg_number_reset =
10459 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10460 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10461 action_type == CA_SET_LEVEL_TIME ? level.time :
10462 action_type == CA_SET_LEVEL_SCORE ? 0 :
10463 #if USE_NEW_CUSTOM_VALUE
10464 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10466 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10468 action_type == CA_SET_CE_SCORE ? 0 :
10471 int action_arg_number =
10472 (action_arg <= CA_ARG_MAX ? action_arg :
10473 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10474 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10475 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10476 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10477 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10478 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10479 #if USE_NEW_CUSTOM_VALUE
10480 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10482 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10484 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10485 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10486 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10487 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10488 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10489 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10490 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10491 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10492 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10493 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10494 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10495 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10496 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10497 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10500 int action_arg_number_old =
10501 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10502 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10503 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10504 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10505 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10508 int action_arg_number_new =
10509 getModifiedActionNumber(action_arg_number_old,
10510 action_mode, action_arg_number,
10511 action_arg_number_min, action_arg_number_max);
10514 int trigger_player_bits =
10515 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10516 change->actual_trigger_player_bits : change->trigger_player);
10518 int trigger_player_bits =
10519 (change->actual_trigger_player >= EL_PLAYER_1 &&
10520 change->actual_trigger_player <= EL_PLAYER_4 ?
10521 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10525 int action_arg_player_bits =
10526 (action_arg >= CA_ARG_PLAYER_1 &&
10527 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10528 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10529 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10532 /* ---------- execute action -------------------------------------------- */
10534 switch (action_type)
10541 /* ---------- level actions ------------------------------------------- */
10543 case CA_RESTART_LEVEL:
10545 game.restart_level = TRUE;
10550 case CA_SHOW_ENVELOPE:
10552 int element = getSpecialActionElement(action_arg_element,
10553 action_arg_number, EL_ENVELOPE_1);
10555 if (IS_ENVELOPE(element))
10556 local_player->show_envelope = element;
10561 case CA_SET_LEVEL_TIME:
10563 if (level.time > 0) /* only modify limited time value */
10565 TimeLeft = action_arg_number_new;
10568 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10570 DisplayGameControlValues();
10572 DrawGameValue_Time(TimeLeft);
10575 if (!TimeLeft && setup.time_limit)
10576 for (i = 0; i < MAX_PLAYERS; i++)
10577 KillPlayer(&stored_player[i]);
10583 case CA_SET_LEVEL_SCORE:
10585 local_player->score = action_arg_number_new;
10588 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10590 DisplayGameControlValues();
10592 DrawGameValue_Score(local_player->score);
10598 case CA_SET_LEVEL_GEMS:
10600 local_player->gems_still_needed = action_arg_number_new;
10603 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10605 DisplayGameControlValues();
10607 DrawGameValue_Emeralds(local_player->gems_still_needed);
10613 #if !USE_PLAYER_GRAVITY
10614 case CA_SET_LEVEL_GRAVITY:
10616 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10617 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10618 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10624 case CA_SET_LEVEL_WIND:
10626 game.wind_direction = action_arg_direction;
10631 case CA_SET_LEVEL_RANDOM_SEED:
10634 /* ensure that setting a new random seed while playing is predictable */
10635 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10637 InitRND(action_arg_number_new);
10641 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10649 for (i = 0; i < 9; i++)
10650 printf("%d, ", RND(2));
10658 /* ---------- player actions ------------------------------------------ */
10660 case CA_MOVE_PLAYER:
10662 /* automatically move to the next field in specified direction */
10663 for (i = 0; i < MAX_PLAYERS; i++)
10664 if (trigger_player_bits & (1 << i))
10665 stored_player[i].programmed_action = action_arg_direction;
10670 case CA_EXIT_PLAYER:
10672 for (i = 0; i < MAX_PLAYERS; i++)
10673 if (action_arg_player_bits & (1 << i))
10674 PlayerWins(&stored_player[i]);
10679 case CA_KILL_PLAYER:
10681 for (i = 0; i < MAX_PLAYERS; i++)
10682 if (action_arg_player_bits & (1 << i))
10683 KillPlayer(&stored_player[i]);
10688 case CA_SET_PLAYER_KEYS:
10690 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10691 int element = getSpecialActionElement(action_arg_element,
10692 action_arg_number, EL_KEY_1);
10694 if (IS_KEY(element))
10696 for (i = 0; i < MAX_PLAYERS; i++)
10698 if (trigger_player_bits & (1 << i))
10700 stored_player[i].key[KEY_NR(element)] = key_state;
10702 DrawGameDoorValues();
10710 case CA_SET_PLAYER_SPEED:
10713 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10716 for (i = 0; i < MAX_PLAYERS; i++)
10718 if (trigger_player_bits & (1 << i))
10720 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10722 if (action_arg == CA_ARG_SPEED_FASTER &&
10723 stored_player[i].cannot_move)
10725 action_arg_number = STEPSIZE_VERY_SLOW;
10727 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10728 action_arg == CA_ARG_SPEED_FASTER)
10730 action_arg_number = 2;
10731 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10734 else if (action_arg == CA_ARG_NUMBER_RESET)
10736 action_arg_number = level.initial_player_stepsize[i];
10740 getModifiedActionNumber(move_stepsize,
10743 action_arg_number_min,
10744 action_arg_number_max);
10746 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10753 case CA_SET_PLAYER_SHIELD:
10755 for (i = 0; i < MAX_PLAYERS; i++)
10757 if (trigger_player_bits & (1 << i))
10759 if (action_arg == CA_ARG_SHIELD_OFF)
10761 stored_player[i].shield_normal_time_left = 0;
10762 stored_player[i].shield_deadly_time_left = 0;
10764 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10766 stored_player[i].shield_normal_time_left = 999999;
10768 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10770 stored_player[i].shield_normal_time_left = 999999;
10771 stored_player[i].shield_deadly_time_left = 999999;
10779 #if USE_PLAYER_GRAVITY
10780 case CA_SET_PLAYER_GRAVITY:
10782 for (i = 0; i < MAX_PLAYERS; i++)
10784 if (trigger_player_bits & (1 << i))
10786 stored_player[i].gravity =
10787 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10788 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10789 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10790 stored_player[i].gravity);
10798 case CA_SET_PLAYER_ARTWORK:
10800 for (i = 0; i < MAX_PLAYERS; i++)
10802 if (trigger_player_bits & (1 << i))
10804 int artwork_element = action_arg_element;
10806 if (action_arg == CA_ARG_ELEMENT_RESET)
10808 (level.use_artwork_element[i] ? level.artwork_element[i] :
10809 stored_player[i].element_nr);
10811 #if USE_GFX_RESET_PLAYER_ARTWORK
10812 if (stored_player[i].artwork_element != artwork_element)
10813 stored_player[i].Frame = 0;
10816 stored_player[i].artwork_element = artwork_element;
10818 SetPlayerWaiting(&stored_player[i], FALSE);
10820 /* set number of special actions for bored and sleeping animation */
10821 stored_player[i].num_special_action_bored =
10822 get_num_special_action(artwork_element,
10823 ACTION_BORING_1, ACTION_BORING_LAST);
10824 stored_player[i].num_special_action_sleeping =
10825 get_num_special_action(artwork_element,
10826 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10833 case CA_SET_PLAYER_INVENTORY:
10835 for (i = 0; i < MAX_PLAYERS; i++)
10837 struct PlayerInfo *player = &stored_player[i];
10840 if (trigger_player_bits & (1 << i))
10842 int inventory_element = action_arg_element;
10844 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10845 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10846 action_arg == CA_ARG_ELEMENT_ACTION)
10848 int element = inventory_element;
10849 int collect_count = element_info[element].collect_count_initial;
10851 if (!IS_CUSTOM_ELEMENT(element))
10854 if (collect_count == 0)
10855 player->inventory_infinite_element = element;
10857 for (k = 0; k < collect_count; k++)
10858 if (player->inventory_size < MAX_INVENTORY_SIZE)
10859 player->inventory_element[player->inventory_size++] =
10862 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10863 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10864 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10866 if (player->inventory_infinite_element != EL_UNDEFINED &&
10867 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10868 action_arg_element_raw))
10869 player->inventory_infinite_element = EL_UNDEFINED;
10871 for (k = 0, j = 0; j < player->inventory_size; j++)
10873 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10874 action_arg_element_raw))
10875 player->inventory_element[k++] = player->inventory_element[j];
10878 player->inventory_size = k;
10880 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10882 if (player->inventory_size > 0)
10884 for (j = 0; j < player->inventory_size - 1; j++)
10885 player->inventory_element[j] = player->inventory_element[j + 1];
10887 player->inventory_size--;
10890 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10892 if (player->inventory_size > 0)
10893 player->inventory_size--;
10895 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10897 player->inventory_infinite_element = EL_UNDEFINED;
10898 player->inventory_size = 0;
10900 else if (action_arg == CA_ARG_INVENTORY_RESET)
10902 player->inventory_infinite_element = EL_UNDEFINED;
10903 player->inventory_size = 0;
10905 if (level.use_initial_inventory[i])
10907 for (j = 0; j < level.initial_inventory_size[i]; j++)
10909 int element = level.initial_inventory_content[i][j];
10910 int collect_count = element_info[element].collect_count_initial;
10912 if (!IS_CUSTOM_ELEMENT(element))
10915 if (collect_count == 0)
10916 player->inventory_infinite_element = element;
10918 for (k = 0; k < collect_count; k++)
10919 if (player->inventory_size < MAX_INVENTORY_SIZE)
10920 player->inventory_element[player->inventory_size++] =
10931 /* ---------- CE actions ---------------------------------------------- */
10933 case CA_SET_CE_VALUE:
10935 #if USE_NEW_CUSTOM_VALUE
10936 int last_ce_value = CustomValue[x][y];
10938 CustomValue[x][y] = action_arg_number_new;
10940 if (CustomValue[x][y] != last_ce_value)
10942 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10943 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10945 if (CustomValue[x][y] == 0)
10947 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10948 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10956 case CA_SET_CE_SCORE:
10958 #if USE_NEW_CUSTOM_VALUE
10959 int last_ce_score = ei->collect_score;
10961 ei->collect_score = action_arg_number_new;
10963 if (ei->collect_score != last_ce_score)
10965 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10966 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10968 if (ei->collect_score == 0)
10972 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10973 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10976 This is a very special case that seems to be a mixture between
10977 CheckElementChange() and CheckTriggeredElementChange(): while
10978 the first one only affects single elements that are triggered
10979 directly, the second one affects multiple elements in the playfield
10980 that are triggered indirectly by another element. This is a third
10981 case: Changing the CE score always affects multiple identical CEs,
10982 so every affected CE must be checked, not only the single CE for
10983 which the CE score was changed in the first place (as every instance
10984 of that CE shares the same CE score, and therefore also can change)!
10986 SCAN_PLAYFIELD(xx, yy)
10988 if (Feld[xx][yy] == element)
10989 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10990 CE_SCORE_GETS_ZERO);
10999 case CA_SET_CE_ARTWORK:
11001 int artwork_element = action_arg_element;
11002 boolean reset_frame = FALSE;
11005 if (action_arg == CA_ARG_ELEMENT_RESET)
11006 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11009 if (ei->gfx_element != artwork_element)
11010 reset_frame = TRUE;
11012 ei->gfx_element = artwork_element;
11014 SCAN_PLAYFIELD(xx, yy)
11016 if (Feld[xx][yy] == element)
11020 ResetGfxAnimation(xx, yy);
11021 ResetRandomAnimationValue(xx, yy);
11024 TEST_DrawLevelField(xx, yy);
11031 /* ---------- engine actions ------------------------------------------ */
11033 case CA_SET_ENGINE_SCAN_MODE:
11035 InitPlayfieldScanMode(action_arg);
11045 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11047 int old_element = Feld[x][y];
11048 int new_element = GetElementFromGroupElement(element);
11049 int previous_move_direction = MovDir[x][y];
11050 #if USE_NEW_CUSTOM_VALUE
11051 int last_ce_value = CustomValue[x][y];
11053 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11054 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11055 boolean add_player_onto_element = (new_element_is_player &&
11056 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11057 /* this breaks SnakeBite when a snake is
11058 halfway through a door that closes */
11059 /* NOW FIXED AT LEVEL INIT IN files.c */
11060 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11062 IS_WALKABLE(old_element));
11065 /* check if element under the player changes from accessible to unaccessible
11066 (needed for special case of dropping element which then changes) */
11067 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11068 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11076 if (!add_player_onto_element)
11078 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11079 RemoveMovingField(x, y);
11083 Feld[x][y] = new_element;
11085 #if !USE_GFX_RESET_GFX_ANIMATION
11086 ResetGfxAnimation(x, y);
11087 ResetRandomAnimationValue(x, y);
11090 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11091 MovDir[x][y] = previous_move_direction;
11093 #if USE_NEW_CUSTOM_VALUE
11094 if (element_info[new_element].use_last_ce_value)
11095 CustomValue[x][y] = last_ce_value;
11098 InitField_WithBug1(x, y, FALSE);
11100 new_element = Feld[x][y]; /* element may have changed */
11102 #if USE_GFX_RESET_GFX_ANIMATION
11103 ResetGfxAnimation(x, y);
11104 ResetRandomAnimationValue(x, y);
11107 TEST_DrawLevelField(x, y);
11109 if (GFX_CRUMBLED(new_element))
11110 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11114 /* check if element under the player changes from accessible to unaccessible
11115 (needed for special case of dropping element which then changes) */
11116 /* (must be checked after creating new element for walkable group elements) */
11117 #if USE_FIX_KILLED_BY_NON_WALKABLE
11118 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11119 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11126 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11127 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11136 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11137 if (new_element_is_player)
11138 RelocatePlayer(x, y, new_element);
11141 ChangeCount[x][y]++; /* count number of changes in the same frame */
11143 TestIfBadThingTouchesPlayer(x, y);
11144 TestIfPlayerTouchesCustomElement(x, y);
11145 TestIfElementTouchesCustomElement(x, y);
11148 static void CreateField(int x, int y, int element)
11150 CreateFieldExt(x, y, element, FALSE);
11153 static void CreateElementFromChange(int x, int y, int element)
11155 element = GET_VALID_RUNTIME_ELEMENT(element);
11157 #if USE_STOP_CHANGED_ELEMENTS
11158 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11160 int old_element = Feld[x][y];
11162 /* prevent changed element from moving in same engine frame
11163 unless both old and new element can either fall or move */
11164 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11165 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11170 CreateFieldExt(x, y, element, TRUE);
11173 static boolean ChangeElement(int x, int y, int element, int page)
11175 struct ElementInfo *ei = &element_info[element];
11176 struct ElementChangeInfo *change = &ei->change_page[page];
11177 int ce_value = CustomValue[x][y];
11178 int ce_score = ei->collect_score;
11179 int target_element;
11180 int old_element = Feld[x][y];
11182 /* always use default change event to prevent running into a loop */
11183 if (ChangeEvent[x][y] == -1)
11184 ChangeEvent[x][y] = CE_DELAY;
11186 if (ChangeEvent[x][y] == CE_DELAY)
11188 /* reset actual trigger element, trigger player and action element */
11189 change->actual_trigger_element = EL_EMPTY;
11190 change->actual_trigger_player = EL_EMPTY;
11191 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11192 change->actual_trigger_side = CH_SIDE_NONE;
11193 change->actual_trigger_ce_value = 0;
11194 change->actual_trigger_ce_score = 0;
11197 /* do not change elements more than a specified maximum number of changes */
11198 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11201 ChangeCount[x][y]++; /* count number of changes in the same frame */
11203 if (change->explode)
11210 if (change->use_target_content)
11212 boolean complete_replace = TRUE;
11213 boolean can_replace[3][3];
11216 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11219 boolean is_walkable;
11220 boolean is_diggable;
11221 boolean is_collectible;
11222 boolean is_removable;
11223 boolean is_destructible;
11224 int ex = x + xx - 1;
11225 int ey = y + yy - 1;
11226 int content_element = change->target_content.e[xx][yy];
11229 can_replace[xx][yy] = TRUE;
11231 if (ex == x && ey == y) /* do not check changing element itself */
11234 if (content_element == EL_EMPTY_SPACE)
11236 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11241 if (!IN_LEV_FIELD(ex, ey))
11243 can_replace[xx][yy] = FALSE;
11244 complete_replace = FALSE;
11251 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11252 e = MovingOrBlocked2Element(ex, ey);
11254 is_empty = (IS_FREE(ex, ey) ||
11255 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11257 is_walkable = (is_empty || IS_WALKABLE(e));
11258 is_diggable = (is_empty || IS_DIGGABLE(e));
11259 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11260 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11261 is_removable = (is_diggable || is_collectible);
11263 can_replace[xx][yy] =
11264 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11265 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11266 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11267 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11268 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11269 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11270 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11272 if (!can_replace[xx][yy])
11273 complete_replace = FALSE;
11276 if (!change->only_if_complete || complete_replace)
11278 boolean something_has_changed = FALSE;
11280 if (change->only_if_complete && change->use_random_replace &&
11281 RND(100) < change->random_percentage)
11284 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11286 int ex = x + xx - 1;
11287 int ey = y + yy - 1;
11288 int content_element;
11290 if (can_replace[xx][yy] && (!change->use_random_replace ||
11291 RND(100) < change->random_percentage))
11293 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11294 RemoveMovingField(ex, ey);
11296 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11298 content_element = change->target_content.e[xx][yy];
11299 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11300 ce_value, ce_score);
11302 CreateElementFromChange(ex, ey, target_element);
11304 something_has_changed = TRUE;
11306 /* for symmetry reasons, freeze newly created border elements */
11307 if (ex != x || ey != y)
11308 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11312 if (something_has_changed)
11314 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11315 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11321 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11322 ce_value, ce_score);
11324 if (element == EL_DIAGONAL_GROWING ||
11325 element == EL_DIAGONAL_SHRINKING)
11327 target_element = Store[x][y];
11329 Store[x][y] = EL_EMPTY;
11332 CreateElementFromChange(x, y, target_element);
11334 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11335 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11338 /* this uses direct change before indirect change */
11339 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11344 #if USE_NEW_DELAYED_ACTION
11346 static void HandleElementChange(int x, int y, int page)
11348 int element = MovingOrBlocked2Element(x, y);
11349 struct ElementInfo *ei = &element_info[element];
11350 struct ElementChangeInfo *change = &ei->change_page[page];
11351 boolean handle_action_before_change = FALSE;
11354 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11355 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11358 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11359 x, y, element, element_info[element].token_name);
11360 printf("HandleElementChange(): This should never happen!\n");
11365 /* this can happen with classic bombs on walkable, changing elements */
11366 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11369 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11370 ChangeDelay[x][y] = 0;
11376 if (ChangeDelay[x][y] == 0) /* initialize element change */
11378 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11380 if (change->can_change)
11383 /* !!! not clear why graphic animation should be reset at all here !!! */
11384 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11385 #if USE_GFX_RESET_WHEN_NOT_MOVING
11386 /* when a custom element is about to change (for example by change delay),
11387 do not reset graphic animation when the custom element is moving */
11388 if (!IS_MOVING(x, y))
11391 ResetGfxAnimation(x, y);
11392 ResetRandomAnimationValue(x, y);
11396 if (change->pre_change_function)
11397 change->pre_change_function(x, y);
11401 ChangeDelay[x][y]--;
11403 if (ChangeDelay[x][y] != 0) /* continue element change */
11405 if (change->can_change)
11407 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11409 if (IS_ANIMATED(graphic))
11410 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11412 if (change->change_function)
11413 change->change_function(x, y);
11416 else /* finish element change */
11418 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11420 page = ChangePage[x][y];
11421 ChangePage[x][y] = -1;
11423 change = &ei->change_page[page];
11426 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11428 ChangeDelay[x][y] = 1; /* try change after next move step */
11429 ChangePage[x][y] = page; /* remember page to use for change */
11435 /* special case: set new level random seed before changing element */
11436 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11437 handle_action_before_change = TRUE;
11439 if (change->has_action && handle_action_before_change)
11440 ExecuteCustomElementAction(x, y, element, page);
11443 if (change->can_change)
11445 if (ChangeElement(x, y, element, page))
11447 if (change->post_change_function)
11448 change->post_change_function(x, y);
11452 if (change->has_action && !handle_action_before_change)
11453 ExecuteCustomElementAction(x, y, element, page);
11459 static void HandleElementChange(int x, int y, int page)
11461 int element = MovingOrBlocked2Element(x, y);
11462 struct ElementInfo *ei = &element_info[element];
11463 struct ElementChangeInfo *change = &ei->change_page[page];
11466 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11469 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11470 x, y, element, element_info[element].token_name);
11471 printf("HandleElementChange(): This should never happen!\n");
11476 /* this can happen with classic bombs on walkable, changing elements */
11477 if (!CAN_CHANGE(element))
11480 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11481 ChangeDelay[x][y] = 0;
11487 if (ChangeDelay[x][y] == 0) /* initialize element change */
11489 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11491 ResetGfxAnimation(x, y);
11492 ResetRandomAnimationValue(x, y);
11494 if (change->pre_change_function)
11495 change->pre_change_function(x, y);
11498 ChangeDelay[x][y]--;
11500 if (ChangeDelay[x][y] != 0) /* continue element change */
11502 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11504 if (IS_ANIMATED(graphic))
11505 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11507 if (change->change_function)
11508 change->change_function(x, y);
11510 else /* finish element change */
11512 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11514 page = ChangePage[x][y];
11515 ChangePage[x][y] = -1;
11517 change = &ei->change_page[page];
11520 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11522 ChangeDelay[x][y] = 1; /* try change after next move step */
11523 ChangePage[x][y] = page; /* remember page to use for change */
11528 if (ChangeElement(x, y, element, page))
11530 if (change->post_change_function)
11531 change->post_change_function(x, y);
11538 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11539 int trigger_element,
11541 int trigger_player,
11545 boolean change_done_any = FALSE;
11546 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11549 if (!(trigger_events[trigger_element][trigger_event]))
11553 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11554 trigger_event, recursion_loop_depth, recursion_loop_detected,
11555 recursion_loop_element, EL_NAME(recursion_loop_element));
11558 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11560 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11562 int element = EL_CUSTOM_START + i;
11563 boolean change_done = FALSE;
11566 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11567 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11570 for (p = 0; p < element_info[element].num_change_pages; p++)
11572 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11574 if (change->can_change_or_has_action &&
11575 change->has_event[trigger_event] &&
11576 change->trigger_side & trigger_side &&
11577 change->trigger_player & trigger_player &&
11578 change->trigger_page & trigger_page_bits &&
11579 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11581 change->actual_trigger_element = trigger_element;
11582 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11583 change->actual_trigger_player_bits = trigger_player;
11584 change->actual_trigger_side = trigger_side;
11585 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11586 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11589 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11590 element, EL_NAME(element), p);
11593 if ((change->can_change && !change_done) || change->has_action)
11597 SCAN_PLAYFIELD(x, y)
11599 if (Feld[x][y] == element)
11601 if (change->can_change && !change_done)
11603 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11604 /* if element already changed in this frame, not only prevent
11605 another element change (checked in ChangeElement()), but
11606 also prevent additional element actions for this element */
11608 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11609 !level.use_action_after_change_bug)
11614 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11615 element, EL_NAME(element), p);
11618 ChangeDelay[x][y] = 1;
11619 ChangeEvent[x][y] = trigger_event;
11621 HandleElementChange(x, y, p);
11623 #if USE_NEW_DELAYED_ACTION
11624 else if (change->has_action)
11626 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11627 /* if element already changed in this frame, not only prevent
11628 another element change (checked in ChangeElement()), but
11629 also prevent additional element actions for this element */
11631 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11632 !level.use_action_after_change_bug)
11638 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11639 element, EL_NAME(element), p);
11642 ExecuteCustomElementAction(x, y, element, p);
11643 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11646 if (change->has_action)
11648 ExecuteCustomElementAction(x, y, element, p);
11649 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11655 if (change->can_change)
11657 change_done = TRUE;
11658 change_done_any = TRUE;
11661 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11662 element, EL_NAME(element), p);
11671 RECURSION_LOOP_DETECTION_END();
11673 return change_done_any;
11676 static boolean CheckElementChangeExt(int x, int y,
11678 int trigger_element,
11680 int trigger_player,
11683 boolean change_done = FALSE;
11686 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11687 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11690 if (Feld[x][y] == EL_BLOCKED)
11692 Blocked2Moving(x, y, &x, &y);
11693 element = Feld[x][y];
11697 /* check if element has already changed */
11698 if (Feld[x][y] != element)
11701 /* check if element has already changed or is about to change after moving */
11702 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11703 Feld[x][y] != element) ||
11705 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11706 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11707 ChangePage[x][y] != -1)))
11712 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11713 trigger_event, recursion_loop_depth, recursion_loop_detected,
11714 recursion_loop_element, EL_NAME(recursion_loop_element));
11717 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11720 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11723 for (p = 0; p < element_info[element].num_change_pages; p++)
11725 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11727 /* check trigger element for all events where the element that is checked
11728 for changing interacts with a directly adjacent element -- this is
11729 different to element changes that affect other elements to change on the
11730 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11731 boolean check_trigger_element =
11732 (trigger_event == CE_TOUCHING_X ||
11733 trigger_event == CE_HITTING_X ||
11734 trigger_event == CE_HIT_BY_X ||
11736 /* this one was forgotten until 3.2.3 */
11737 trigger_event == CE_DIGGING_X);
11740 if (change->can_change_or_has_action &&
11741 change->has_event[trigger_event] &&
11742 change->trigger_side & trigger_side &&
11743 change->trigger_player & trigger_player &&
11744 (!check_trigger_element ||
11745 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11747 change->actual_trigger_element = trigger_element;
11748 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11749 change->actual_trigger_player_bits = trigger_player;
11750 change->actual_trigger_side = trigger_side;
11751 change->actual_trigger_ce_value = CustomValue[x][y];
11752 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11754 /* special case: trigger element not at (x,y) position for some events */
11755 if (check_trigger_element)
11767 { 0, 0 }, { 0, 0 }, { 0, 0 },
11771 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11772 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11774 change->actual_trigger_ce_value = CustomValue[xx][yy];
11775 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11778 if (change->can_change && !change_done)
11780 ChangeDelay[x][y] = 1;
11781 ChangeEvent[x][y] = trigger_event;
11783 HandleElementChange(x, y, p);
11785 change_done = TRUE;
11787 #if USE_NEW_DELAYED_ACTION
11788 else if (change->has_action)
11790 ExecuteCustomElementAction(x, y, element, p);
11791 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11794 if (change->has_action)
11796 ExecuteCustomElementAction(x, y, element, p);
11797 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11803 RECURSION_LOOP_DETECTION_END();
11805 return change_done;
11808 static void PlayPlayerSound(struct PlayerInfo *player)
11810 int jx = player->jx, jy = player->jy;
11811 int sound_element = player->artwork_element;
11812 int last_action = player->last_action_waiting;
11813 int action = player->action_waiting;
11815 if (player->is_waiting)
11817 if (action != last_action)
11818 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11820 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11824 if (action != last_action)
11825 StopSound(element_info[sound_element].sound[last_action]);
11827 if (last_action == ACTION_SLEEPING)
11828 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11832 static void PlayAllPlayersSound()
11836 for (i = 0; i < MAX_PLAYERS; i++)
11837 if (stored_player[i].active)
11838 PlayPlayerSound(&stored_player[i]);
11841 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11843 boolean last_waiting = player->is_waiting;
11844 int move_dir = player->MovDir;
11846 player->dir_waiting = move_dir;
11847 player->last_action_waiting = player->action_waiting;
11851 if (!last_waiting) /* not waiting -> waiting */
11853 player->is_waiting = TRUE;
11855 player->frame_counter_bored =
11857 game.player_boring_delay_fixed +
11858 GetSimpleRandom(game.player_boring_delay_random);
11859 player->frame_counter_sleeping =
11861 game.player_sleeping_delay_fixed +
11862 GetSimpleRandom(game.player_sleeping_delay_random);
11864 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11867 if (game.player_sleeping_delay_fixed +
11868 game.player_sleeping_delay_random > 0 &&
11869 player->anim_delay_counter == 0 &&
11870 player->post_delay_counter == 0 &&
11871 FrameCounter >= player->frame_counter_sleeping)
11872 player->is_sleeping = TRUE;
11873 else if (game.player_boring_delay_fixed +
11874 game.player_boring_delay_random > 0 &&
11875 FrameCounter >= player->frame_counter_bored)
11876 player->is_bored = TRUE;
11878 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11879 player->is_bored ? ACTION_BORING :
11882 if (player->is_sleeping && player->use_murphy)
11884 /* special case for sleeping Murphy when leaning against non-free tile */
11886 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11887 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11888 !IS_MOVING(player->jx - 1, player->jy)))
11889 move_dir = MV_LEFT;
11890 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11891 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11892 !IS_MOVING(player->jx + 1, player->jy)))
11893 move_dir = MV_RIGHT;
11895 player->is_sleeping = FALSE;
11897 player->dir_waiting = move_dir;
11900 if (player->is_sleeping)
11902 if (player->num_special_action_sleeping > 0)
11904 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11906 int last_special_action = player->special_action_sleeping;
11907 int num_special_action = player->num_special_action_sleeping;
11908 int special_action =
11909 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11910 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11911 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11912 last_special_action + 1 : ACTION_SLEEPING);
11913 int special_graphic =
11914 el_act_dir2img(player->artwork_element, special_action, move_dir);
11916 player->anim_delay_counter =
11917 graphic_info[special_graphic].anim_delay_fixed +
11918 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11919 player->post_delay_counter =
11920 graphic_info[special_graphic].post_delay_fixed +
11921 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11923 player->special_action_sleeping = special_action;
11926 if (player->anim_delay_counter > 0)
11928 player->action_waiting = player->special_action_sleeping;
11929 player->anim_delay_counter--;
11931 else if (player->post_delay_counter > 0)
11933 player->post_delay_counter--;
11937 else if (player->is_bored)
11939 if (player->num_special_action_bored > 0)
11941 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11943 int special_action =
11944 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11945 int special_graphic =
11946 el_act_dir2img(player->artwork_element, special_action, move_dir);
11948 player->anim_delay_counter =
11949 graphic_info[special_graphic].anim_delay_fixed +
11950 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11951 player->post_delay_counter =
11952 graphic_info[special_graphic].post_delay_fixed +
11953 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11955 player->special_action_bored = special_action;
11958 if (player->anim_delay_counter > 0)
11960 player->action_waiting = player->special_action_bored;
11961 player->anim_delay_counter--;
11963 else if (player->post_delay_counter > 0)
11965 player->post_delay_counter--;
11970 else if (last_waiting) /* waiting -> not waiting */
11972 player->is_waiting = FALSE;
11973 player->is_bored = FALSE;
11974 player->is_sleeping = FALSE;
11976 player->frame_counter_bored = -1;
11977 player->frame_counter_sleeping = -1;
11979 player->anim_delay_counter = 0;
11980 player->post_delay_counter = 0;
11982 player->dir_waiting = player->MovDir;
11983 player->action_waiting = ACTION_DEFAULT;
11985 player->special_action_bored = ACTION_DEFAULT;
11986 player->special_action_sleeping = ACTION_DEFAULT;
11990 static void CheckSingleStepMode(struct PlayerInfo *player)
11992 if (tape.single_step && tape.recording && !tape.pausing)
11994 /* as it is called "single step mode", just return to pause mode when the
11995 player stopped moving after one tile (or never starts moving at all) */
11996 if (!player->is_moving && !player->is_pushing)
11998 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11999 SnapField(player, 0, 0); /* stop snapping */
12004 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12006 int left = player_action & JOY_LEFT;
12007 int right = player_action & JOY_RIGHT;
12008 int up = player_action & JOY_UP;
12009 int down = player_action & JOY_DOWN;
12010 int button1 = player_action & JOY_BUTTON_1;
12011 int button2 = player_action & JOY_BUTTON_2;
12012 int dx = (left ? -1 : right ? 1 : 0);
12013 int dy = (up ? -1 : down ? 1 : 0);
12015 if (!player->active || tape.pausing)
12021 SnapField(player, dx, dy);
12025 DropElement(player);
12027 MovePlayer(player, dx, dy);
12030 CheckSingleStepMode(player);
12032 SetPlayerWaiting(player, FALSE);
12034 return player_action;
12038 /* no actions for this player (no input at player's configured device) */
12040 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12041 SnapField(player, 0, 0);
12042 CheckGravityMovementWhenNotMoving(player);
12044 if (player->MovPos == 0)
12045 SetPlayerWaiting(player, TRUE);
12047 if (player->MovPos == 0) /* needed for tape.playing */
12048 player->is_moving = FALSE;
12050 player->is_dropping = FALSE;
12051 player->is_dropping_pressed = FALSE;
12052 player->drop_pressed_delay = 0;
12054 CheckSingleStepMode(player);
12060 static void CheckLevelTime()
12064 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12065 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12067 if (level.native_em_level->lev->home == 0) /* all players at home */
12069 PlayerWins(local_player);
12071 AllPlayersGone = TRUE;
12073 level.native_em_level->lev->home = -1;
12076 if (level.native_em_level->ply[0]->alive == 0 &&
12077 level.native_em_level->ply[1]->alive == 0 &&
12078 level.native_em_level->ply[2]->alive == 0 &&
12079 level.native_em_level->ply[3]->alive == 0) /* all dead */
12080 AllPlayersGone = TRUE;
12082 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12084 if (game_sp.LevelSolved &&
12085 !game_sp.GameOver) /* game won */
12087 PlayerWins(local_player);
12089 game_sp.GameOver = TRUE;
12091 AllPlayersGone = TRUE;
12094 if (game_sp.GameOver) /* game lost */
12095 AllPlayersGone = TRUE;
12098 if (TimeFrames >= FRAMES_PER_SECOND)
12103 for (i = 0; i < MAX_PLAYERS; i++)
12105 struct PlayerInfo *player = &stored_player[i];
12107 if (SHIELD_ON(player))
12109 player->shield_normal_time_left--;
12111 if (player->shield_deadly_time_left > 0)
12112 player->shield_deadly_time_left--;
12116 if (!local_player->LevelSolved && !level.use_step_counter)
12124 if (TimeLeft <= 10 && setup.time_limit)
12125 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12128 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12129 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12131 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12133 /* (already called by UpdateAndDisplayGameControlValues() below) */
12134 // DisplayGameControlValues();
12136 DrawGameValue_Time(TimeLeft);
12139 if (!TimeLeft && setup.time_limit)
12141 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12142 level.native_em_level->lev->killed_out_of_time = TRUE;
12144 for (i = 0; i < MAX_PLAYERS; i++)
12145 KillPlayer(&stored_player[i]);
12149 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12151 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12153 /* (already called by UpdateAndDisplayGameControlValues() below) */
12154 // DisplayGameControlValues();
12157 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12158 DrawGameValue_Time(TimePlayed);
12161 level.native_em_level->lev->time =
12162 (game.no_time_limit ? TimePlayed : TimeLeft);
12165 if (tape.recording || tape.playing)
12166 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12170 UpdateAndDisplayGameControlValues();
12172 UpdateGameDoorValues();
12173 DrawGameDoorValues();
12177 void AdvanceFrameAndPlayerCounters(int player_nr)
12181 /* advance frame counters (global frame counter and time frame counter) */
12185 /* advance player counters (counters for move delay, move animation etc.) */
12186 for (i = 0; i < MAX_PLAYERS; i++)
12188 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12189 int move_delay_value = stored_player[i].move_delay_value;
12190 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12192 if (!advance_player_counters) /* not all players may be affected */
12195 #if USE_NEW_PLAYER_ANIM
12196 if (move_frames == 0) /* less than one move per game frame */
12198 int stepsize = TILEX / move_delay_value;
12199 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12200 int count = (stored_player[i].is_moving ?
12201 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12203 if (count % delay == 0)
12208 stored_player[i].Frame += move_frames;
12210 if (stored_player[i].MovPos != 0)
12211 stored_player[i].StepFrame += move_frames;
12213 if (stored_player[i].move_delay > 0)
12214 stored_player[i].move_delay--;
12216 /* due to bugs in previous versions, counter must count up, not down */
12217 if (stored_player[i].push_delay != -1)
12218 stored_player[i].push_delay++;
12220 if (stored_player[i].drop_delay > 0)
12221 stored_player[i].drop_delay--;
12223 if (stored_player[i].is_dropping_pressed)
12224 stored_player[i].drop_pressed_delay++;
12228 void StartGameActions(boolean init_network_game, boolean record_tape,
12231 unsigned int new_random_seed = InitRND(random_seed);
12234 TapeStartRecording(new_random_seed);
12236 #if defined(NETWORK_AVALIABLE)
12237 if (init_network_game)
12239 SendToServer_StartPlaying();
12250 static unsigned int game_frame_delay = 0;
12251 unsigned int game_frame_delay_value;
12252 byte *recorded_player_action;
12253 byte summarized_player_action = 0;
12254 byte tape_action[MAX_PLAYERS];
12257 /* detect endless loops, caused by custom element programming */
12258 if (recursion_loop_detected && recursion_loop_depth == 0)
12260 char *message = getStringCat3("Internal Error! Element ",
12261 EL_NAME(recursion_loop_element),
12262 " caused endless loop! Quit the game?");
12264 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12265 EL_NAME(recursion_loop_element));
12267 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12269 recursion_loop_detected = FALSE; /* if game should be continued */
12276 if (game.restart_level)
12277 StartGameActions(options.network, setup.autorecord, level.random_seed);
12279 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12280 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12282 if (level.native_em_level->lev->home == 0) /* all players at home */
12284 PlayerWins(local_player);
12286 AllPlayersGone = TRUE;
12288 level.native_em_level->lev->home = -1;
12291 if (level.native_em_level->ply[0]->alive == 0 &&
12292 level.native_em_level->ply[1]->alive == 0 &&
12293 level.native_em_level->ply[2]->alive == 0 &&
12294 level.native_em_level->ply[3]->alive == 0) /* all dead */
12295 AllPlayersGone = TRUE;
12297 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12299 if (game_sp.LevelSolved &&
12300 !game_sp.GameOver) /* game won */
12302 PlayerWins(local_player);
12304 game_sp.GameOver = TRUE;
12306 AllPlayersGone = TRUE;
12309 if (game_sp.GameOver) /* game lost */
12310 AllPlayersGone = TRUE;
12313 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12316 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12319 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12322 game_frame_delay_value =
12323 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12325 if (tape.playing && tape.warp_forward && !tape.pausing)
12326 game_frame_delay_value = 0;
12328 /* ---------- main game synchronization point ---------- */
12330 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12332 if (network_playing && !network_player_action_received)
12334 /* try to get network player actions in time */
12336 #if defined(NETWORK_AVALIABLE)
12337 /* last chance to get network player actions without main loop delay */
12338 HandleNetworking();
12341 /* game was quit by network peer */
12342 if (game_status != GAME_MODE_PLAYING)
12345 if (!network_player_action_received)
12346 return; /* failed to get network player actions in time */
12348 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12354 /* at this point we know that we really continue executing the game */
12356 network_player_action_received = FALSE;
12358 /* when playing tape, read previously recorded player input from tape data */
12359 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12362 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12367 if (tape.set_centered_player)
12369 game.centered_player_nr_next = tape.centered_player_nr_next;
12370 game.set_centered_player = TRUE;
12373 for (i = 0; i < MAX_PLAYERS; i++)
12375 summarized_player_action |= stored_player[i].action;
12378 if (!network_playing && (setup.team_mode || tape.playing))
12379 stored_player[i].effective_action = stored_player[i].action;
12381 if (!network_playing)
12382 stored_player[i].effective_action = stored_player[i].action;
12386 #if defined(NETWORK_AVALIABLE)
12387 if (network_playing)
12388 SendToServer_MovePlayer(summarized_player_action);
12391 if (!options.network && !setup.team_mode)
12392 local_player->effective_action = summarized_player_action;
12394 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12396 for (i = 0; i < MAX_PLAYERS; i++)
12397 stored_player[i].effective_action =
12398 (i == game.centered_player_nr ? summarized_player_action : 0);
12401 if (recorded_player_action != NULL)
12402 for (i = 0; i < MAX_PLAYERS; i++)
12403 stored_player[i].effective_action = recorded_player_action[i];
12405 for (i = 0; i < MAX_PLAYERS; i++)
12407 tape_action[i] = stored_player[i].effective_action;
12410 /* (this can only happen in the R'n'D game engine) */
12411 if (setup.team_mode &&
12414 !tape.player_participates[i])
12415 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12417 /* (this can only happen in the R'n'D game engine) */
12418 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12419 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12423 /* only record actions from input devices, but not programmed actions */
12424 if (tape.recording)
12425 TapeRecordAction(tape_action);
12427 #if USE_NEW_PLAYER_ASSIGNMENTS
12429 // if (setup.team_mode)
12430 if (TEST_game_team_mode)
12433 byte mapped_action[MAX_PLAYERS];
12435 #if DEBUG_PLAYER_ACTIONS
12437 for (i = 0; i < MAX_PLAYERS; i++)
12438 printf(" %d, ", stored_player[i].effective_action);
12441 for (i = 0; i < MAX_PLAYERS; i++)
12442 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12444 for (i = 0; i < MAX_PLAYERS; i++)
12445 stored_player[i].effective_action = mapped_action[i];
12447 #if DEBUG_PLAYER_ACTIONS
12449 for (i = 0; i < MAX_PLAYERS; i++)
12450 printf(" %d, ", stored_player[i].effective_action);
12454 #if DEBUG_PLAYER_ACTIONS
12458 for (i = 0; i < MAX_PLAYERS; i++)
12459 printf(" %d, ", stored_player[i].effective_action);
12466 if (!options.network && !setup.team_mode)
12467 local_player->effective_action = summarized_player_action;
12471 printf("::: summarized_player_action == %d\n",
12472 local_player->effective_action);
12479 #if DEBUG_INIT_PLAYER
12482 printf("Player status (final):\n");
12484 for (i = 0; i < MAX_PLAYERS; i++)
12486 struct PlayerInfo *player = &stored_player[i];
12488 printf("- player %d: present == %d, connected == %d, active == %d",
12494 if (local_player == player)
12495 printf(" (local player)");
12505 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12507 GameActions_EM_Main();
12509 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12511 GameActions_SP_Main();
12519 void GameActions_EM_Main()
12521 byte effective_action[MAX_PLAYERS];
12522 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12525 for (i = 0; i < MAX_PLAYERS; i++)
12526 effective_action[i] = stored_player[i].effective_action;
12528 GameActions_EM(effective_action, warp_mode);
12532 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12535 void GameActions_SP_Main()
12537 byte effective_action[MAX_PLAYERS];
12538 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12541 for (i = 0; i < MAX_PLAYERS; i++)
12542 effective_action[i] = stored_player[i].effective_action;
12544 GameActions_SP(effective_action, warp_mode);
12548 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12551 void GameActions_RND()
12553 int magic_wall_x = 0, magic_wall_y = 0;
12554 int i, x, y, element, graphic;
12556 InitPlayfieldScanModeVars();
12558 #if USE_ONE_MORE_CHANGE_PER_FRAME
12559 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12561 SCAN_PLAYFIELD(x, y)
12563 ChangeCount[x][y] = 0;
12564 ChangeEvent[x][y] = -1;
12569 if (game.set_centered_player)
12571 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12573 /* switching to "all players" only possible if all players fit to screen */
12574 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12576 game.centered_player_nr_next = game.centered_player_nr;
12577 game.set_centered_player = FALSE;
12580 /* do not switch focus to non-existing (or non-active) player */
12581 if (game.centered_player_nr_next >= 0 &&
12582 !stored_player[game.centered_player_nr_next].active)
12584 game.centered_player_nr_next = game.centered_player_nr;
12585 game.set_centered_player = FALSE;
12589 if (game.set_centered_player &&
12590 ScreenMovPos == 0) /* screen currently aligned at tile position */
12594 if (game.centered_player_nr_next == -1)
12596 setScreenCenteredToAllPlayers(&sx, &sy);
12600 sx = stored_player[game.centered_player_nr_next].jx;
12601 sy = stored_player[game.centered_player_nr_next].jy;
12604 game.centered_player_nr = game.centered_player_nr_next;
12605 game.set_centered_player = FALSE;
12607 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12608 DrawGameDoorValues();
12611 for (i = 0; i < MAX_PLAYERS; i++)
12613 int actual_player_action = stored_player[i].effective_action;
12616 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12617 - rnd_equinox_tetrachloride 048
12618 - rnd_equinox_tetrachloride_ii 096
12619 - rnd_emanuel_schmieg 002
12620 - doctor_sloan_ww 001, 020
12622 if (stored_player[i].MovPos == 0)
12623 CheckGravityMovement(&stored_player[i]);
12626 /* overwrite programmed action with tape action */
12627 if (stored_player[i].programmed_action)
12628 actual_player_action = stored_player[i].programmed_action;
12630 PlayerActions(&stored_player[i], actual_player_action);
12632 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12635 ScrollScreen(NULL, SCROLL_GO_ON);
12637 /* for backwards compatibility, the following code emulates a fixed bug that
12638 occured when pushing elements (causing elements that just made their last
12639 pushing step to already (if possible) make their first falling step in the
12640 same game frame, which is bad); this code is also needed to use the famous
12641 "spring push bug" which is used in older levels and might be wanted to be
12642 used also in newer levels, but in this case the buggy pushing code is only
12643 affecting the "spring" element and no other elements */
12645 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12647 for (i = 0; i < MAX_PLAYERS; i++)
12649 struct PlayerInfo *player = &stored_player[i];
12650 int x = player->jx;
12651 int y = player->jy;
12653 if (player->active && player->is_pushing && player->is_moving &&
12655 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12656 Feld[x][y] == EL_SPRING))
12658 ContinueMoving(x, y);
12660 /* continue moving after pushing (this is actually a bug) */
12661 if (!IS_MOVING(x, y))
12662 Stop[x][y] = FALSE;
12668 debug_print_timestamp(0, "start main loop profiling");
12671 SCAN_PLAYFIELD(x, y)
12673 ChangeCount[x][y] = 0;
12674 ChangeEvent[x][y] = -1;
12676 /* this must be handled before main playfield loop */
12677 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12680 if (MovDelay[x][y] <= 0)
12684 #if USE_NEW_SNAP_DELAY
12685 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12688 if (MovDelay[x][y] <= 0)
12691 TEST_DrawLevelField(x, y);
12693 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12699 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12701 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12702 printf("GameActions(): This should never happen!\n");
12704 ChangePage[x][y] = -1;
12708 Stop[x][y] = FALSE;
12709 if (WasJustMoving[x][y] > 0)
12710 WasJustMoving[x][y]--;
12711 if (WasJustFalling[x][y] > 0)
12712 WasJustFalling[x][y]--;
12713 if (CheckCollision[x][y] > 0)
12714 CheckCollision[x][y]--;
12715 if (CheckImpact[x][y] > 0)
12716 CheckImpact[x][y]--;
12720 /* reset finished pushing action (not done in ContinueMoving() to allow
12721 continuous pushing animation for elements with zero push delay) */
12722 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12724 ResetGfxAnimation(x, y);
12725 TEST_DrawLevelField(x, y);
12729 if (IS_BLOCKED(x, y))
12733 Blocked2Moving(x, y, &oldx, &oldy);
12734 if (!IS_MOVING(oldx, oldy))
12736 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12737 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12738 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12739 printf("GameActions(): This should never happen!\n");
12746 debug_print_timestamp(0, "- time for pre-main loop:");
12749 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12750 SCAN_PLAYFIELD(x, y)
12752 element = Feld[x][y];
12753 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12758 int element2 = element;
12759 int graphic2 = graphic;
12761 int element2 = Feld[x][y];
12762 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12764 int last_gfx_frame = GfxFrame[x][y];
12766 if (graphic_info[graphic2].anim_global_sync)
12767 GfxFrame[x][y] = FrameCounter;
12768 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12769 GfxFrame[x][y] = CustomValue[x][y];
12770 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12771 GfxFrame[x][y] = element_info[element2].collect_score;
12772 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12773 GfxFrame[x][y] = ChangeDelay[x][y];
12775 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12776 DrawLevelGraphicAnimation(x, y, graphic2);
12779 ResetGfxFrame(x, y, TRUE);
12783 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12784 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12785 ResetRandomAnimationValue(x, y);
12789 SetRandomAnimationValue(x, y);
12793 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12796 #endif // -------------------- !!! TEST ONLY !!! --------------------
12799 debug_print_timestamp(0, "- time for TEST loop: -->");
12802 SCAN_PLAYFIELD(x, y)
12804 element = Feld[x][y];
12805 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12807 ResetGfxFrame(x, y, TRUE);
12809 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12810 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12811 ResetRandomAnimationValue(x, y);
12813 SetRandomAnimationValue(x, y);
12815 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12817 if (IS_INACTIVE(element))
12819 if (IS_ANIMATED(graphic))
12820 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12825 /* this may take place after moving, so 'element' may have changed */
12826 if (IS_CHANGING(x, y) &&
12827 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12829 int page = element_info[element].event_page_nr[CE_DELAY];
12832 HandleElementChange(x, y, page);
12834 if (CAN_CHANGE(element))
12835 HandleElementChange(x, y, page);
12837 if (HAS_ACTION(element))
12838 ExecuteCustomElementAction(x, y, element, page);
12841 element = Feld[x][y];
12842 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12845 #if 0 // ---------------------------------------------------------------------
12847 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12851 element = Feld[x][y];
12852 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12854 if (IS_ANIMATED(graphic) &&
12855 !IS_MOVING(x, y) &&
12857 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12859 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12860 TEST_DrawTwinkleOnField(x, y);
12862 else if (IS_MOVING(x, y))
12863 ContinueMoving(x, y);
12870 case EL_EM_EXIT_OPEN:
12871 case EL_SP_EXIT_OPEN:
12872 case EL_STEEL_EXIT_OPEN:
12873 case EL_EM_STEEL_EXIT_OPEN:
12874 case EL_SP_TERMINAL:
12875 case EL_SP_TERMINAL_ACTIVE:
12876 case EL_EXTRA_TIME:
12877 case EL_SHIELD_NORMAL:
12878 case EL_SHIELD_DEADLY:
12879 if (IS_ANIMATED(graphic))
12880 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12883 case EL_DYNAMITE_ACTIVE:
12884 case EL_EM_DYNAMITE_ACTIVE:
12885 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12886 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12887 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12888 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12889 case EL_SP_DISK_RED_ACTIVE:
12890 CheckDynamite(x, y);
12893 case EL_AMOEBA_GROWING:
12894 AmoebeWaechst(x, y);
12897 case EL_AMOEBA_SHRINKING:
12898 AmoebaDisappearing(x, y);
12901 #if !USE_NEW_AMOEBA_CODE
12902 case EL_AMOEBA_WET:
12903 case EL_AMOEBA_DRY:
12904 case EL_AMOEBA_FULL:
12906 case EL_EMC_DRIPPER:
12907 AmoebeAbleger(x, y);
12911 case EL_GAME_OF_LIFE:
12916 case EL_EXIT_CLOSED:
12920 case EL_EM_EXIT_CLOSED:
12924 case EL_STEEL_EXIT_CLOSED:
12925 CheckExitSteel(x, y);
12928 case EL_EM_STEEL_EXIT_CLOSED:
12929 CheckExitSteelEM(x, y);
12932 case EL_SP_EXIT_CLOSED:
12936 case EL_EXPANDABLE_WALL_GROWING:
12937 case EL_EXPANDABLE_STEELWALL_GROWING:
12938 MauerWaechst(x, y);
12941 case EL_EXPANDABLE_WALL:
12942 case EL_EXPANDABLE_WALL_HORIZONTAL:
12943 case EL_EXPANDABLE_WALL_VERTICAL:
12944 case EL_EXPANDABLE_WALL_ANY:
12945 case EL_BD_EXPANDABLE_WALL:
12946 MauerAbleger(x, y);
12949 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12950 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12951 case EL_EXPANDABLE_STEELWALL_ANY:
12952 MauerAblegerStahl(x, y);
12956 CheckForDragon(x, y);
12962 case EL_ELEMENT_SNAPPING:
12963 case EL_DIAGONAL_SHRINKING:
12964 case EL_DIAGONAL_GROWING:
12967 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12969 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12974 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12975 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12980 #else // ---------------------------------------------------------------------
12982 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12986 element = Feld[x][y];
12987 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12989 if (IS_ANIMATED(graphic) &&
12990 !IS_MOVING(x, y) &&
12992 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12994 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12995 TEST_DrawTwinkleOnField(x, y);
12997 else if ((element == EL_ACID ||
12998 element == EL_EXIT_OPEN ||
12999 element == EL_EM_EXIT_OPEN ||
13000 element == EL_SP_EXIT_OPEN ||
13001 element == EL_STEEL_EXIT_OPEN ||
13002 element == EL_EM_STEEL_EXIT_OPEN ||
13003 element == EL_SP_TERMINAL ||
13004 element == EL_SP_TERMINAL_ACTIVE ||
13005 element == EL_EXTRA_TIME ||
13006 element == EL_SHIELD_NORMAL ||
13007 element == EL_SHIELD_DEADLY) &&
13008 IS_ANIMATED(graphic))
13009 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13010 else if (IS_MOVING(x, y))
13011 ContinueMoving(x, y);
13012 else if (IS_ACTIVE_BOMB(element))
13013 CheckDynamite(x, y);
13014 else if (element == EL_AMOEBA_GROWING)
13015 AmoebeWaechst(x, y);
13016 else if (element == EL_AMOEBA_SHRINKING)
13017 AmoebaDisappearing(x, y);
13019 #if !USE_NEW_AMOEBA_CODE
13020 else if (IS_AMOEBALIVE(element))
13021 AmoebeAbleger(x, y);
13024 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13026 else if (element == EL_EXIT_CLOSED)
13028 else if (element == EL_EM_EXIT_CLOSED)
13030 else if (element == EL_STEEL_EXIT_CLOSED)
13031 CheckExitSteel(x, y);
13032 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13033 CheckExitSteelEM(x, y);
13034 else if (element == EL_SP_EXIT_CLOSED)
13036 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13037 element == EL_EXPANDABLE_STEELWALL_GROWING)
13038 MauerWaechst(x, y);
13039 else if (element == EL_EXPANDABLE_WALL ||
13040 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13041 element == EL_EXPANDABLE_WALL_VERTICAL ||
13042 element == EL_EXPANDABLE_WALL_ANY ||
13043 element == EL_BD_EXPANDABLE_WALL)
13044 MauerAbleger(x, y);
13045 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13046 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13047 element == EL_EXPANDABLE_STEELWALL_ANY)
13048 MauerAblegerStahl(x, y);
13049 else if (element == EL_FLAMES)
13050 CheckForDragon(x, y);
13051 else if (element == EL_EXPLOSION)
13052 ; /* drawing of correct explosion animation is handled separately */
13053 else if (element == EL_ELEMENT_SNAPPING ||
13054 element == EL_DIAGONAL_SHRINKING ||
13055 element == EL_DIAGONAL_GROWING)
13057 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13059 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13061 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13062 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13064 #endif // ---------------------------------------------------------------------
13066 if (IS_BELT_ACTIVE(element))
13067 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13069 if (game.magic_wall_active)
13071 int jx = local_player->jx, jy = local_player->jy;
13073 /* play the element sound at the position nearest to the player */
13074 if ((element == EL_MAGIC_WALL_FULL ||
13075 element == EL_MAGIC_WALL_ACTIVE ||
13076 element == EL_MAGIC_WALL_EMPTYING ||
13077 element == EL_BD_MAGIC_WALL_FULL ||
13078 element == EL_BD_MAGIC_WALL_ACTIVE ||
13079 element == EL_BD_MAGIC_WALL_EMPTYING ||
13080 element == EL_DC_MAGIC_WALL_FULL ||
13081 element == EL_DC_MAGIC_WALL_ACTIVE ||
13082 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13083 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13092 debug_print_timestamp(0, "- time for MAIN loop: -->");
13095 #if USE_NEW_AMOEBA_CODE
13096 /* new experimental amoeba growth stuff */
13097 if (!(FrameCounter % 8))
13099 static unsigned int random = 1684108901;
13101 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13103 x = RND(lev_fieldx);
13104 y = RND(lev_fieldy);
13105 element = Feld[x][y];
13107 if (!IS_PLAYER(x,y) &&
13108 (element == EL_EMPTY ||
13109 CAN_GROW_INTO(element) ||
13110 element == EL_QUICKSAND_EMPTY ||
13111 element == EL_QUICKSAND_FAST_EMPTY ||
13112 element == EL_ACID_SPLASH_LEFT ||
13113 element == EL_ACID_SPLASH_RIGHT))
13115 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13116 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13117 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13118 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13119 Feld[x][y] = EL_AMOEBA_DROP;
13122 random = random * 129 + 1;
13128 if (game.explosions_delayed)
13131 game.explosions_delayed = FALSE;
13133 SCAN_PLAYFIELD(x, y)
13135 element = Feld[x][y];
13137 if (ExplodeField[x][y])
13138 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13139 else if (element == EL_EXPLOSION)
13140 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13142 ExplodeField[x][y] = EX_TYPE_NONE;
13145 game.explosions_delayed = TRUE;
13148 if (game.magic_wall_active)
13150 if (!(game.magic_wall_time_left % 4))
13152 int element = Feld[magic_wall_x][magic_wall_y];
13154 if (element == EL_BD_MAGIC_WALL_FULL ||
13155 element == EL_BD_MAGIC_WALL_ACTIVE ||
13156 element == EL_BD_MAGIC_WALL_EMPTYING)
13157 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13158 else if (element == EL_DC_MAGIC_WALL_FULL ||
13159 element == EL_DC_MAGIC_WALL_ACTIVE ||
13160 element == EL_DC_MAGIC_WALL_EMPTYING)
13161 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13163 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13166 if (game.magic_wall_time_left > 0)
13168 game.magic_wall_time_left--;
13170 if (!game.magic_wall_time_left)
13172 SCAN_PLAYFIELD(x, y)
13174 element = Feld[x][y];
13176 if (element == EL_MAGIC_WALL_ACTIVE ||
13177 element == EL_MAGIC_WALL_FULL)
13179 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13180 TEST_DrawLevelField(x, y);
13182 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13183 element == EL_BD_MAGIC_WALL_FULL)
13185 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13186 TEST_DrawLevelField(x, y);
13188 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13189 element == EL_DC_MAGIC_WALL_FULL)
13191 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13192 TEST_DrawLevelField(x, y);
13196 game.magic_wall_active = FALSE;
13201 if (game.light_time_left > 0)
13203 game.light_time_left--;
13205 if (game.light_time_left == 0)
13206 RedrawAllLightSwitchesAndInvisibleElements();
13209 if (game.timegate_time_left > 0)
13211 game.timegate_time_left--;
13213 if (game.timegate_time_left == 0)
13214 CloseAllOpenTimegates();
13217 if (game.lenses_time_left > 0)
13219 game.lenses_time_left--;
13221 if (game.lenses_time_left == 0)
13222 RedrawAllInvisibleElementsForLenses();
13225 if (game.magnify_time_left > 0)
13227 game.magnify_time_left--;
13229 if (game.magnify_time_left == 0)
13230 RedrawAllInvisibleElementsForMagnifier();
13233 for (i = 0; i < MAX_PLAYERS; i++)
13235 struct PlayerInfo *player = &stored_player[i];
13237 if (SHIELD_ON(player))
13239 if (player->shield_deadly_time_left)
13240 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13241 else if (player->shield_normal_time_left)
13242 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13246 #if USE_DELAYED_GFX_REDRAW
13247 SCAN_PLAYFIELD(x, y)
13250 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13252 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13253 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13256 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13257 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13259 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13260 DrawLevelField(x, y);
13262 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13263 DrawLevelFieldCrumbled(x, y);
13265 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13266 DrawLevelFieldCrumbledNeighbours(x, y);
13268 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13269 DrawTwinkleOnField(x, y);
13272 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13279 PlayAllPlayersSound();
13281 if (options.debug) /* calculate frames per second */
13283 static unsigned int fps_counter = 0;
13284 static int fps_frames = 0;
13285 unsigned int fps_delay_ms = Counter() - fps_counter;
13289 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13291 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13294 fps_counter = Counter();
13297 redraw_mask |= REDRAW_FPS;
13300 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13302 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13304 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13306 local_player->show_envelope = 0;
13310 debug_print_timestamp(0, "stop main loop profiling ");
13311 printf("----------------------------------------------------------\n");
13314 /* use random number generator in every frame to make it less predictable */
13315 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13319 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13321 int min_x = x, min_y = y, max_x = x, max_y = y;
13324 for (i = 0; i < MAX_PLAYERS; i++)
13326 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13328 if (!stored_player[i].active || &stored_player[i] == player)
13331 min_x = MIN(min_x, jx);
13332 min_y = MIN(min_y, jy);
13333 max_x = MAX(max_x, jx);
13334 max_y = MAX(max_y, jy);
13337 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13340 static boolean AllPlayersInVisibleScreen()
13344 for (i = 0; i < MAX_PLAYERS; i++)
13346 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13348 if (!stored_player[i].active)
13351 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13358 void ScrollLevel(int dx, int dy)
13361 /* (directly solved in BlitBitmap() now) */
13362 static Bitmap *bitmap_db_field2 = NULL;
13363 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13370 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13371 /* only horizontal XOR vertical scroll direction allowed */
13372 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13377 /* (directly solved in BlitBitmap() now) */
13378 if (bitmap_db_field2 == NULL)
13379 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13381 /* needed when blitting directly to same bitmap -- should not be needed with
13382 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13383 BlitBitmap(drawto_field, bitmap_db_field2,
13384 FX + TILEX * (dx == -1) - softscroll_offset,
13385 FY + TILEY * (dy == -1) - softscroll_offset,
13386 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13387 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13388 FX + TILEX * (dx == 1) - softscroll_offset,
13389 FY + TILEY * (dy == 1) - softscroll_offset);
13390 BlitBitmap(bitmap_db_field2, drawto_field,
13391 FX + TILEX * (dx == 1) - softscroll_offset,
13392 FY + TILEY * (dy == 1) - softscroll_offset,
13393 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13394 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13395 FX + TILEX * (dx == 1) - softscroll_offset,
13396 FY + TILEY * (dy == 1) - softscroll_offset);
13401 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13402 int xsize = (BX2 - BX1 + 1);
13403 int ysize = (BY2 - BY1 + 1);
13404 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13405 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13406 int step = (start < end ? +1 : -1);
13408 for (i = start; i != end; i += step)
13410 BlitBitmap(drawto_field, drawto_field,
13411 FX + TILEX * (dx != 0 ? i + step : 0),
13412 FY + TILEY * (dy != 0 ? i + step : 0),
13413 TILEX * (dx != 0 ? 1 : xsize),
13414 TILEY * (dy != 0 ? 1 : ysize),
13415 FX + TILEX * (dx != 0 ? i : 0),
13416 FY + TILEY * (dy != 0 ? i : 0));
13423 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13425 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13429 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13431 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13436 BlitBitmap(drawto_field, drawto_field,
13437 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13438 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13439 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13440 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13441 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13442 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13444 BlitBitmap(drawto_field, drawto_field,
13445 FX + TILEX * (dx == -1) - softscroll_offset,
13446 FY + TILEY * (dy == -1) - softscroll_offset,
13447 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13448 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13449 FX + TILEX * (dx == 1) - softscroll_offset,
13450 FY + TILEY * (dy == 1) - softscroll_offset);
13458 x = (dx == 1 ? BX1 : BX2);
13459 for (y = BY1; y <= BY2; y++)
13460 DrawScreenField(x, y);
13465 y = (dy == 1 ? BY1 : BY2);
13466 for (x = BX1; x <= BX2; x++)
13467 DrawScreenField(x, y);
13470 redraw_mask |= REDRAW_FIELD;
13473 static boolean canFallDown(struct PlayerInfo *player)
13475 int jx = player->jx, jy = player->jy;
13477 return (IN_LEV_FIELD(jx, jy + 1) &&
13478 (IS_FREE(jx, jy + 1) ||
13479 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13480 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13481 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13484 static boolean canPassField(int x, int y, int move_dir)
13486 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13487 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13488 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13489 int nextx = x + dx;
13490 int nexty = y + dy;
13491 int element = Feld[x][y];
13493 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13494 !CAN_MOVE(element) &&
13495 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13496 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13497 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13500 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13502 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13503 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13504 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13508 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13509 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13510 (IS_DIGGABLE(Feld[newx][newy]) ||
13511 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13512 canPassField(newx, newy, move_dir)));
13515 static void CheckGravityMovement(struct PlayerInfo *player)
13517 #if USE_PLAYER_GRAVITY
13518 if (player->gravity && !player->programmed_action)
13520 if (game.gravity && !player->programmed_action)
13523 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13524 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13525 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13526 int jx = player->jx, jy = player->jy;
13527 boolean player_is_moving_to_valid_field =
13528 (!player_is_snapping &&
13529 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13530 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13531 boolean player_can_fall_down = canFallDown(player);
13533 if (player_can_fall_down &&
13534 !player_is_moving_to_valid_field)
13535 player->programmed_action = MV_DOWN;
13539 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13541 return CheckGravityMovement(player);
13543 #if USE_PLAYER_GRAVITY
13544 if (player->gravity && !player->programmed_action)
13546 if (game.gravity && !player->programmed_action)
13549 int jx = player->jx, jy = player->jy;
13550 boolean field_under_player_is_free =
13551 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13552 boolean player_is_standing_on_valid_field =
13553 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13554 (IS_WALKABLE(Feld[jx][jy]) &&
13555 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13557 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13558 player->programmed_action = MV_DOWN;
13563 MovePlayerOneStep()
13564 -----------------------------------------------------------------------------
13565 dx, dy: direction (non-diagonal) to try to move the player to
13566 real_dx, real_dy: direction as read from input device (can be diagonal)
13569 boolean MovePlayerOneStep(struct PlayerInfo *player,
13570 int dx, int dy, int real_dx, int real_dy)
13572 int jx = player->jx, jy = player->jy;
13573 int new_jx = jx + dx, new_jy = jy + dy;
13574 #if !USE_FIXED_DONT_RUN_INTO
13578 boolean player_can_move = !player->cannot_move;
13580 if (!player->active || (!dx && !dy))
13581 return MP_NO_ACTION;
13583 player->MovDir = (dx < 0 ? MV_LEFT :
13584 dx > 0 ? MV_RIGHT :
13586 dy > 0 ? MV_DOWN : MV_NONE);
13588 if (!IN_LEV_FIELD(new_jx, new_jy))
13589 return MP_NO_ACTION;
13591 if (!player_can_move)
13593 if (player->MovPos == 0)
13595 player->is_moving = FALSE;
13596 player->is_digging = FALSE;
13597 player->is_collecting = FALSE;
13598 player->is_snapping = FALSE;
13599 player->is_pushing = FALSE;
13604 if (!options.network && game.centered_player_nr == -1 &&
13605 !AllPlayersInSight(player, new_jx, new_jy))
13606 return MP_NO_ACTION;
13608 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13609 return MP_NO_ACTION;
13612 #if !USE_FIXED_DONT_RUN_INTO
13613 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13615 /* (moved to DigField()) */
13616 if (player_can_move && DONT_RUN_INTO(element))
13618 if (element == EL_ACID && dx == 0 && dy == 1)
13620 SplashAcid(new_jx, new_jy);
13621 Feld[jx][jy] = EL_PLAYER_1;
13622 InitMovingField(jx, jy, MV_DOWN);
13623 Store[jx][jy] = EL_ACID;
13624 ContinueMoving(jx, jy);
13625 BuryPlayer(player);
13628 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13634 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13635 if (can_move != MP_MOVING)
13638 /* check if DigField() has caused relocation of the player */
13639 if (player->jx != jx || player->jy != jy)
13640 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13642 StorePlayer[jx][jy] = 0;
13643 player->last_jx = jx;
13644 player->last_jy = jy;
13645 player->jx = new_jx;
13646 player->jy = new_jy;
13647 StorePlayer[new_jx][new_jy] = player->element_nr;
13649 if (player->move_delay_value_next != -1)
13651 player->move_delay_value = player->move_delay_value_next;
13652 player->move_delay_value_next = -1;
13656 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13658 player->step_counter++;
13660 PlayerVisit[jx][jy] = FrameCounter;
13662 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13663 player->is_moving = TRUE;
13667 /* should better be called in MovePlayer(), but this breaks some tapes */
13668 ScrollPlayer(player, SCROLL_INIT);
13674 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13676 int jx = player->jx, jy = player->jy;
13677 int old_jx = jx, old_jy = jy;
13678 int moved = MP_NO_ACTION;
13680 if (!player->active)
13685 if (player->MovPos == 0)
13687 player->is_moving = FALSE;
13688 player->is_digging = FALSE;
13689 player->is_collecting = FALSE;
13690 player->is_snapping = FALSE;
13691 player->is_pushing = FALSE;
13697 if (player->move_delay > 0)
13700 player->move_delay = -1; /* set to "uninitialized" value */
13702 /* store if player is automatically moved to next field */
13703 player->is_auto_moving = (player->programmed_action != MV_NONE);
13705 /* remove the last programmed player action */
13706 player->programmed_action = 0;
13708 if (player->MovPos)
13710 /* should only happen if pre-1.2 tape recordings are played */
13711 /* this is only for backward compatibility */
13713 int original_move_delay_value = player->move_delay_value;
13716 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13720 /* scroll remaining steps with finest movement resolution */
13721 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13723 while (player->MovPos)
13725 ScrollPlayer(player, SCROLL_GO_ON);
13726 ScrollScreen(NULL, SCROLL_GO_ON);
13728 AdvanceFrameAndPlayerCounters(player->index_nr);
13734 player->move_delay_value = original_move_delay_value;
13737 player->is_active = FALSE;
13739 if (player->last_move_dir & MV_HORIZONTAL)
13741 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13742 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13746 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13747 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13750 #if USE_FIXED_BORDER_RUNNING_GFX
13751 if (!moved && !player->is_active)
13753 player->is_moving = FALSE;
13754 player->is_digging = FALSE;
13755 player->is_collecting = FALSE;
13756 player->is_snapping = FALSE;
13757 player->is_pushing = FALSE;
13765 if (moved & MP_MOVING && !ScreenMovPos &&
13766 (player->index_nr == game.centered_player_nr ||
13767 game.centered_player_nr == -1))
13769 if (moved & MP_MOVING && !ScreenMovPos &&
13770 (player == local_player || !options.network))
13773 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13774 int offset = game.scroll_delay_value;
13776 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13778 /* actual player has left the screen -- scroll in that direction */
13779 if (jx != old_jx) /* player has moved horizontally */
13780 scroll_x += (jx - old_jx);
13781 else /* player has moved vertically */
13782 scroll_y += (jy - old_jy);
13786 if (jx != old_jx) /* player has moved horizontally */
13788 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13789 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13790 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13792 /* don't scroll over playfield boundaries */
13793 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13794 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13796 /* don't scroll more than one field at a time */
13797 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13799 /* don't scroll against the player's moving direction */
13800 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13801 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13802 scroll_x = old_scroll_x;
13804 else /* player has moved vertically */
13806 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13807 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13808 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13810 /* don't scroll over playfield boundaries */
13811 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13812 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13814 /* don't scroll more than one field at a time */
13815 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13817 /* don't scroll against the player's moving direction */
13818 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13819 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13820 scroll_y = old_scroll_y;
13824 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13827 if (!options.network && game.centered_player_nr == -1 &&
13828 !AllPlayersInVisibleScreen())
13830 scroll_x = old_scroll_x;
13831 scroll_y = old_scroll_y;
13835 if (!options.network && !AllPlayersInVisibleScreen())
13837 scroll_x = old_scroll_x;
13838 scroll_y = old_scroll_y;
13843 ScrollScreen(player, SCROLL_INIT);
13844 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13849 player->StepFrame = 0;
13851 if (moved & MP_MOVING)
13853 if (old_jx != jx && old_jy == jy)
13854 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13855 else if (old_jx == jx && old_jy != jy)
13856 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13858 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13860 player->last_move_dir = player->MovDir;
13861 player->is_moving = TRUE;
13862 player->is_snapping = FALSE;
13863 player->is_switching = FALSE;
13864 player->is_dropping = FALSE;
13865 player->is_dropping_pressed = FALSE;
13866 player->drop_pressed_delay = 0;
13869 /* should better be called here than above, but this breaks some tapes */
13870 ScrollPlayer(player, SCROLL_INIT);
13875 CheckGravityMovementWhenNotMoving(player);
13877 player->is_moving = FALSE;
13879 /* at this point, the player is allowed to move, but cannot move right now
13880 (e.g. because of something blocking the way) -- ensure that the player
13881 is also allowed to move in the next frame (in old versions before 3.1.1,
13882 the player was forced to wait again for eight frames before next try) */
13884 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13885 player->move_delay = 0; /* allow direct movement in the next frame */
13888 if (player->move_delay == -1) /* not yet initialized by DigField() */
13889 player->move_delay = player->move_delay_value;
13891 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13893 TestIfPlayerTouchesBadThing(jx, jy);
13894 TestIfPlayerTouchesCustomElement(jx, jy);
13897 if (!player->active)
13898 RemovePlayer(player);
13903 void ScrollPlayer(struct PlayerInfo *player, int mode)
13905 int jx = player->jx, jy = player->jy;
13906 int last_jx = player->last_jx, last_jy = player->last_jy;
13907 int move_stepsize = TILEX / player->move_delay_value;
13909 #if USE_NEW_PLAYER_SPEED
13910 if (!player->active)
13913 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13916 if (!player->active || player->MovPos == 0)
13920 if (mode == SCROLL_INIT)
13922 player->actual_frame_counter = FrameCounter;
13923 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13925 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13926 Feld[last_jx][last_jy] == EL_EMPTY)
13928 int last_field_block_delay = 0; /* start with no blocking at all */
13929 int block_delay_adjustment = player->block_delay_adjustment;
13931 /* if player blocks last field, add delay for exactly one move */
13932 if (player->block_last_field)
13934 last_field_block_delay += player->move_delay_value;
13936 /* when blocking enabled, prevent moving up despite gravity */
13937 #if USE_PLAYER_GRAVITY
13938 if (player->gravity && player->MovDir == MV_UP)
13939 block_delay_adjustment = -1;
13941 if (game.gravity && player->MovDir == MV_UP)
13942 block_delay_adjustment = -1;
13946 /* add block delay adjustment (also possible when not blocking) */
13947 last_field_block_delay += block_delay_adjustment;
13949 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13950 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13953 #if USE_NEW_PLAYER_SPEED
13954 if (player->MovPos != 0) /* player has not yet reached destination */
13960 else if (!FrameReached(&player->actual_frame_counter, 1))
13963 #if USE_NEW_PLAYER_SPEED
13964 if (player->MovPos != 0)
13966 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13967 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13969 /* before DrawPlayer() to draw correct player graphic for this case */
13970 if (player->MovPos == 0)
13971 CheckGravityMovement(player);
13974 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13975 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13977 /* before DrawPlayer() to draw correct player graphic for this case */
13978 if (player->MovPos == 0)
13979 CheckGravityMovement(player);
13982 if (player->MovPos == 0) /* player reached destination field */
13984 if (player->move_delay_reset_counter > 0)
13986 player->move_delay_reset_counter--;
13988 if (player->move_delay_reset_counter == 0)
13990 /* continue with normal speed after quickly moving through gate */
13991 HALVE_PLAYER_SPEED(player);
13993 /* be able to make the next move without delay */
13994 player->move_delay = 0;
13998 player->last_jx = jx;
13999 player->last_jy = jy;
14001 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14002 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14004 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14006 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14007 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14009 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14011 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14012 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14014 DrawPlayer(player); /* needed here only to cleanup last field */
14015 RemovePlayer(player);
14017 if (local_player->friends_still_needed == 0 ||
14018 IS_SP_ELEMENT(Feld[jx][jy]))
14019 PlayerWins(player);
14022 /* this breaks one level: "machine", level 000 */
14024 int move_direction = player->MovDir;
14025 int enter_side = MV_DIR_OPPOSITE(move_direction);
14026 int leave_side = move_direction;
14027 int old_jx = last_jx;
14028 int old_jy = last_jy;
14029 int old_element = Feld[old_jx][old_jy];
14030 int new_element = Feld[jx][jy];
14032 if (IS_CUSTOM_ELEMENT(old_element))
14033 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14035 player->index_bit, leave_side);
14037 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14038 CE_PLAYER_LEAVES_X,
14039 player->index_bit, leave_side);
14041 if (IS_CUSTOM_ELEMENT(new_element))
14042 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14043 player->index_bit, enter_side);
14045 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14046 CE_PLAYER_ENTERS_X,
14047 player->index_bit, enter_side);
14049 #if USE_FIX_CE_ACTION_WITH_PLAYER
14050 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14051 CE_MOVE_OF_X, move_direction);
14053 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14054 CE_MOVE_OF_X, move_direction);
14058 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14060 TestIfPlayerTouchesBadThing(jx, jy);
14061 TestIfPlayerTouchesCustomElement(jx, jy);
14063 /* needed because pushed element has not yet reached its destination,
14064 so it would trigger a change event at its previous field location */
14065 if (!player->is_pushing)
14066 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14068 if (!player->active)
14069 RemovePlayer(player);
14072 if (!local_player->LevelSolved && level.use_step_counter)
14082 if (TimeLeft <= 10 && setup.time_limit)
14083 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14086 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14088 DisplayGameControlValues();
14090 DrawGameValue_Time(TimeLeft);
14093 if (!TimeLeft && setup.time_limit)
14094 for (i = 0; i < MAX_PLAYERS; i++)
14095 KillPlayer(&stored_player[i]);
14098 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14100 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14102 DisplayGameControlValues();
14105 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14106 DrawGameValue_Time(TimePlayed);
14110 if (tape.single_step && tape.recording && !tape.pausing &&
14111 !player->programmed_action)
14112 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14116 void ScrollScreen(struct PlayerInfo *player, int mode)
14118 static unsigned int screen_frame_counter = 0;
14120 if (mode == SCROLL_INIT)
14122 /* set scrolling step size according to actual player's moving speed */
14123 ScrollStepSize = TILEX / player->move_delay_value;
14125 screen_frame_counter = FrameCounter;
14126 ScreenMovDir = player->MovDir;
14127 ScreenMovPos = player->MovPos;
14128 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14131 else if (!FrameReached(&screen_frame_counter, 1))
14136 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14137 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14138 redraw_mask |= REDRAW_FIELD;
14141 ScreenMovDir = MV_NONE;
14144 void TestIfPlayerTouchesCustomElement(int x, int y)
14146 static int xy[4][2] =
14153 static int trigger_sides[4][2] =
14155 /* center side border side */
14156 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14157 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14158 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14159 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14161 static int touch_dir[4] =
14163 MV_LEFT | MV_RIGHT,
14168 int center_element = Feld[x][y]; /* should always be non-moving! */
14171 for (i = 0; i < NUM_DIRECTIONS; i++)
14173 int xx = x + xy[i][0];
14174 int yy = y + xy[i][1];
14175 int center_side = trigger_sides[i][0];
14176 int border_side = trigger_sides[i][1];
14177 int border_element;
14179 if (!IN_LEV_FIELD(xx, yy))
14182 if (IS_PLAYER(x, y)) /* player found at center element */
14184 struct PlayerInfo *player = PLAYERINFO(x, y);
14186 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14187 border_element = Feld[xx][yy]; /* may be moving! */
14188 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14189 border_element = Feld[xx][yy];
14190 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14191 border_element = MovingOrBlocked2Element(xx, yy);
14193 continue; /* center and border element do not touch */
14195 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14196 player->index_bit, border_side);
14197 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14198 CE_PLAYER_TOUCHES_X,
14199 player->index_bit, border_side);
14201 #if USE_FIX_CE_ACTION_WITH_PLAYER
14203 /* use player element that is initially defined in the level playfield,
14204 not the player element that corresponds to the runtime player number
14205 (example: a level that contains EL_PLAYER_3 as the only player would
14206 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14207 int player_element = PLAYERINFO(x, y)->initial_element;
14209 CheckElementChangeBySide(xx, yy, border_element, player_element,
14210 CE_TOUCHING_X, border_side);
14214 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14216 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14218 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14220 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14221 continue; /* center and border element do not touch */
14224 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14225 player->index_bit, center_side);
14226 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14227 CE_PLAYER_TOUCHES_X,
14228 player->index_bit, center_side);
14230 #if USE_FIX_CE_ACTION_WITH_PLAYER
14232 /* use player element that is initially defined in the level playfield,
14233 not the player element that corresponds to the runtime player number
14234 (example: a level that contains EL_PLAYER_3 as the only player would
14235 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14236 int player_element = PLAYERINFO(xx, yy)->initial_element;
14238 CheckElementChangeBySide(x, y, center_element, player_element,
14239 CE_TOUCHING_X, center_side);
14248 #if USE_ELEMENT_TOUCHING_BUGFIX
14250 void TestIfElementTouchesCustomElement(int x, int y)
14252 static int xy[4][2] =
14259 static int trigger_sides[4][2] =
14261 /* center side border side */
14262 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14263 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14264 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14265 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14267 static int touch_dir[4] =
14269 MV_LEFT | MV_RIGHT,
14274 boolean change_center_element = FALSE;
14275 int center_element = Feld[x][y]; /* should always be non-moving! */
14276 int border_element_old[NUM_DIRECTIONS];
14279 for (i = 0; i < NUM_DIRECTIONS; i++)
14281 int xx = x + xy[i][0];
14282 int yy = y + xy[i][1];
14283 int border_element;
14285 border_element_old[i] = -1;
14287 if (!IN_LEV_FIELD(xx, yy))
14290 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14291 border_element = Feld[xx][yy]; /* may be moving! */
14292 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14293 border_element = Feld[xx][yy];
14294 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14295 border_element = MovingOrBlocked2Element(xx, yy);
14297 continue; /* center and border element do not touch */
14299 border_element_old[i] = border_element;
14302 for (i = 0; i < NUM_DIRECTIONS; i++)
14304 int xx = x + xy[i][0];
14305 int yy = y + xy[i][1];
14306 int center_side = trigger_sides[i][0];
14307 int border_element = border_element_old[i];
14309 if (border_element == -1)
14312 /* check for change of border element */
14313 CheckElementChangeBySide(xx, yy, border_element, center_element,
14314 CE_TOUCHING_X, center_side);
14316 /* (center element cannot be player, so we dont have to check this here) */
14319 for (i = 0; i < NUM_DIRECTIONS; i++)
14321 int xx = x + xy[i][0];
14322 int yy = y + xy[i][1];
14323 int border_side = trigger_sides[i][1];
14324 int border_element = border_element_old[i];
14326 if (border_element == -1)
14329 /* check for change of center element (but change it only once) */
14330 if (!change_center_element)
14331 change_center_element =
14332 CheckElementChangeBySide(x, y, center_element, border_element,
14333 CE_TOUCHING_X, border_side);
14335 #if USE_FIX_CE_ACTION_WITH_PLAYER
14336 if (IS_PLAYER(xx, yy))
14338 /* use player element that is initially defined in the level playfield,
14339 not the player element that corresponds to the runtime player number
14340 (example: a level that contains EL_PLAYER_3 as the only player would
14341 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14342 int player_element = PLAYERINFO(xx, yy)->initial_element;
14344 CheckElementChangeBySide(x, y, center_element, player_element,
14345 CE_TOUCHING_X, border_side);
14353 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14355 static int xy[4][2] =
14362 static int trigger_sides[4][2] =
14364 /* center side border side */
14365 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14366 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14367 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14368 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14370 static int touch_dir[4] =
14372 MV_LEFT | MV_RIGHT,
14377 boolean change_center_element = FALSE;
14378 int center_element = Feld[x][y]; /* should always be non-moving! */
14381 for (i = 0; i < NUM_DIRECTIONS; i++)
14383 int xx = x + xy[i][0];
14384 int yy = y + xy[i][1];
14385 int center_side = trigger_sides[i][0];
14386 int border_side = trigger_sides[i][1];
14387 int border_element;
14389 if (!IN_LEV_FIELD(xx, yy))
14392 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14393 border_element = Feld[xx][yy]; /* may be moving! */
14394 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14395 border_element = Feld[xx][yy];
14396 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14397 border_element = MovingOrBlocked2Element(xx, yy);
14399 continue; /* center and border element do not touch */
14401 /* check for change of center element (but change it only once) */
14402 if (!change_center_element)
14403 change_center_element =
14404 CheckElementChangeBySide(x, y, center_element, border_element,
14405 CE_TOUCHING_X, border_side);
14407 /* check for change of border element */
14408 CheckElementChangeBySide(xx, yy, border_element, center_element,
14409 CE_TOUCHING_X, center_side);
14415 void TestIfElementHitsCustomElement(int x, int y, int direction)
14417 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14418 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14419 int hitx = x + dx, hity = y + dy;
14420 int hitting_element = Feld[x][y];
14421 int touched_element;
14423 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14426 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14427 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14429 if (IN_LEV_FIELD(hitx, hity))
14431 int opposite_direction = MV_DIR_OPPOSITE(direction);
14432 int hitting_side = direction;
14433 int touched_side = opposite_direction;
14434 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14435 MovDir[hitx][hity] != direction ||
14436 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14442 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14443 CE_HITTING_X, touched_side);
14445 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14446 CE_HIT_BY_X, hitting_side);
14448 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14449 CE_HIT_BY_SOMETHING, opposite_direction);
14451 #if USE_FIX_CE_ACTION_WITH_PLAYER
14452 if (IS_PLAYER(hitx, hity))
14454 /* use player element that is initially defined in the level playfield,
14455 not the player element that corresponds to the runtime player number
14456 (example: a level that contains EL_PLAYER_3 as the only player would
14457 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14458 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14460 CheckElementChangeBySide(x, y, hitting_element, player_element,
14461 CE_HITTING_X, touched_side);
14467 /* "hitting something" is also true when hitting the playfield border */
14468 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14469 CE_HITTING_SOMETHING, direction);
14473 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14475 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14476 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14477 int hitx = x + dx, hity = y + dy;
14478 int hitting_element = Feld[x][y];
14479 int touched_element;
14481 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14482 !IS_FREE(hitx, hity) &&
14483 (!IS_MOVING(hitx, hity) ||
14484 MovDir[hitx][hity] != direction ||
14485 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14488 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14492 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14496 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14497 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14499 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14500 EP_CAN_SMASH_EVERYTHING, direction);
14502 if (IN_LEV_FIELD(hitx, hity))
14504 int opposite_direction = MV_DIR_OPPOSITE(direction);
14505 int hitting_side = direction;
14506 int touched_side = opposite_direction;
14508 int touched_element = MovingOrBlocked2Element(hitx, hity);
14511 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14512 MovDir[hitx][hity] != direction ||
14513 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14522 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14523 CE_SMASHED_BY_SOMETHING, opposite_direction);
14525 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14526 CE_OTHER_IS_SMASHING, touched_side);
14528 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14529 CE_OTHER_GETS_SMASHED, hitting_side);
14535 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14537 int i, kill_x = -1, kill_y = -1;
14539 int bad_element = -1;
14540 static int test_xy[4][2] =
14547 static int test_dir[4] =
14555 for (i = 0; i < NUM_DIRECTIONS; i++)
14557 int test_x, test_y, test_move_dir, test_element;
14559 test_x = good_x + test_xy[i][0];
14560 test_y = good_y + test_xy[i][1];
14562 if (!IN_LEV_FIELD(test_x, test_y))
14566 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14568 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14570 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14571 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14573 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14574 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14578 bad_element = test_element;
14584 if (kill_x != -1 || kill_y != -1)
14586 if (IS_PLAYER(good_x, good_y))
14588 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14590 if (player->shield_deadly_time_left > 0 &&
14591 !IS_INDESTRUCTIBLE(bad_element))
14592 Bang(kill_x, kill_y);
14593 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14594 KillPlayer(player);
14597 Bang(good_x, good_y);
14601 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14603 int i, kill_x = -1, kill_y = -1;
14604 int bad_element = Feld[bad_x][bad_y];
14605 static int test_xy[4][2] =
14612 static int touch_dir[4] =
14614 MV_LEFT | MV_RIGHT,
14619 static int test_dir[4] =
14627 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14630 for (i = 0; i < NUM_DIRECTIONS; i++)
14632 int test_x, test_y, test_move_dir, test_element;
14634 test_x = bad_x + test_xy[i][0];
14635 test_y = bad_y + test_xy[i][1];
14637 if (!IN_LEV_FIELD(test_x, test_y))
14641 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14643 test_element = Feld[test_x][test_y];
14645 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14646 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14648 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14649 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14651 /* good thing is player or penguin that does not move away */
14652 if (IS_PLAYER(test_x, test_y))
14654 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14656 if (bad_element == EL_ROBOT && player->is_moving)
14657 continue; /* robot does not kill player if he is moving */
14659 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14661 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14662 continue; /* center and border element do not touch */
14670 else if (test_element == EL_PENGUIN)
14680 if (kill_x != -1 || kill_y != -1)
14682 if (IS_PLAYER(kill_x, kill_y))
14684 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14686 if (player->shield_deadly_time_left > 0 &&
14687 !IS_INDESTRUCTIBLE(bad_element))
14688 Bang(bad_x, bad_y);
14689 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14690 KillPlayer(player);
14693 Bang(kill_x, kill_y);
14697 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14699 int bad_element = Feld[bad_x][bad_y];
14700 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14701 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14702 int test_x = bad_x + dx, test_y = bad_y + dy;
14703 int test_move_dir, test_element;
14704 int kill_x = -1, kill_y = -1;
14706 if (!IN_LEV_FIELD(test_x, test_y))
14710 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14712 test_element = Feld[test_x][test_y];
14714 if (test_move_dir != bad_move_dir)
14716 /* good thing can be player or penguin that does not move away */
14717 if (IS_PLAYER(test_x, test_y))
14719 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14721 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14722 player as being hit when he is moving towards the bad thing, because
14723 the "get hit by" condition would be lost after the player stops) */
14724 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14725 return; /* player moves away from bad thing */
14730 else if (test_element == EL_PENGUIN)
14737 if (kill_x != -1 || kill_y != -1)
14739 if (IS_PLAYER(kill_x, kill_y))
14741 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14743 if (player->shield_deadly_time_left > 0 &&
14744 !IS_INDESTRUCTIBLE(bad_element))
14745 Bang(bad_x, bad_y);
14746 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14747 KillPlayer(player);
14750 Bang(kill_x, kill_y);
14754 void TestIfPlayerTouchesBadThing(int x, int y)
14756 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14759 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14761 TestIfGoodThingHitsBadThing(x, y, move_dir);
14764 void TestIfBadThingTouchesPlayer(int x, int y)
14766 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14769 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14771 TestIfBadThingHitsGoodThing(x, y, move_dir);
14774 void TestIfFriendTouchesBadThing(int x, int y)
14776 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14779 void TestIfBadThingTouchesFriend(int x, int y)
14781 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14784 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14786 int i, kill_x = bad_x, kill_y = bad_y;
14787 static int xy[4][2] =
14795 for (i = 0; i < NUM_DIRECTIONS; i++)
14799 x = bad_x + xy[i][0];
14800 y = bad_y + xy[i][1];
14801 if (!IN_LEV_FIELD(x, y))
14804 element = Feld[x][y];
14805 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14806 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14814 if (kill_x != bad_x || kill_y != bad_y)
14815 Bang(bad_x, bad_y);
14818 void KillPlayer(struct PlayerInfo *player)
14820 int jx = player->jx, jy = player->jy;
14822 if (!player->active)
14826 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14827 player->killed, player->active, player->reanimated);
14830 /* the following code was introduced to prevent an infinite loop when calling
14832 -> CheckTriggeredElementChangeExt()
14833 -> ExecuteCustomElementAction()
14835 -> (infinitely repeating the above sequence of function calls)
14836 which occurs when killing the player while having a CE with the setting
14837 "kill player X when explosion of <player X>"; the solution using a new
14838 field "player->killed" was chosen for backwards compatibility, although
14839 clever use of the fields "player->active" etc. would probably also work */
14841 if (player->killed)
14845 player->killed = TRUE;
14847 /* remove accessible field at the player's position */
14848 Feld[jx][jy] = EL_EMPTY;
14850 /* deactivate shield (else Bang()/Explode() would not work right) */
14851 player->shield_normal_time_left = 0;
14852 player->shield_deadly_time_left = 0;
14855 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14856 player->killed, player->active, player->reanimated);
14862 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14863 player->killed, player->active, player->reanimated);
14866 #if USE_PLAYER_REANIMATION
14868 if (player->reanimated) /* killed player may have been reanimated */
14869 player->killed = player->reanimated = FALSE;
14871 BuryPlayer(player);
14873 if (player->killed) /* player may have been reanimated */
14874 BuryPlayer(player);
14877 BuryPlayer(player);
14881 static void KillPlayerUnlessEnemyProtected(int x, int y)
14883 if (!PLAYER_ENEMY_PROTECTED(x, y))
14884 KillPlayer(PLAYERINFO(x, y));
14887 static void KillPlayerUnlessExplosionProtected(int x, int y)
14889 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14890 KillPlayer(PLAYERINFO(x, y));
14893 void BuryPlayer(struct PlayerInfo *player)
14895 int jx = player->jx, jy = player->jy;
14897 if (!player->active)
14900 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14901 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14903 player->GameOver = TRUE;
14904 RemovePlayer(player);
14907 void RemovePlayer(struct PlayerInfo *player)
14909 int jx = player->jx, jy = player->jy;
14910 int i, found = FALSE;
14912 player->present = FALSE;
14913 player->active = FALSE;
14915 if (!ExplodeField[jx][jy])
14916 StorePlayer[jx][jy] = 0;
14918 if (player->is_moving)
14919 TEST_DrawLevelField(player->last_jx, player->last_jy);
14921 for (i = 0; i < MAX_PLAYERS; i++)
14922 if (stored_player[i].active)
14926 AllPlayersGone = TRUE;
14932 #if USE_NEW_SNAP_DELAY
14933 static void setFieldForSnapping(int x, int y, int element, int direction)
14935 struct ElementInfo *ei = &element_info[element];
14936 int direction_bit = MV_DIR_TO_BIT(direction);
14937 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14938 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14939 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14941 Feld[x][y] = EL_ELEMENT_SNAPPING;
14942 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14944 ResetGfxAnimation(x, y);
14946 GfxElement[x][y] = element;
14947 GfxAction[x][y] = action;
14948 GfxDir[x][y] = direction;
14949 GfxFrame[x][y] = -1;
14954 =============================================================================
14955 checkDiagonalPushing()
14956 -----------------------------------------------------------------------------
14957 check if diagonal input device direction results in pushing of object
14958 (by checking if the alternative direction is walkable, diggable, ...)
14959 =============================================================================
14962 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14963 int x, int y, int real_dx, int real_dy)
14965 int jx, jy, dx, dy, xx, yy;
14967 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14970 /* diagonal direction: check alternative direction */
14975 xx = jx + (dx == 0 ? real_dx : 0);
14976 yy = jy + (dy == 0 ? real_dy : 0);
14978 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14982 =============================================================================
14984 -----------------------------------------------------------------------------
14985 x, y: field next to player (non-diagonal) to try to dig to
14986 real_dx, real_dy: direction as read from input device (can be diagonal)
14987 =============================================================================
14990 static int DigField(struct PlayerInfo *player,
14991 int oldx, int oldy, int x, int y,
14992 int real_dx, int real_dy, int mode)
14994 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14995 boolean player_was_pushing = player->is_pushing;
14996 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14997 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14998 int jx = oldx, jy = oldy;
14999 int dx = x - jx, dy = y - jy;
15000 int nextx = x + dx, nexty = y + dy;
15001 int move_direction = (dx == -1 ? MV_LEFT :
15002 dx == +1 ? MV_RIGHT :
15004 dy == +1 ? MV_DOWN : MV_NONE);
15005 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15006 int dig_side = MV_DIR_OPPOSITE(move_direction);
15007 int old_element = Feld[jx][jy];
15008 #if USE_FIXED_DONT_RUN_INTO
15009 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15015 if (is_player) /* function can also be called by EL_PENGUIN */
15017 if (player->MovPos == 0)
15019 player->is_digging = FALSE;
15020 player->is_collecting = FALSE;
15023 if (player->MovPos == 0) /* last pushing move finished */
15024 player->is_pushing = FALSE;
15026 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15028 player->is_switching = FALSE;
15029 player->push_delay = -1;
15031 return MP_NO_ACTION;
15035 #if !USE_FIXED_DONT_RUN_INTO
15036 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15037 return MP_NO_ACTION;
15040 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15041 old_element = Back[jx][jy];
15043 /* in case of element dropped at player position, check background */
15044 else if (Back[jx][jy] != EL_EMPTY &&
15045 game.engine_version >= VERSION_IDENT(2,2,0,0))
15046 old_element = Back[jx][jy];
15048 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15049 return MP_NO_ACTION; /* field has no opening in this direction */
15051 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15052 return MP_NO_ACTION; /* field has no opening in this direction */
15054 #if USE_FIXED_DONT_RUN_INTO
15055 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15059 Feld[jx][jy] = player->artwork_element;
15060 InitMovingField(jx, jy, MV_DOWN);
15061 Store[jx][jy] = EL_ACID;
15062 ContinueMoving(jx, jy);
15063 BuryPlayer(player);
15065 return MP_DONT_RUN_INTO;
15069 #if USE_FIXED_DONT_RUN_INTO
15070 if (player_can_move && DONT_RUN_INTO(element))
15072 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15074 return MP_DONT_RUN_INTO;
15078 #if USE_FIXED_DONT_RUN_INTO
15079 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15080 return MP_NO_ACTION;
15083 #if !USE_FIXED_DONT_RUN_INTO
15084 element = Feld[x][y];
15087 collect_count = element_info[element].collect_count_initial;
15089 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15090 return MP_NO_ACTION;
15092 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15093 player_can_move = player_can_move_or_snap;
15095 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15096 game.engine_version >= VERSION_IDENT(2,2,0,0))
15098 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15099 player->index_bit, dig_side);
15100 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15101 player->index_bit, dig_side);
15103 if (element == EL_DC_LANDMINE)
15106 if (Feld[x][y] != element) /* field changed by snapping */
15109 return MP_NO_ACTION;
15112 #if USE_PLAYER_GRAVITY
15113 if (player->gravity && is_player && !player->is_auto_moving &&
15114 canFallDown(player) && move_direction != MV_DOWN &&
15115 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15116 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15118 if (game.gravity && is_player && !player->is_auto_moving &&
15119 canFallDown(player) && move_direction != MV_DOWN &&
15120 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15121 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15124 if (player_can_move &&
15125 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15127 int sound_element = SND_ELEMENT(element);
15128 int sound_action = ACTION_WALKING;
15130 if (IS_RND_GATE(element))
15132 if (!player->key[RND_GATE_NR(element)])
15133 return MP_NO_ACTION;
15135 else if (IS_RND_GATE_GRAY(element))
15137 if (!player->key[RND_GATE_GRAY_NR(element)])
15138 return MP_NO_ACTION;
15140 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15142 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15143 return MP_NO_ACTION;
15145 else if (element == EL_EXIT_OPEN ||
15146 element == EL_EM_EXIT_OPEN ||
15148 element == EL_EM_EXIT_OPENING ||
15150 element == EL_STEEL_EXIT_OPEN ||
15151 element == EL_EM_STEEL_EXIT_OPEN ||
15153 element == EL_EM_STEEL_EXIT_OPENING ||
15155 element == EL_SP_EXIT_OPEN ||
15156 element == EL_SP_EXIT_OPENING)
15158 sound_action = ACTION_PASSING; /* player is passing exit */
15160 else if (element == EL_EMPTY)
15162 sound_action = ACTION_MOVING; /* nothing to walk on */
15165 /* play sound from background or player, whatever is available */
15166 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15167 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15169 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15171 else if (player_can_move &&
15172 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15174 if (!ACCESS_FROM(element, opposite_direction))
15175 return MP_NO_ACTION; /* field not accessible from this direction */
15177 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15178 return MP_NO_ACTION;
15180 if (IS_EM_GATE(element))
15182 if (!player->key[EM_GATE_NR(element)])
15183 return MP_NO_ACTION;
15185 else if (IS_EM_GATE_GRAY(element))
15187 if (!player->key[EM_GATE_GRAY_NR(element)])
15188 return MP_NO_ACTION;
15190 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15192 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15193 return MP_NO_ACTION;
15195 else if (IS_EMC_GATE(element))
15197 if (!player->key[EMC_GATE_NR(element)])
15198 return MP_NO_ACTION;
15200 else if (IS_EMC_GATE_GRAY(element))
15202 if (!player->key[EMC_GATE_GRAY_NR(element)])
15203 return MP_NO_ACTION;
15205 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15207 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15208 return MP_NO_ACTION;
15210 else if (element == EL_DC_GATE_WHITE ||
15211 element == EL_DC_GATE_WHITE_GRAY ||
15212 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15214 if (player->num_white_keys == 0)
15215 return MP_NO_ACTION;
15217 player->num_white_keys--;
15219 else if (IS_SP_PORT(element))
15221 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15222 element == EL_SP_GRAVITY_PORT_RIGHT ||
15223 element == EL_SP_GRAVITY_PORT_UP ||
15224 element == EL_SP_GRAVITY_PORT_DOWN)
15225 #if USE_PLAYER_GRAVITY
15226 player->gravity = !player->gravity;
15228 game.gravity = !game.gravity;
15230 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15231 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15232 element == EL_SP_GRAVITY_ON_PORT_UP ||
15233 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15234 #if USE_PLAYER_GRAVITY
15235 player->gravity = TRUE;
15237 game.gravity = TRUE;
15239 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15240 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15241 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15242 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15243 #if USE_PLAYER_GRAVITY
15244 player->gravity = FALSE;
15246 game.gravity = FALSE;
15250 /* automatically move to the next field with double speed */
15251 player->programmed_action = move_direction;
15253 if (player->move_delay_reset_counter == 0)
15255 player->move_delay_reset_counter = 2; /* two double speed steps */
15257 DOUBLE_PLAYER_SPEED(player);
15260 PlayLevelSoundAction(x, y, ACTION_PASSING);
15262 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15266 if (mode != DF_SNAP)
15268 GfxElement[x][y] = GFX_ELEMENT(element);
15269 player->is_digging = TRUE;
15272 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15274 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15275 player->index_bit, dig_side);
15277 if (mode == DF_SNAP)
15279 #if USE_NEW_SNAP_DELAY
15280 if (level.block_snap_field)
15281 setFieldForSnapping(x, y, element, move_direction);
15283 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15285 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15288 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15289 player->index_bit, dig_side);
15292 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15296 if (is_player && mode != DF_SNAP)
15298 GfxElement[x][y] = element;
15299 player->is_collecting = TRUE;
15302 if (element == EL_SPEED_PILL)
15304 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15306 else if (element == EL_EXTRA_TIME && level.time > 0)
15308 TimeLeft += level.extra_time;
15311 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15313 DisplayGameControlValues();
15315 DrawGameValue_Time(TimeLeft);
15318 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15320 player->shield_normal_time_left += level.shield_normal_time;
15321 if (element == EL_SHIELD_DEADLY)
15322 player->shield_deadly_time_left += level.shield_deadly_time;
15324 else if (element == EL_DYNAMITE ||
15325 element == EL_EM_DYNAMITE ||
15326 element == EL_SP_DISK_RED)
15328 if (player->inventory_size < MAX_INVENTORY_SIZE)
15329 player->inventory_element[player->inventory_size++] = element;
15331 DrawGameDoorValues();
15333 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15335 player->dynabomb_count++;
15336 player->dynabombs_left++;
15338 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15340 player->dynabomb_size++;
15342 else if (element == EL_DYNABOMB_INCREASE_POWER)
15344 player->dynabomb_xl = TRUE;
15346 else if (IS_KEY(element))
15348 player->key[KEY_NR(element)] = TRUE;
15350 DrawGameDoorValues();
15352 else if (element == EL_DC_KEY_WHITE)
15354 player->num_white_keys++;
15356 /* display white keys? */
15357 /* DrawGameDoorValues(); */
15359 else if (IS_ENVELOPE(element))
15361 player->show_envelope = element;
15363 else if (element == EL_EMC_LENSES)
15365 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15367 RedrawAllInvisibleElementsForLenses();
15369 else if (element == EL_EMC_MAGNIFIER)
15371 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15373 RedrawAllInvisibleElementsForMagnifier();
15375 else if (IS_DROPPABLE(element) ||
15376 IS_THROWABLE(element)) /* can be collected and dropped */
15380 if (collect_count == 0)
15381 player->inventory_infinite_element = element;
15383 for (i = 0; i < collect_count; i++)
15384 if (player->inventory_size < MAX_INVENTORY_SIZE)
15385 player->inventory_element[player->inventory_size++] = element;
15387 DrawGameDoorValues();
15389 else if (collect_count > 0)
15391 local_player->gems_still_needed -= collect_count;
15392 if (local_player->gems_still_needed < 0)
15393 local_player->gems_still_needed = 0;
15396 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15398 DisplayGameControlValues();
15400 DrawGameValue_Emeralds(local_player->gems_still_needed);
15404 RaiseScoreElement(element);
15405 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15408 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15409 player->index_bit, dig_side);
15411 if (mode == DF_SNAP)
15413 #if USE_NEW_SNAP_DELAY
15414 if (level.block_snap_field)
15415 setFieldForSnapping(x, y, element, move_direction);
15417 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15419 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15422 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15423 player->index_bit, dig_side);
15426 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15428 if (mode == DF_SNAP && element != EL_BD_ROCK)
15429 return MP_NO_ACTION;
15431 if (CAN_FALL(element) && dy)
15432 return MP_NO_ACTION;
15434 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15435 !(element == EL_SPRING && level.use_spring_bug))
15436 return MP_NO_ACTION;
15438 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15439 ((move_direction & MV_VERTICAL &&
15440 ((element_info[element].move_pattern & MV_LEFT &&
15441 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15442 (element_info[element].move_pattern & MV_RIGHT &&
15443 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15444 (move_direction & MV_HORIZONTAL &&
15445 ((element_info[element].move_pattern & MV_UP &&
15446 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15447 (element_info[element].move_pattern & MV_DOWN &&
15448 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15449 return MP_NO_ACTION;
15451 /* do not push elements already moving away faster than player */
15452 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15453 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15454 return MP_NO_ACTION;
15456 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15458 if (player->push_delay_value == -1 || !player_was_pushing)
15459 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15461 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15463 if (player->push_delay_value == -1)
15464 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15466 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15468 if (!player->is_pushing)
15469 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15472 player->is_pushing = TRUE;
15473 player->is_active = TRUE;
15475 if (!(IN_LEV_FIELD(nextx, nexty) &&
15476 (IS_FREE(nextx, nexty) ||
15477 (IS_SB_ELEMENT(element) &&
15478 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15479 (IS_CUSTOM_ELEMENT(element) &&
15480 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15481 return MP_NO_ACTION;
15483 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15484 return MP_NO_ACTION;
15486 if (player->push_delay == -1) /* new pushing; restart delay */
15487 player->push_delay = 0;
15489 if (player->push_delay < player->push_delay_value &&
15490 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15491 element != EL_SPRING && element != EL_BALLOON)
15493 /* make sure that there is no move delay before next try to push */
15494 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15495 player->move_delay = 0;
15497 return MP_NO_ACTION;
15500 if (IS_CUSTOM_ELEMENT(element) &&
15501 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15503 if (!DigFieldByCE(nextx, nexty, element))
15504 return MP_NO_ACTION;
15507 if (IS_SB_ELEMENT(element))
15509 if (element == EL_SOKOBAN_FIELD_FULL)
15511 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15512 local_player->sokobanfields_still_needed++;
15515 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15517 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15518 local_player->sokobanfields_still_needed--;
15521 Feld[x][y] = EL_SOKOBAN_OBJECT;
15523 if (Back[x][y] == Back[nextx][nexty])
15524 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15525 else if (Back[x][y] != 0)
15526 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15529 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15533 if (local_player->sokobanfields_still_needed == 0 &&
15534 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15536 if (local_player->sokobanfields_still_needed == 0 &&
15537 game.emulation == EMU_SOKOBAN)
15540 PlayerWins(player);
15542 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15546 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15548 InitMovingField(x, y, move_direction);
15549 GfxAction[x][y] = ACTION_PUSHING;
15551 if (mode == DF_SNAP)
15552 ContinueMoving(x, y);
15554 MovPos[x][y] = (dx != 0 ? dx : dy);
15556 Pushed[x][y] = TRUE;
15557 Pushed[nextx][nexty] = TRUE;
15559 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15560 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15562 player->push_delay_value = -1; /* get new value later */
15564 /* check for element change _after_ element has been pushed */
15565 if (game.use_change_when_pushing_bug)
15567 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15568 player->index_bit, dig_side);
15569 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15570 player->index_bit, dig_side);
15573 else if (IS_SWITCHABLE(element))
15575 if (PLAYER_SWITCHING(player, x, y))
15577 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15578 player->index_bit, dig_side);
15583 player->is_switching = TRUE;
15584 player->switch_x = x;
15585 player->switch_y = y;
15587 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15589 if (element == EL_ROBOT_WHEEL)
15591 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15595 game.robot_wheel_active = TRUE;
15597 TEST_DrawLevelField(x, y);
15599 else if (element == EL_SP_TERMINAL)
15603 SCAN_PLAYFIELD(xx, yy)
15605 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15607 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15608 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15611 else if (IS_BELT_SWITCH(element))
15613 ToggleBeltSwitch(x, y);
15615 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15616 element == EL_SWITCHGATE_SWITCH_DOWN ||
15617 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15618 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15620 ToggleSwitchgateSwitch(x, y);
15622 else if (element == EL_LIGHT_SWITCH ||
15623 element == EL_LIGHT_SWITCH_ACTIVE)
15625 ToggleLightSwitch(x, y);
15627 else if (element == EL_TIMEGATE_SWITCH ||
15628 element == EL_DC_TIMEGATE_SWITCH)
15630 ActivateTimegateSwitch(x, y);
15632 else if (element == EL_BALLOON_SWITCH_LEFT ||
15633 element == EL_BALLOON_SWITCH_RIGHT ||
15634 element == EL_BALLOON_SWITCH_UP ||
15635 element == EL_BALLOON_SWITCH_DOWN ||
15636 element == EL_BALLOON_SWITCH_NONE ||
15637 element == EL_BALLOON_SWITCH_ANY)
15639 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15640 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15641 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15642 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15643 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15646 else if (element == EL_LAMP)
15648 Feld[x][y] = EL_LAMP_ACTIVE;
15649 local_player->lights_still_needed--;
15651 ResetGfxAnimation(x, y);
15652 TEST_DrawLevelField(x, y);
15654 else if (element == EL_TIME_ORB_FULL)
15656 Feld[x][y] = EL_TIME_ORB_EMPTY;
15658 if (level.time > 0 || level.use_time_orb_bug)
15660 TimeLeft += level.time_orb_time;
15661 game.no_time_limit = FALSE;
15664 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15666 DisplayGameControlValues();
15668 DrawGameValue_Time(TimeLeft);
15672 ResetGfxAnimation(x, y);
15673 TEST_DrawLevelField(x, y);
15675 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15676 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15680 game.ball_state = !game.ball_state;
15682 SCAN_PLAYFIELD(xx, yy)
15684 int e = Feld[xx][yy];
15686 if (game.ball_state)
15688 if (e == EL_EMC_MAGIC_BALL)
15689 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15690 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15691 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15695 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15696 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15697 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15698 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15703 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15704 player->index_bit, dig_side);
15706 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15707 player->index_bit, dig_side);
15709 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15710 player->index_bit, dig_side);
15716 if (!PLAYER_SWITCHING(player, x, y))
15718 player->is_switching = TRUE;
15719 player->switch_x = x;
15720 player->switch_y = y;
15722 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15723 player->index_bit, dig_side);
15724 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15725 player->index_bit, dig_side);
15727 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15728 player->index_bit, dig_side);
15729 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15730 player->index_bit, dig_side);
15733 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15734 player->index_bit, dig_side);
15735 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15736 player->index_bit, dig_side);
15738 return MP_NO_ACTION;
15741 player->push_delay = -1;
15743 if (is_player) /* function can also be called by EL_PENGUIN */
15745 if (Feld[x][y] != element) /* really digged/collected something */
15747 player->is_collecting = !player->is_digging;
15748 player->is_active = TRUE;
15755 static boolean DigFieldByCE(int x, int y, int digging_element)
15757 int element = Feld[x][y];
15759 if (!IS_FREE(x, y))
15761 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15762 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15765 /* no element can dig solid indestructible elements */
15766 if (IS_INDESTRUCTIBLE(element) &&
15767 !IS_DIGGABLE(element) &&
15768 !IS_COLLECTIBLE(element))
15771 if (AmoebaNr[x][y] &&
15772 (element == EL_AMOEBA_FULL ||
15773 element == EL_BD_AMOEBA ||
15774 element == EL_AMOEBA_GROWING))
15776 AmoebaCnt[AmoebaNr[x][y]]--;
15777 AmoebaCnt2[AmoebaNr[x][y]]--;
15780 if (IS_MOVING(x, y))
15781 RemoveMovingField(x, y);
15785 TEST_DrawLevelField(x, y);
15788 /* if digged element was about to explode, prevent the explosion */
15789 ExplodeField[x][y] = EX_TYPE_NONE;
15791 PlayLevelSoundAction(x, y, action);
15794 Store[x][y] = EL_EMPTY;
15797 /* this makes it possible to leave the removed element again */
15798 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15799 Store[x][y] = element;
15801 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15803 int move_leave_element = element_info[digging_element].move_leave_element;
15805 /* this makes it possible to leave the removed element again */
15806 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15807 element : move_leave_element);
15814 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15816 int jx = player->jx, jy = player->jy;
15817 int x = jx + dx, y = jy + dy;
15818 int snap_direction = (dx == -1 ? MV_LEFT :
15819 dx == +1 ? MV_RIGHT :
15821 dy == +1 ? MV_DOWN : MV_NONE);
15822 boolean can_continue_snapping = (level.continuous_snapping &&
15823 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15825 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15828 if (!player->active || !IN_LEV_FIELD(x, y))
15836 if (player->MovPos == 0)
15837 player->is_pushing = FALSE;
15839 player->is_snapping = FALSE;
15841 if (player->MovPos == 0)
15843 player->is_moving = FALSE;
15844 player->is_digging = FALSE;
15845 player->is_collecting = FALSE;
15851 #if USE_NEW_CONTINUOUS_SNAPPING
15852 /* prevent snapping with already pressed snap key when not allowed */
15853 if (player->is_snapping && !can_continue_snapping)
15856 if (player->is_snapping)
15860 player->MovDir = snap_direction;
15862 if (player->MovPos == 0)
15864 player->is_moving = FALSE;
15865 player->is_digging = FALSE;
15866 player->is_collecting = FALSE;
15869 player->is_dropping = FALSE;
15870 player->is_dropping_pressed = FALSE;
15871 player->drop_pressed_delay = 0;
15873 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15876 player->is_snapping = TRUE;
15877 player->is_active = TRUE;
15879 if (player->MovPos == 0)
15881 player->is_moving = FALSE;
15882 player->is_digging = FALSE;
15883 player->is_collecting = FALSE;
15886 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15887 TEST_DrawLevelField(player->last_jx, player->last_jy);
15889 TEST_DrawLevelField(x, y);
15894 static boolean DropElement(struct PlayerInfo *player)
15896 int old_element, new_element;
15897 int dropx = player->jx, dropy = player->jy;
15898 int drop_direction = player->MovDir;
15899 int drop_side = drop_direction;
15901 int drop_element = get_next_dropped_element(player);
15903 int drop_element = (player->inventory_size > 0 ?
15904 player->inventory_element[player->inventory_size - 1] :
15905 player->inventory_infinite_element != EL_UNDEFINED ?
15906 player->inventory_infinite_element :
15907 player->dynabombs_left > 0 ?
15908 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15912 player->is_dropping_pressed = TRUE;
15914 /* do not drop an element on top of another element; when holding drop key
15915 pressed without moving, dropped element must move away before the next
15916 element can be dropped (this is especially important if the next element
15917 is dynamite, which can be placed on background for historical reasons) */
15918 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15921 if (IS_THROWABLE(drop_element))
15923 dropx += GET_DX_FROM_DIR(drop_direction);
15924 dropy += GET_DY_FROM_DIR(drop_direction);
15926 if (!IN_LEV_FIELD(dropx, dropy))
15930 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15931 new_element = drop_element; /* default: no change when dropping */
15933 /* check if player is active, not moving and ready to drop */
15934 if (!player->active || player->MovPos || player->drop_delay > 0)
15937 /* check if player has anything that can be dropped */
15938 if (new_element == EL_UNDEFINED)
15941 /* check if drop key was pressed long enough for EM style dynamite */
15942 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15945 /* check if anything can be dropped at the current position */
15946 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15949 /* collected custom elements can only be dropped on empty fields */
15950 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15953 if (old_element != EL_EMPTY)
15954 Back[dropx][dropy] = old_element; /* store old element on this field */
15956 ResetGfxAnimation(dropx, dropy);
15957 ResetRandomAnimationValue(dropx, dropy);
15959 if (player->inventory_size > 0 ||
15960 player->inventory_infinite_element != EL_UNDEFINED)
15962 if (player->inventory_size > 0)
15964 player->inventory_size--;
15966 DrawGameDoorValues();
15968 if (new_element == EL_DYNAMITE)
15969 new_element = EL_DYNAMITE_ACTIVE;
15970 else if (new_element == EL_EM_DYNAMITE)
15971 new_element = EL_EM_DYNAMITE_ACTIVE;
15972 else if (new_element == EL_SP_DISK_RED)
15973 new_element = EL_SP_DISK_RED_ACTIVE;
15976 Feld[dropx][dropy] = new_element;
15978 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15979 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15980 el2img(Feld[dropx][dropy]), 0);
15982 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15984 /* needed if previous element just changed to "empty" in the last frame */
15985 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15987 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15988 player->index_bit, drop_side);
15989 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15991 player->index_bit, drop_side);
15993 TestIfElementTouchesCustomElement(dropx, dropy);
15995 else /* player is dropping a dyna bomb */
15997 player->dynabombs_left--;
15999 Feld[dropx][dropy] = new_element;
16001 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16002 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16003 el2img(Feld[dropx][dropy]), 0);
16005 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16008 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16009 InitField_WithBug1(dropx, dropy, FALSE);
16011 new_element = Feld[dropx][dropy]; /* element might have changed */
16013 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16014 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16017 int move_direction;
16021 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16022 MovDir[dropx][dropy] = drop_direction;
16025 move_direction = MovDir[dropx][dropy];
16026 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16027 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16030 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16032 #if USE_FIX_IMPACT_COLLISION
16033 /* do not cause impact style collision by dropping elements that can fall */
16034 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16036 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16040 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16041 player->is_dropping = TRUE;
16043 player->drop_pressed_delay = 0;
16044 player->is_dropping_pressed = FALSE;
16046 player->drop_x = dropx;
16047 player->drop_y = dropy;
16052 /* ------------------------------------------------------------------------- */
16053 /* game sound playing functions */
16054 /* ------------------------------------------------------------------------- */
16056 static int *loop_sound_frame = NULL;
16057 static int *loop_sound_volume = NULL;
16059 void InitPlayLevelSound()
16061 int num_sounds = getSoundListSize();
16063 checked_free(loop_sound_frame);
16064 checked_free(loop_sound_volume);
16066 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16067 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16070 static void PlayLevelSound(int x, int y, int nr)
16072 int sx = SCREENX(x), sy = SCREENY(y);
16073 int volume, stereo_position;
16074 int max_distance = 8;
16075 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16077 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16078 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16081 if (!IN_LEV_FIELD(x, y) ||
16082 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16083 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16086 volume = SOUND_MAX_VOLUME;
16088 if (!IN_SCR_FIELD(sx, sy))
16090 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16091 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16093 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16096 stereo_position = (SOUND_MAX_LEFT +
16097 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16098 (SCR_FIELDX + 2 * max_distance));
16100 if (IS_LOOP_SOUND(nr))
16102 /* This assures that quieter loop sounds do not overwrite louder ones,
16103 while restarting sound volume comparison with each new game frame. */
16105 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16108 loop_sound_volume[nr] = volume;
16109 loop_sound_frame[nr] = FrameCounter;
16112 PlaySoundExt(nr, volume, stereo_position, type);
16115 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16117 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16118 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16119 y < LEVELY(BY1) ? LEVELY(BY1) :
16120 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16124 static void PlayLevelSoundAction(int x, int y, int action)
16126 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16129 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16131 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16133 if (sound_effect != SND_UNDEFINED)
16134 PlayLevelSound(x, y, sound_effect);
16137 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16140 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16142 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16143 PlayLevelSound(x, y, sound_effect);
16146 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16148 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16150 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16151 PlayLevelSound(x, y, sound_effect);
16154 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16156 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16158 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16159 StopSound(sound_effect);
16162 static void PlayLevelMusic()
16164 if (levelset.music[level_nr] != MUS_UNDEFINED)
16165 PlayMusic(levelset.music[level_nr]); /* from config file */
16167 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16170 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16172 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16173 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16174 int x = xx - 1 - offset;
16175 int y = yy - 1 - offset;
16180 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16184 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16188 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16192 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16196 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16200 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16204 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16207 case SAMPLE_android_clone:
16208 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16211 case SAMPLE_android_move:
16212 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16215 case SAMPLE_spring:
16216 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16220 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16224 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16227 case SAMPLE_eater_eat:
16228 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16232 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16235 case SAMPLE_collect:
16236 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16239 case SAMPLE_diamond:
16240 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16243 case SAMPLE_squash:
16244 /* !!! CHECK THIS !!! */
16246 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16248 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16252 case SAMPLE_wonderfall:
16253 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16257 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16261 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16265 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16269 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16273 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16277 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16280 case SAMPLE_wonder:
16281 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16285 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16288 case SAMPLE_exit_open:
16289 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16292 case SAMPLE_exit_leave:
16293 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16296 case SAMPLE_dynamite:
16297 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16301 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16305 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16309 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16313 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16317 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16321 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16325 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16330 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16332 int element = map_element_SP_to_RND(element_sp);
16333 int action = map_action_SP_to_RND(action_sp);
16334 int offset = (setup.sp_show_border_elements ? 0 : 1);
16335 int x = xx - offset;
16336 int y = yy - offset;
16339 printf("::: %d -> %d\n", element_sp, action_sp);
16342 PlayLevelSoundElementAction(x, y, element, action);
16345 void RaiseScore(int value)
16347 local_player->score += value;
16350 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16352 DisplayGameControlValues();
16354 DrawGameValue_Score(local_player->score);
16358 void RaiseScoreElement(int element)
16363 case EL_BD_DIAMOND:
16364 case EL_EMERALD_YELLOW:
16365 case EL_EMERALD_RED:
16366 case EL_EMERALD_PURPLE:
16367 case EL_SP_INFOTRON:
16368 RaiseScore(level.score[SC_EMERALD]);
16371 RaiseScore(level.score[SC_DIAMOND]);
16374 RaiseScore(level.score[SC_CRYSTAL]);
16377 RaiseScore(level.score[SC_PEARL]);
16380 case EL_BD_BUTTERFLY:
16381 case EL_SP_ELECTRON:
16382 RaiseScore(level.score[SC_BUG]);
16385 case EL_BD_FIREFLY:
16386 case EL_SP_SNIKSNAK:
16387 RaiseScore(level.score[SC_SPACESHIP]);
16390 case EL_DARK_YAMYAM:
16391 RaiseScore(level.score[SC_YAMYAM]);
16394 RaiseScore(level.score[SC_ROBOT]);
16397 RaiseScore(level.score[SC_PACMAN]);
16400 RaiseScore(level.score[SC_NUT]);
16403 case EL_EM_DYNAMITE:
16404 case EL_SP_DISK_RED:
16405 case EL_DYNABOMB_INCREASE_NUMBER:
16406 case EL_DYNABOMB_INCREASE_SIZE:
16407 case EL_DYNABOMB_INCREASE_POWER:
16408 RaiseScore(level.score[SC_DYNAMITE]);
16410 case EL_SHIELD_NORMAL:
16411 case EL_SHIELD_DEADLY:
16412 RaiseScore(level.score[SC_SHIELD]);
16414 case EL_EXTRA_TIME:
16415 RaiseScore(level.extra_time_score);
16429 case EL_DC_KEY_WHITE:
16430 RaiseScore(level.score[SC_KEY]);
16433 RaiseScore(element_info[element].collect_score);
16438 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16440 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16442 #if defined(NETWORK_AVALIABLE)
16443 if (options.network)
16444 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16453 FadeSkipNextFadeIn();
16455 fading = fading_none;
16459 OpenDoor(DOOR_CLOSE_1);
16462 game_status = GAME_MODE_MAIN;
16465 DrawAndFadeInMainMenu(REDRAW_FIELD);
16473 FadeOut(REDRAW_FIELD);
16476 game_status = GAME_MODE_MAIN;
16478 DrawAndFadeInMainMenu(REDRAW_FIELD);
16482 else /* continue playing the game */
16484 if (tape.playing && tape.deactivate_display)
16485 TapeDeactivateDisplayOff(TRUE);
16487 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16489 if (tape.playing && tape.deactivate_display)
16490 TapeDeactivateDisplayOn();
16494 void RequestQuitGame(boolean ask_if_really_quit)
16496 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16497 boolean skip_request = AllPlayersGone || quick_quit;
16499 RequestQuitGameExt(skip_request, quick_quit,
16500 "Do you really want to quit the game?");
16504 /* ------------------------------------------------------------------------- */
16505 /* random generator functions */
16506 /* ------------------------------------------------------------------------- */
16508 unsigned int InitEngineRandom_RND(int seed)
16510 game.num_random_calls = 0;
16513 unsigned int rnd_seed = InitEngineRandom(seed);
16515 printf("::: START RND: %d\n", rnd_seed);
16520 return InitEngineRandom(seed);
16526 unsigned int RND(int max)
16530 game.num_random_calls++;
16532 return GetEngineRandom(max);
16539 /* ------------------------------------------------------------------------- */
16540 /* game engine snapshot handling functions */
16541 /* ------------------------------------------------------------------------- */
16543 struct EngineSnapshotInfo
16545 /* runtime values for custom element collect score */
16546 int collect_score[NUM_CUSTOM_ELEMENTS];
16548 /* runtime values for group element choice position */
16549 int choice_pos[NUM_GROUP_ELEMENTS];
16551 /* runtime values for belt position animations */
16552 int belt_graphic[4][NUM_BELT_PARTS];
16553 int belt_anim_mode[4][NUM_BELT_PARTS];
16556 static struct EngineSnapshotInfo engine_snapshot_rnd;
16557 static char *snapshot_level_identifier = NULL;
16558 static int snapshot_level_nr = -1;
16560 static void SaveEngineSnapshotValues_RND()
16562 static int belt_base_active_element[4] =
16564 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16565 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16566 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16567 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16571 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16573 int element = EL_CUSTOM_START + i;
16575 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16578 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16580 int element = EL_GROUP_START + i;
16582 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16585 for (i = 0; i < 4; i++)
16587 for (j = 0; j < NUM_BELT_PARTS; j++)
16589 int element = belt_base_active_element[i] + j;
16590 int graphic = el2img(element);
16591 int anim_mode = graphic_info[graphic].anim_mode;
16593 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16594 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16599 static void LoadEngineSnapshotValues_RND()
16601 unsigned int num_random_calls = game.num_random_calls;
16604 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16606 int element = EL_CUSTOM_START + i;
16608 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16611 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16613 int element = EL_GROUP_START + i;
16615 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16618 for (i = 0; i < 4; i++)
16620 for (j = 0; j < NUM_BELT_PARTS; j++)
16622 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16623 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16625 graphic_info[graphic].anim_mode = anim_mode;
16629 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16631 InitRND(tape.random_seed);
16632 for (i = 0; i < num_random_calls; i++)
16636 if (game.num_random_calls != num_random_calls)
16638 Error(ERR_INFO, "number of random calls out of sync");
16639 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16640 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16641 Error(ERR_EXIT, "this should not happen -- please debug");
16645 void SaveEngineSnapshot()
16647 /* do not save snapshots from editor */
16648 if (level_editor_test_game)
16651 /* free previous snapshot buffers, if needed */
16652 FreeEngineSnapshotBuffers();
16654 /* copy some special values to a structure better suited for the snapshot */
16656 SaveEngineSnapshotValues_RND();
16657 SaveEngineSnapshotValues_EM();
16658 SaveEngineSnapshotValues_SP();
16660 /* save values stored in special snapshot structure */
16662 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16666 /* save further RND engine values */
16668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16670 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16672 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16674 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16677 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16691 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16699 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16723 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16726 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16732 /* save level identification information */
16734 setString(&snapshot_level_identifier, leveldir_current->identifier);
16735 snapshot_level_nr = level_nr;
16738 ListNode *node = engine_snapshot_list_rnd;
16741 while (node != NULL)
16743 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16748 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16752 void LoadEngineSnapshot()
16754 /* restore generically stored snapshot buffers */
16756 LoadEngineSnapshotBuffers();
16758 /* restore special values from snapshot structure */
16760 LoadEngineSnapshotValues_RND();
16761 LoadEngineSnapshotValues_EM();
16762 LoadEngineSnapshotValues_SP();
16765 boolean CheckEngineSnapshot()
16767 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16768 snapshot_level_nr == level_nr);
16772 /* ---------- new game button stuff ---------------------------------------- */
16780 } gamebutton_info[NUM_GAME_BUTTONS] =
16783 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16784 GAME_CTRL_ID_STOP, "stop game"
16787 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16788 GAME_CTRL_ID_PAUSE, "pause game"
16791 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16792 GAME_CTRL_ID_PLAY, "play game"
16795 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16796 SOUND_CTRL_ID_MUSIC, "background music on/off"
16799 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16800 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16803 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16804 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16808 void CreateGameButtons()
16812 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16814 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16815 struct Rect *pos = gamebutton_info[i].pos;
16816 struct GadgetInfo *gi;
16819 unsigned int event_mask;
16820 int gd_x = gfx->src_x;
16821 int gd_y = gfx->src_y;
16822 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16823 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16824 int gd_xa = gfx->src_x + gfx->active_xoffset;
16825 int gd_ya = gfx->src_y + gfx->active_yoffset;
16826 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16827 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16830 if (id == GAME_CTRL_ID_STOP ||
16831 id == GAME_CTRL_ID_PAUSE ||
16832 id == GAME_CTRL_ID_PLAY)
16834 button_type = GD_TYPE_NORMAL_BUTTON;
16836 event_mask = GD_EVENT_RELEASED;
16840 button_type = GD_TYPE_CHECK_BUTTON;
16842 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16843 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16844 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16845 event_mask = GD_EVENT_PRESSED;
16848 gi = CreateGadget(GDI_CUSTOM_ID, id,
16849 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16850 GDI_X, DX + pos->x,
16851 GDI_Y, DY + pos->y,
16852 GDI_WIDTH, gfx->width,
16853 GDI_HEIGHT, gfx->height,
16854 GDI_TYPE, button_type,
16855 GDI_STATE, GD_BUTTON_UNPRESSED,
16856 GDI_CHECKED, checked,
16857 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16858 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16859 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16860 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16861 GDI_DIRECT_DRAW, FALSE,
16862 GDI_EVENT_MASK, event_mask,
16863 GDI_CALLBACK_ACTION, HandleGameButtons,
16867 Error(ERR_EXIT, "cannot create gadget");
16869 game_gadget[id] = gi;
16873 void FreeGameButtons()
16877 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878 FreeGadget(game_gadget[i]);
16881 static void MapGameButtons()
16885 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16886 MapGadget(game_gadget[i]);
16889 void UnmapGameButtons()
16893 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16894 UnmapGadget(game_gadget[i]);
16897 void RedrawGameButtons()
16901 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16902 RedrawGadget(game_gadget[i]);
16905 static void HandleGameButtonsExt(int id)
16907 if (game_status != GAME_MODE_PLAYING)
16912 case GAME_CTRL_ID_STOP:
16916 RequestQuitGame(TRUE);
16919 case GAME_CTRL_ID_PAUSE:
16920 if (options.network)
16922 #if defined(NETWORK_AVALIABLE)
16924 SendToServer_ContinuePlaying();
16926 SendToServer_PausePlaying();
16930 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16933 case GAME_CTRL_ID_PLAY:
16936 #if defined(NETWORK_AVALIABLE)
16937 if (options.network)
16938 SendToServer_ContinuePlaying();
16942 tape.pausing = FALSE;
16943 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16948 case SOUND_CTRL_ID_MUSIC:
16949 if (setup.sound_music)
16951 setup.sound_music = FALSE;
16955 else if (audio.music_available)
16957 setup.sound = setup.sound_music = TRUE;
16959 SetAudioMode(setup.sound);
16965 case SOUND_CTRL_ID_LOOPS:
16966 if (setup.sound_loops)
16967 setup.sound_loops = FALSE;
16968 else if (audio.loops_available)
16970 setup.sound = setup.sound_loops = TRUE;
16972 SetAudioMode(setup.sound);
16976 case SOUND_CTRL_ID_SIMPLE:
16977 if (setup.sound_simple)
16978 setup.sound_simple = FALSE;
16979 else if (audio.sound_available)
16981 setup.sound = setup.sound_simple = TRUE;
16983 SetAudioMode(setup.sound);
16992 static void HandleGameButtons(struct GadgetInfo *gi)
16994 HandleGameButtonsExt(gi->custom_id);
16997 void HandleSoundButtonKeys(Key key)
17000 if (key == setup.shortcut.sound_simple)
17001 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17002 else if (key == setup.shortcut.sound_loops)
17003 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17004 else if (key == setup.shortcut.sound_music)
17005 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17007 if (key == setup.shortcut.sound_simple)
17008 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17009 else if (key == setup.shortcut.sound_loops)
17010 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17011 else if (key == setup.shortcut.sound_music)
17012 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);