1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_PANEL_LEVEL_NUMBER 0
135 #define GAME_PANEL_GEMS 1
136 #define GAME_PANEL_INVENTORY_COUNT 2
137 #define GAME_PANEL_INVENTORY_FIRST_1 3
138 #define GAME_PANEL_INVENTORY_FIRST_2 4
139 #define GAME_PANEL_INVENTORY_FIRST_3 5
140 #define GAME_PANEL_INVENTORY_FIRST_4 6
141 #define GAME_PANEL_INVENTORY_FIRST_5 7
142 #define GAME_PANEL_INVENTORY_FIRST_6 8
143 #define GAME_PANEL_INVENTORY_FIRST_7 9
144 #define GAME_PANEL_INVENTORY_FIRST_8 10
145 #define GAME_PANEL_INVENTORY_LAST_1 11
146 #define GAME_PANEL_INVENTORY_LAST_2 12
147 #define GAME_PANEL_INVENTORY_LAST_3 13
148 #define GAME_PANEL_INVENTORY_LAST_4 14
149 #define GAME_PANEL_INVENTORY_LAST_5 15
150 #define GAME_PANEL_INVENTORY_LAST_6 16
151 #define GAME_PANEL_INVENTORY_LAST_7 17
152 #define GAME_PANEL_INVENTORY_LAST_8 18
153 #define GAME_PANEL_KEY_1 19
154 #define GAME_PANEL_KEY_2 20
155 #define GAME_PANEL_KEY_3 21
156 #define GAME_PANEL_KEY_4 22
157 #define GAME_PANEL_KEY_5 23
158 #define GAME_PANEL_KEY_6 24
159 #define GAME_PANEL_KEY_7 25
160 #define GAME_PANEL_KEY_8 26
161 #define GAME_PANEL_KEY_WHITE 27
162 #define GAME_PANEL_KEY_WHITE_COUNT 28
163 #define GAME_PANEL_SCORE 29
164 #define GAME_PANEL_TIME 30
165 #define GAME_PANEL_TIME_HH 31
166 #define GAME_PANEL_TIME_MM 32
167 #define GAME_PANEL_TIME_SS 33
168 #define GAME_PANEL_SHIELD_NORMAL 34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME 35
170 #define GAME_PANEL_SHIELD_DEADLY 36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME 37
172 #define GAME_PANEL_EXIT 38
173 #define GAME_PANEL_EMC_MAGIC_BALL 39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 40
175 #define GAME_PANEL_LIGHT_SWITCH 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME 42
177 #define GAME_PANEL_TIMEGATE_SWITCH 43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_PANEL_SWITCHGATE_SWITCH 45
180 #define GAME_PANEL_EMC_LENSES 46
181 #define GAME_PANEL_EMC_LENSES_TIME 47
182 #define GAME_PANEL_EMC_MAGNIFIER 48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME 49
184 #define GAME_PANEL_BALLOON_SWITCH 50
185 #define GAME_PANEL_DYNABOMB_NUMBER 51
186 #define GAME_PANEL_DYNABOMB_SIZE 52
187 #define GAME_PANEL_DYNABOMB_POWER 53
188 #define GAME_PANEL_PENGUINS 54
189 #define GAME_PANEL_SOKOBAN_OBJECTS 55
190 #define GAME_PANEL_SOKOBAN_FIELDS 56
191 #define GAME_PANEL_ROBOT_WHEEL 57
192 #define GAME_PANEL_CONVEYOR_BELT_1 58
193 #define GAME_PANEL_CONVEYOR_BELT_2 59
194 #define GAME_PANEL_CONVEYOR_BELT_3 60
195 #define GAME_PANEL_CONVEYOR_BELT_4 61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 65
200 #define GAME_PANEL_MAGIC_WALL 66
201 #define GAME_PANEL_MAGIC_WALL_TIME 67
202 #define GAME_PANEL_GRAVITY_STATE 68
203 #define GAME_PANEL_CE_SCORE_1 69
204 #define GAME_PANEL_CE_SCORE_2 70
205 #define GAME_PANEL_CE_SCORE_3 71
206 #define GAME_PANEL_CE_SCORE_4 72
207 #define GAME_PANEL_CE_SCORE_5 73
208 #define GAME_PANEL_CE_SCORE_6 74
209 #define GAME_PANEL_CE_SCORE_7 75
210 #define GAME_PANEL_CE_SCORE_8 76
211 #define GAME_PANEL_CE_SCORE_1_ELEMENT 77
212 #define GAME_PANEL_CE_SCORE_2_ELEMENT 78
213 #define GAME_PANEL_CE_SCORE_3_ELEMENT 79
214 #define GAME_PANEL_CE_SCORE_4_ELEMENT 80
215 #define GAME_PANEL_CE_SCORE_5_ELEMENT 81
216 #define GAME_PANEL_CE_SCORE_6_ELEMENT 82
217 #define GAME_PANEL_CE_SCORE_7_ELEMENT 83
218 #define GAME_PANEL_CE_SCORE_8_ELEMENT 84
219 #define GAME_PANEL_PLAYER_NAME 85
220 #define GAME_PANEL_LEVEL_NAME 86
221 #define GAME_PANEL_LEVEL_AUTHOR 87
223 #define NUM_GAME_PANEL_CONTROLS 88
225 struct GamePanelControlInfo
229 struct TextPosInfo *pos;
232 int value, last_value;
233 int frame, last_frame;
237 static struct GamePanelControlInfo game_panel_controls[] =
240 GAME_PANEL_LEVEL_NUMBER,
241 &game.panel.level_number,
250 GAME_PANEL_INVENTORY_COUNT,
251 &game.panel.inventory_count,
255 GAME_PANEL_INVENTORY_FIRST_1,
256 &game.panel.inventory_first[0],
260 GAME_PANEL_INVENTORY_FIRST_2,
261 &game.panel.inventory_first[1],
265 GAME_PANEL_INVENTORY_FIRST_3,
266 &game.panel.inventory_first[2],
270 GAME_PANEL_INVENTORY_FIRST_4,
271 &game.panel.inventory_first[3],
275 GAME_PANEL_INVENTORY_FIRST_5,
276 &game.panel.inventory_first[4],
280 GAME_PANEL_INVENTORY_FIRST_6,
281 &game.panel.inventory_first[5],
285 GAME_PANEL_INVENTORY_FIRST_7,
286 &game.panel.inventory_first[6],
290 GAME_PANEL_INVENTORY_FIRST_8,
291 &game.panel.inventory_first[7],
295 GAME_PANEL_INVENTORY_LAST_1,
296 &game.panel.inventory_last[0],
300 GAME_PANEL_INVENTORY_LAST_2,
301 &game.panel.inventory_last[1],
305 GAME_PANEL_INVENTORY_LAST_3,
306 &game.panel.inventory_last[2],
310 GAME_PANEL_INVENTORY_LAST_4,
311 &game.panel.inventory_last[3],
315 GAME_PANEL_INVENTORY_LAST_5,
316 &game.panel.inventory_last[4],
320 GAME_PANEL_INVENTORY_LAST_6,
321 &game.panel.inventory_last[5],
325 GAME_PANEL_INVENTORY_LAST_7,
326 &game.panel.inventory_last[6],
330 GAME_PANEL_INVENTORY_LAST_8,
331 &game.panel.inventory_last[7],
375 GAME_PANEL_KEY_WHITE,
376 &game.panel.key_white,
380 GAME_PANEL_KEY_WHITE_COUNT,
381 &game.panel.key_white_count,
410 GAME_PANEL_SHIELD_NORMAL,
411 &game.panel.shield_normal,
415 GAME_PANEL_SHIELD_NORMAL_TIME,
416 &game.panel.shield_normal_time,
420 GAME_PANEL_SHIELD_DEADLY,
421 &game.panel.shield_deadly,
425 GAME_PANEL_SHIELD_DEADLY_TIME,
426 &game.panel.shield_deadly_time,
435 GAME_PANEL_EMC_MAGIC_BALL,
436 &game.panel.emc_magic_ball,
440 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441 &game.panel.emc_magic_ball_switch,
445 GAME_PANEL_LIGHT_SWITCH,
446 &game.panel.light_switch,
450 GAME_PANEL_LIGHT_SWITCH_TIME,
451 &game.panel.light_switch_time,
455 GAME_PANEL_TIMEGATE_SWITCH,
456 &game.panel.timegate_switch,
460 GAME_PANEL_TIMEGATE_SWITCH_TIME,
461 &game.panel.timegate_switch_time,
465 GAME_PANEL_SWITCHGATE_SWITCH,
466 &game.panel.switchgate_switch,
470 GAME_PANEL_EMC_LENSES,
471 &game.panel.emc_lenses,
475 GAME_PANEL_EMC_LENSES_TIME,
476 &game.panel.emc_lenses_time,
480 GAME_PANEL_EMC_MAGNIFIER,
481 &game.panel.emc_magnifier,
485 GAME_PANEL_EMC_MAGNIFIER_TIME,
486 &game.panel.emc_magnifier_time,
490 GAME_PANEL_BALLOON_SWITCH,
491 &game.panel.balloon_switch,
495 GAME_PANEL_DYNABOMB_NUMBER,
496 &game.panel.dynabomb_number,
500 GAME_PANEL_DYNABOMB_SIZE,
501 &game.panel.dynabomb_size,
505 GAME_PANEL_DYNABOMB_POWER,
506 &game.panel.dynabomb_power,
511 &game.panel.penguins,
515 GAME_PANEL_SOKOBAN_OBJECTS,
516 &game.panel.sokoban_objects,
520 GAME_PANEL_SOKOBAN_FIELDS,
521 &game.panel.sokoban_fields,
525 GAME_PANEL_ROBOT_WHEEL,
526 &game.panel.robot_wheel,
530 GAME_PANEL_CONVEYOR_BELT_1,
531 &game.panel.conveyor_belt[0],
535 GAME_PANEL_CONVEYOR_BELT_2,
536 &game.panel.conveyor_belt[1],
540 GAME_PANEL_CONVEYOR_BELT_3,
541 &game.panel.conveyor_belt[2],
545 GAME_PANEL_CONVEYOR_BELT_4,
546 &game.panel.conveyor_belt[3],
550 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551 &game.panel.conveyor_belt_switch[0],
555 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556 &game.panel.conveyor_belt_switch[1],
560 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561 &game.panel.conveyor_belt_switch[2],
565 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566 &game.panel.conveyor_belt_switch[3],
570 GAME_PANEL_MAGIC_WALL,
571 &game.panel.magic_wall,
575 GAME_PANEL_MAGIC_WALL_TIME,
576 &game.panel.magic_wall_time,
580 GAME_PANEL_GRAVITY_STATE,
581 &game.panel.gravity_state,
585 GAME_PANEL_CE_SCORE_1,
586 &game.panel.ce_score[0],
590 GAME_PANEL_CE_SCORE_2,
591 &game.panel.ce_score[1],
595 GAME_PANEL_CE_SCORE_3,
596 &game.panel.ce_score[2],
600 GAME_PANEL_CE_SCORE_4,
601 &game.panel.ce_score[3],
605 GAME_PANEL_CE_SCORE_5,
606 &game.panel.ce_score[4],
610 GAME_PANEL_CE_SCORE_6,
611 &game.panel.ce_score[5],
615 GAME_PANEL_CE_SCORE_7,
616 &game.panel.ce_score[6],
620 GAME_PANEL_CE_SCORE_8,
621 &game.panel.ce_score[7],
625 GAME_PANEL_CE_SCORE_1_ELEMENT,
626 &game.panel.ce_score_element[0],
630 GAME_PANEL_CE_SCORE_2_ELEMENT,
631 &game.panel.ce_score_element[1],
635 GAME_PANEL_CE_SCORE_3_ELEMENT,
636 &game.panel.ce_score_element[2],
640 GAME_PANEL_CE_SCORE_4_ELEMENT,
641 &game.panel.ce_score_element[3],
645 GAME_PANEL_CE_SCORE_5_ELEMENT,
646 &game.panel.ce_score_element[4],
650 GAME_PANEL_CE_SCORE_6_ELEMENT,
651 &game.panel.ce_score_element[5],
655 GAME_PANEL_CE_SCORE_7_ELEMENT,
656 &game.panel.ce_score_element[6],
660 GAME_PANEL_CE_SCORE_8_ELEMENT,
661 &game.panel.ce_score_element[7],
665 GAME_PANEL_PLAYER_NAME,
666 &game.panel.player_name,
670 GAME_PANEL_LEVEL_NAME,
671 &game.panel.level_name,
675 GAME_PANEL_LEVEL_AUTHOR,
676 &game.panel.level_author,
689 /* values for delayed check of falling and moving elements and for collision */
690 #define CHECK_DELAY_MOVING 3
691 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
692 #define CHECK_DELAY_COLLISION 2
693 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
695 /* values for initial player move delay (initial delay counter value) */
696 #define INITIAL_MOVE_DELAY_OFF -1
697 #define INITIAL_MOVE_DELAY_ON 0
699 /* values for player movement speed (which is in fact a delay value) */
700 #define MOVE_DELAY_MIN_SPEED 32
701 #define MOVE_DELAY_NORMAL_SPEED 8
702 #define MOVE_DELAY_HIGH_SPEED 4
703 #define MOVE_DELAY_MAX_SPEED 1
705 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
706 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
708 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
709 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
711 /* values for other actions */
712 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
713 #define MOVE_STEPSIZE_MIN (1)
714 #define MOVE_STEPSIZE_MAX (TILEX)
716 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
717 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
719 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
721 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
722 RND(element_info[e].push_delay_random))
723 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
724 RND(element_info[e].drop_delay_random))
725 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
726 RND(element_info[e].move_delay_random))
727 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
728 (element_info[e].move_delay_random))
729 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
730 RND(element_info[e].ce_value_random_initial))
731 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
732 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
733 RND((c)->delay_random * (c)->delay_frames))
734 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
735 RND((c)->delay_random))
738 #define GET_VALID_RUNTIME_ELEMENT(e) \
739 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
741 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
742 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
743 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
744 (be) + (e) - EL_SELF)
746 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
747 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
748 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
749 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
750 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
751 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
752 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
753 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
754 RESOLVED_REFERENCE_ELEMENT(be, e) : \
757 #define CAN_GROW_INTO(e) \
758 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
760 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
761 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
764 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
765 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
766 (CAN_MOVE_INTO_ACID(e) && \
767 Feld[x][y] == EL_ACID) || \
770 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
771 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
772 (CAN_MOVE_INTO_ACID(e) && \
773 Feld[x][y] == EL_ACID) || \
776 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
777 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
779 (CAN_MOVE_INTO_ACID(e) && \
780 Feld[x][y] == EL_ACID) || \
781 (DONT_COLLIDE_WITH(e) && \
783 !PLAYER_ENEMY_PROTECTED(x, y))))
785 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
786 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
788 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
789 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
791 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
792 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
794 #define ANDROID_CAN_CLONE_FIELD(x, y) \
795 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
796 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
798 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
799 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
801 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
802 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
804 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
805 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
807 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
808 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
810 #define PIG_CAN_ENTER_FIELD(e, x, y) \
811 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
813 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
814 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
815 Feld[x][y] == EL_EM_EXIT_OPEN || \
816 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
817 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
818 IS_FOOD_PENGUIN(Feld[x][y])))
819 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
820 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
822 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
823 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
825 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
826 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
828 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
829 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
830 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
832 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
834 #define CE_ENTER_FIELD_COND(e, x, y) \
835 (!IS_PLAYER(x, y) && \
836 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
838 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
839 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
841 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
842 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
844 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
845 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
846 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
847 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
849 /* game button identifiers */
850 #define GAME_CTRL_ID_STOP 0
851 #define GAME_CTRL_ID_PAUSE 1
852 #define GAME_CTRL_ID_PLAY 2
853 #define SOUND_CTRL_ID_MUSIC 3
854 #define SOUND_CTRL_ID_LOOPS 4
855 #define SOUND_CTRL_ID_SIMPLE 5
857 #define NUM_GAME_BUTTONS 6
860 /* forward declaration for internal use */
862 static void CreateField(int, int, int);
864 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
865 static void AdvanceFrameAndPlayerCounters(int);
867 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
868 static boolean MovePlayer(struct PlayerInfo *, int, int);
869 static void ScrollPlayer(struct PlayerInfo *, int);
870 static void ScrollScreen(struct PlayerInfo *, int);
872 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
874 static void InitBeltMovement(void);
875 static void CloseAllOpenTimegates(void);
876 static void CheckGravityMovement(struct PlayerInfo *);
877 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
878 static void KillPlayerUnlessEnemyProtected(int, int);
879 static void KillPlayerUnlessExplosionProtected(int, int);
881 static void TestIfPlayerTouchesCustomElement(int, int);
882 static void TestIfElementTouchesCustomElement(int, int);
883 static void TestIfElementHitsCustomElement(int, int, int);
885 static void TestIfElementSmashesCustomElement(int, int, int);
888 static void HandleElementChange(int, int, int);
889 static void ExecuteCustomElementAction(int, int, int, int);
890 static boolean ChangeElement(int, int, int, int);
892 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
893 #define CheckTriggeredElementChange(x, y, e, ev) \
894 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
895 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
896 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
897 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
898 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
899 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
900 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
902 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
903 #define CheckElementChange(x, y, e, te, ev) \
904 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
905 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
906 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
907 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
908 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
910 static void PlayLevelSound(int, int, int);
911 static void PlayLevelSoundNearest(int, int, int);
912 static void PlayLevelSoundAction(int, int, int);
913 static void PlayLevelSoundElementAction(int, int, int, int);
914 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
915 static void PlayLevelSoundActionIfLoop(int, int, int);
916 static void StopLevelSoundActionIfLoop(int, int, int);
917 static void PlayLevelMusic();
919 static void MapGameButtons();
920 static void HandleGameButtons(struct GadgetInfo *);
922 int AmoebeNachbarNr(int, int);
923 void AmoebeUmwandeln(int, int);
924 void ContinueMoving(int, int);
926 void InitMovDir(int, int);
927 void InitAmoebaNr(int, int);
928 int NewHiScore(void);
930 void TestIfGoodThingHitsBadThing(int, int, int);
931 void TestIfBadThingHitsGoodThing(int, int, int);
932 void TestIfPlayerTouchesBadThing(int, int);
933 void TestIfPlayerRunsIntoBadThing(int, int, int);
934 void TestIfBadThingTouchesPlayer(int, int);
935 void TestIfBadThingRunsIntoPlayer(int, int, int);
936 void TestIfFriendTouchesBadThing(int, int);
937 void TestIfBadThingTouchesFriend(int, int);
938 void TestIfBadThingTouchesOtherBadThing(int, int);
940 void KillPlayer(struct PlayerInfo *);
941 void BuryPlayer(struct PlayerInfo *);
942 void RemovePlayer(struct PlayerInfo *);
944 boolean SnapField(struct PlayerInfo *, int, int);
945 boolean DropElement(struct PlayerInfo *);
947 static int getInvisibleActiveFromInvisibleElement(int);
948 static int getInvisibleFromInvisibleActiveElement(int);
950 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
952 /* for detection of endless loops, caused by custom element programming */
953 /* (using maximal playfield width x 10 is just a rough approximation) */
954 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
956 #define RECURSION_LOOP_DETECTION_START(e, rc) \
958 if (recursion_loop_detected) \
961 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
963 recursion_loop_detected = TRUE; \
964 recursion_loop_element = (e); \
967 recursion_loop_depth++; \
970 #define RECURSION_LOOP_DETECTION_END() \
972 recursion_loop_depth--; \
975 static int recursion_loop_depth;
976 static boolean recursion_loop_detected;
977 static boolean recursion_loop_element;
980 /* ------------------------------------------------------------------------- */
981 /* definition of elements that automatically change to other elements after */
982 /* a specified time, eventually calling a function when changing */
983 /* ------------------------------------------------------------------------- */
985 /* forward declaration for changer functions */
986 static void InitBuggyBase(int, int);
987 static void WarnBuggyBase(int, int);
989 static void InitTrap(int, int);
990 static void ActivateTrap(int, int);
991 static void ChangeActiveTrap(int, int);
993 static void InitRobotWheel(int, int);
994 static void RunRobotWheel(int, int);
995 static void StopRobotWheel(int, int);
997 static void InitTimegateWheel(int, int);
998 static void RunTimegateWheel(int, int);
1000 static void InitMagicBallDelay(int, int);
1001 static void ActivateMagicBall(int, int);
1003 struct ChangingElementInfo
1008 void (*pre_change_function)(int x, int y);
1009 void (*change_function)(int x, int y);
1010 void (*post_change_function)(int x, int y);
1013 static struct ChangingElementInfo change_delay_list[] =
1048 EL_STEEL_EXIT_OPENING,
1056 EL_STEEL_EXIT_CLOSING,
1057 EL_STEEL_EXIT_CLOSED,
1084 EL_EM_STEEL_EXIT_OPENING,
1085 EL_EM_STEEL_EXIT_OPEN,
1092 EL_EM_STEEL_EXIT_CLOSING,
1096 EL_EM_STEEL_EXIT_CLOSED,
1120 EL_SWITCHGATE_OPENING,
1128 EL_SWITCHGATE_CLOSING,
1129 EL_SWITCHGATE_CLOSED,
1136 EL_TIMEGATE_OPENING,
1144 EL_TIMEGATE_CLOSING,
1153 EL_ACID_SPLASH_LEFT,
1161 EL_ACID_SPLASH_RIGHT,
1170 EL_SP_BUGGY_BASE_ACTIVATING,
1177 EL_SP_BUGGY_BASE_ACTIVATING,
1178 EL_SP_BUGGY_BASE_ACTIVE,
1185 EL_SP_BUGGY_BASE_ACTIVE,
1209 EL_ROBOT_WHEEL_ACTIVE,
1217 EL_TIMEGATE_SWITCH_ACTIVE,
1225 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1226 EL_DC_TIMEGATE_SWITCH,
1233 EL_EMC_MAGIC_BALL_ACTIVE,
1234 EL_EMC_MAGIC_BALL_ACTIVE,
1241 EL_EMC_SPRING_BUMPER_ACTIVE,
1242 EL_EMC_SPRING_BUMPER,
1249 EL_DIAGONAL_SHRINKING,
1257 EL_DIAGONAL_GROWING,
1278 int push_delay_fixed, push_delay_random;
1282 { EL_SPRING, 0, 0 },
1283 { EL_BALLOON, 0, 0 },
1285 { EL_SOKOBAN_OBJECT, 2, 0 },
1286 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1287 { EL_SATELLITE, 2, 0 },
1288 { EL_SP_DISK_YELLOW, 2, 0 },
1290 { EL_UNDEFINED, 0, 0 },
1298 move_stepsize_list[] =
1300 { EL_AMOEBA_DROP, 2 },
1301 { EL_AMOEBA_DROPPING, 2 },
1302 { EL_QUICKSAND_FILLING, 1 },
1303 { EL_QUICKSAND_EMPTYING, 1 },
1304 { EL_QUICKSAND_FAST_FILLING, 2 },
1305 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1306 { EL_MAGIC_WALL_FILLING, 2 },
1307 { EL_MAGIC_WALL_EMPTYING, 2 },
1308 { EL_BD_MAGIC_WALL_FILLING, 2 },
1309 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1310 { EL_DC_MAGIC_WALL_FILLING, 2 },
1311 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1313 { EL_UNDEFINED, 0 },
1321 collect_count_list[] =
1324 { EL_BD_DIAMOND, 1 },
1325 { EL_EMERALD_YELLOW, 1 },
1326 { EL_EMERALD_RED, 1 },
1327 { EL_EMERALD_PURPLE, 1 },
1329 { EL_SP_INFOTRON, 1 },
1333 { EL_UNDEFINED, 0 },
1341 access_direction_list[] =
1343 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1344 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1345 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1346 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1347 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1348 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1349 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1350 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1351 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1352 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1353 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1355 { EL_SP_PORT_LEFT, MV_RIGHT },
1356 { EL_SP_PORT_RIGHT, MV_LEFT },
1357 { EL_SP_PORT_UP, MV_DOWN },
1358 { EL_SP_PORT_DOWN, MV_UP },
1359 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1360 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1361 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1362 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1363 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1364 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1365 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1366 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1367 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1368 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1369 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1370 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1371 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1372 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1373 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1375 { EL_UNDEFINED, MV_NONE }
1378 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1380 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1381 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1382 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1383 IS_JUST_CHANGING(x, y))
1385 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1387 /* static variables for playfield scan mode (scanning forward or backward) */
1388 static int playfield_scan_start_x = 0;
1389 static int playfield_scan_start_y = 0;
1390 static int playfield_scan_delta_x = 1;
1391 static int playfield_scan_delta_y = 1;
1393 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1394 (y) >= 0 && (y) <= lev_fieldy - 1; \
1395 (y) += playfield_scan_delta_y) \
1396 for ((x) = playfield_scan_start_x; \
1397 (x) >= 0 && (x) <= lev_fieldx - 1; \
1398 (x) += playfield_scan_delta_x)
1401 void DEBUG_SetMaximumDynamite()
1405 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1406 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1407 local_player->inventory_element[local_player->inventory_size++] =
1412 static void InitPlayfieldScanModeVars()
1414 if (game.use_reverse_scan_direction)
1416 playfield_scan_start_x = lev_fieldx - 1;
1417 playfield_scan_start_y = lev_fieldy - 1;
1419 playfield_scan_delta_x = -1;
1420 playfield_scan_delta_y = -1;
1424 playfield_scan_start_x = 0;
1425 playfield_scan_start_y = 0;
1427 playfield_scan_delta_x = 1;
1428 playfield_scan_delta_y = 1;
1432 static void InitPlayfieldScanMode(int mode)
1434 game.use_reverse_scan_direction =
1435 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1437 InitPlayfieldScanModeVars();
1440 static int get_move_delay_from_stepsize(int move_stepsize)
1443 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1445 /* make sure that stepsize value is always a power of 2 */
1446 move_stepsize = (1 << log_2(move_stepsize));
1448 return TILEX / move_stepsize;
1451 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1454 int player_nr = player->index_nr;
1455 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1456 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1458 /* do no immediately change move delay -- the player might just be moving */
1459 player->move_delay_value_next = move_delay;
1461 /* information if player can move must be set separately */
1462 player->cannot_move = cannot_move;
1466 player->move_delay = game.initial_move_delay[player_nr];
1467 player->move_delay_value = game.initial_move_delay_value[player_nr];
1469 player->move_delay_value_next = -1;
1471 player->move_delay_reset_counter = 0;
1475 void GetPlayerConfig()
1477 GameFrameDelay = setup.game_frame_delay;
1479 if (!audio.sound_available)
1480 setup.sound_simple = FALSE;
1482 if (!audio.loops_available)
1483 setup.sound_loops = FALSE;
1485 if (!audio.music_available)
1486 setup.sound_music = FALSE;
1488 if (!video.fullscreen_available)
1489 setup.fullscreen = FALSE;
1491 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1493 SetAudioMode(setup.sound);
1497 int GetElementFromGroupElement(int element)
1499 if (IS_GROUP_ELEMENT(element))
1501 struct ElementGroupInfo *group = element_info[element].group;
1502 int last_anim_random_frame = gfx.anim_random_frame;
1505 if (group->choice_mode == ANIM_RANDOM)
1506 gfx.anim_random_frame = RND(group->num_elements_resolved);
1508 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1509 group->choice_mode, 0,
1512 if (group->choice_mode == ANIM_RANDOM)
1513 gfx.anim_random_frame = last_anim_random_frame;
1515 group->choice_pos++;
1517 element = group->element_resolved[element_pos];
1523 static void InitPlayerField(int x, int y, int element, boolean init_game)
1525 if (element == EL_SP_MURPHY)
1529 if (stored_player[0].present)
1531 Feld[x][y] = EL_SP_MURPHY_CLONE;
1537 stored_player[0].use_murphy = TRUE;
1539 if (!level.use_artwork_element[0])
1540 stored_player[0].artwork_element = EL_SP_MURPHY;
1543 Feld[x][y] = EL_PLAYER_1;
1549 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1550 int jx = player->jx, jy = player->jy;
1552 player->present = TRUE;
1554 player->block_last_field = (element == EL_SP_MURPHY ?
1555 level.sp_block_last_field :
1556 level.block_last_field);
1558 /* ---------- initialize player's last field block delay --------------- */
1560 /* always start with reliable default value (no adjustment needed) */
1561 player->block_delay_adjustment = 0;
1563 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1564 if (player->block_last_field && element == EL_SP_MURPHY)
1565 player->block_delay_adjustment = 1;
1567 /* special case 2: in game engines before 3.1.1, blocking was different */
1568 if (game.use_block_last_field_bug)
1569 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1571 if (!options.network || player->connected)
1573 player->active = TRUE;
1575 /* remove potentially duplicate players */
1576 if (StorePlayer[jx][jy] == Feld[x][y])
1577 StorePlayer[jx][jy] = 0;
1579 StorePlayer[x][y] = Feld[x][y];
1583 printf("Player %d activated.\n", player->element_nr);
1584 printf("[Local player is %d and currently %s.]\n",
1585 local_player->element_nr,
1586 local_player->active ? "active" : "not active");
1590 Feld[x][y] = EL_EMPTY;
1592 player->jx = player->last_jx = x;
1593 player->jy = player->last_jy = y;
1597 static void InitField(int x, int y, boolean init_game)
1599 int element = Feld[x][y];
1608 InitPlayerField(x, y, element, init_game);
1611 case EL_SOKOBAN_FIELD_PLAYER:
1612 element = Feld[x][y] = EL_PLAYER_1;
1613 InitField(x, y, init_game);
1615 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1616 InitField(x, y, init_game);
1619 case EL_SOKOBAN_FIELD_EMPTY:
1620 local_player->sokobanfields_still_needed++;
1624 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1625 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1626 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1627 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1628 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1629 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1630 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1631 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1632 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1633 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1642 case EL_SPACESHIP_RIGHT:
1643 case EL_SPACESHIP_UP:
1644 case EL_SPACESHIP_LEFT:
1645 case EL_SPACESHIP_DOWN:
1646 case EL_BD_BUTTERFLY:
1647 case EL_BD_BUTTERFLY_RIGHT:
1648 case EL_BD_BUTTERFLY_UP:
1649 case EL_BD_BUTTERFLY_LEFT:
1650 case EL_BD_BUTTERFLY_DOWN:
1652 case EL_BD_FIREFLY_RIGHT:
1653 case EL_BD_FIREFLY_UP:
1654 case EL_BD_FIREFLY_LEFT:
1655 case EL_BD_FIREFLY_DOWN:
1656 case EL_PACMAN_RIGHT:
1658 case EL_PACMAN_LEFT:
1659 case EL_PACMAN_DOWN:
1661 case EL_YAMYAM_LEFT:
1662 case EL_YAMYAM_RIGHT:
1664 case EL_YAMYAM_DOWN:
1665 case EL_DARK_YAMYAM:
1668 case EL_SP_SNIKSNAK:
1669 case EL_SP_ELECTRON:
1678 case EL_AMOEBA_FULL:
1683 case EL_AMOEBA_DROP:
1684 if (y == lev_fieldy - 1)
1686 Feld[x][y] = EL_AMOEBA_GROWING;
1687 Store[x][y] = EL_AMOEBA_WET;
1691 case EL_DYNAMITE_ACTIVE:
1692 case EL_SP_DISK_RED_ACTIVE:
1693 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1694 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1695 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1696 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1697 MovDelay[x][y] = 96;
1700 case EL_EM_DYNAMITE_ACTIVE:
1701 MovDelay[x][y] = 32;
1705 local_player->lights_still_needed++;
1709 local_player->friends_still_needed++;
1714 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1717 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1718 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1719 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1720 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1721 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1722 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1723 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1724 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1725 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1726 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1727 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1728 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1731 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1732 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1733 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1735 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1737 game.belt_dir[belt_nr] = belt_dir;
1738 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1740 else /* more than one switch -- set it like the first switch */
1742 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1747 #if !USE_BOTH_SWITCHGATE_SWITCHES
1748 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1750 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1753 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1755 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1759 case EL_LIGHT_SWITCH_ACTIVE:
1761 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1764 case EL_INVISIBLE_STEELWALL:
1765 case EL_INVISIBLE_WALL:
1766 case EL_INVISIBLE_SAND:
1767 if (game.light_time_left > 0 ||
1768 game.lenses_time_left > 0)
1769 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1772 case EL_EMC_MAGIC_BALL:
1773 if (game.ball_state)
1774 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1777 case EL_EMC_MAGIC_BALL_SWITCH:
1778 if (game.ball_state)
1779 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1783 if (IS_CUSTOM_ELEMENT(element))
1785 if (CAN_MOVE(element))
1788 #if USE_NEW_CUSTOM_VALUE
1789 if (!element_info[element].use_last_ce_value || init_game)
1790 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1793 else if (IS_GROUP_ELEMENT(element))
1795 Feld[x][y] = GetElementFromGroupElement(element);
1797 InitField(x, y, init_game);
1804 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1807 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1809 InitField(x, y, init_game);
1811 /* not needed to call InitMovDir() -- already done by InitField()! */
1812 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1813 CAN_MOVE(Feld[x][y]))
1817 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1819 int old_element = Feld[x][y];
1821 InitField(x, y, init_game);
1823 /* not needed to call InitMovDir() -- already done by InitField()! */
1824 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1825 CAN_MOVE(old_element) &&
1826 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1829 /* this case is in fact a combination of not less than three bugs:
1830 first, it calls InitMovDir() for elements that can move, although this is
1831 already done by InitField(); then, it checks the element that was at this
1832 field _before_ the call to InitField() (which can change it); lastly, it
1833 was not called for "mole with direction" elements, which were treated as
1834 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1840 static int get_key_element_from_nr(int key_nr)
1842 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1843 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1844 EL_EM_KEY_1 : EL_KEY_1);
1846 return key_base_element + key_nr;
1849 static int get_next_dropped_element(struct PlayerInfo *player)
1851 return (player->inventory_size > 0 ?
1852 player->inventory_element[player->inventory_size - 1] :
1853 player->inventory_infinite_element != EL_UNDEFINED ?
1854 player->inventory_infinite_element :
1855 player->dynabombs_left > 0 ?
1856 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1860 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1862 /* pos >= 0: get element from bottom of the stack;
1863 pos < 0: get element from top of the stack */
1867 int min_inventory_size = -pos;
1868 int inventory_pos = player->inventory_size - min_inventory_size;
1869 int min_dynabombs_left = min_inventory_size - player->inventory_size;
1871 return (player->inventory_size >= min_inventory_size ?
1872 player->inventory_element[inventory_pos] :
1873 player->inventory_infinite_element != EL_UNDEFINED ?
1874 player->inventory_infinite_element :
1875 player->dynabombs_left >= min_dynabombs_left ?
1876 EL_DYNABOMB_PLAYER_1 + player->index_nr :
1881 int min_dynabombs_left = pos + 1;
1882 int min_inventory_size = pos + 1 - player->dynabombs_left;
1883 int inventory_pos = pos - player->dynabombs_left;
1885 return (player->inventory_infinite_element != EL_UNDEFINED ?
1886 player->inventory_infinite_element :
1887 player->dynabombs_left >= min_dynabombs_left ?
1888 EL_DYNABOMB_PLAYER_1 + player->index_nr :
1889 player->inventory_size >= min_inventory_size ?
1890 player->inventory_element[inventory_pos] :
1895 void InitGameControlValues()
1899 for (i = 0; game_panel_controls[i].nr != -1; i++)
1901 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
1903 int type = gpc->type;
1904 struct TextPosInfo *pos = gpc->pos;
1908 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
1909 Error(ERR_EXIT, "this should not happen -- please debug");
1912 /* force update of game controls after initialization */
1913 gpc->value = gpc->last_value = -1;
1914 gpc->frame = gpc->last_frame = -1;
1915 gpc->gfx_frame = -1;
1917 /* determine panel value width for later calculation of alignment */
1918 if (type == TYPE_INTEGER || type == TYPE_STRING)
1920 pos->width = pos->size * getFontWidth(pos->font);
1921 pos->height = getFontHeight(pos->font);
1923 else if (type == TYPE_ELEMENT)
1925 pos->width = pos->size;
1926 pos->height = pos->size;
1931 void UpdateGameControlValues()
1934 int time = (level.time == 0 ? TimePlayed : TimeLeft);
1935 int score = (local_player->LevelSolved ? local_player->score_final :
1936 local_player->score);
1937 int exit_closed = (local_player->gems_still_needed > 0 ||
1938 local_player->sokobanfields_still_needed > 0 ||
1939 local_player->lights_still_needed > 0);
1941 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
1942 game_panel_controls[GAME_PANEL_GEMS].value =
1943 local_player->gems_still_needed;
1945 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
1946 for (i = 0; i < MAX_NUM_KEYS; i++)
1947 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
1948 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
1949 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
1951 if (game.centered_player_nr == -1)
1953 for (i = 0; i < MAX_PLAYERS; i++)
1955 for (k = 0; k < MAX_NUM_KEYS; k++)
1956 if (stored_player[i].key[k])
1957 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1958 get_key_element_from_nr(k);
1960 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1961 stored_player[i].inventory_size;
1963 if (stored_player[i].num_white_keys > 0)
1964 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
1967 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1968 stored_player[i].num_white_keys;
1973 int player_nr = game.centered_player_nr;
1975 for (k = 0; k < MAX_NUM_KEYS; k++)
1976 if (stored_player[player_nr].key[k])
1977 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1978 get_key_element_from_nr(k);
1980 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1981 stored_player[player_nr].inventory_size;
1983 if (stored_player[player_nr].num_white_keys > 0)
1984 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
1986 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1987 stored_player[player_nr].num_white_keys;
1990 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
1992 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
1993 get_inventory_element_from_pos(local_player, i);
1994 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
1995 get_inventory_element_from_pos(local_player, -i - 1);
1998 game_panel_controls[GAME_PANEL_SCORE].value = score;
2000 game_panel_controls[GAME_PANEL_TIME].value = time;
2002 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2003 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2004 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2006 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2007 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2009 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2010 local_player->shield_normal_time_left;
2011 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2012 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2014 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2015 local_player->shield_deadly_time_left;
2017 game_panel_controls[GAME_PANEL_EXIT].value =
2018 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2020 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2021 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2022 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2023 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2024 EL_EMC_MAGIC_BALL_SWITCH);
2026 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2027 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2028 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2029 game.light_time_left;
2031 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2032 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2033 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2034 game.timegate_time_left;
2036 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2037 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2039 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2040 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2041 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2042 game.lenses_time_left;
2044 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2045 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2046 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2047 game.magnify_time_left;
2049 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2050 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2051 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2052 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2053 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2054 EL_BALLOON_SWITCH_NONE);
2056 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2057 local_player->dynabomb_count;
2058 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2059 local_player->dynabomb_size;
2060 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2061 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2063 game_panel_controls[GAME_PANEL_PENGUINS].value =
2064 local_player->friends_still_needed;
2066 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2067 local_player->sokobanfields_still_needed;
2068 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2069 local_player->sokobanfields_still_needed;
2071 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2072 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2074 for (i = 0; i < NUM_BELTS; i++)
2076 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2077 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2078 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2079 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2080 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2083 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2084 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2085 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2086 game.magic_wall_time_left;
2088 #if USE_PLAYER_GRAVITY
2089 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2090 local_player->gravity;
2092 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2095 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2097 if (game.panel.ce_score[i].id != EL_UNDEFINED)
2099 int ce_score = element_info[game.panel.ce_score[i].id].collect_score;
2101 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = ce_score;
2102 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = ce_score;
2106 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = 0;
2107 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = 0;
2111 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2112 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2113 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2115 for (i = 0; game_panel_controls[i].nr != -1; i++)
2117 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2119 if (gpc->type == TYPE_ELEMENT)
2121 if (gpc->value != gpc->last_value)
2126 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2132 void DisplayGameControlValues()
2136 game_status = GAME_MODE_PSEUDO_PANEL;
2138 for (i = 0; game_panel_controls[i].nr != -1; i++)
2140 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2142 int type = gpc->type;
2143 struct TextPosInfo *pos = gpc->pos;
2144 int value = gpc->value;
2145 int frame = gpc->frame;
2146 int last_value = gpc->last_value;
2147 int last_frame = gpc->last_frame;
2148 int size = pos->size;
2149 int font = pos->font;
2151 if (value == last_value && frame == last_frame)
2154 gpc->last_value = value;
2155 gpc->last_frame = frame;
2158 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2161 if (PANEL_DEACTIVATED(pos))
2164 if (type == TYPE_INTEGER)
2166 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2167 nr == GAME_PANEL_TIME)
2169 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2171 if (use_dynamic_size) /* use dynamic number of digits */
2173 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2174 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2175 int size2 = size1 + 1;
2176 int font1 = pos->font;
2177 int font2 = pos->font_alt;
2179 size = (value < value_change ? size1 : size2);
2180 font = (value < value_change ? font1 : font2);
2182 /* clear background if value just changed its size (dynamic digits) */
2183 if ((last_value < value_change) != (value < value_change))
2185 int width1 = size1 * getFontWidth(font1);
2186 int width2 = size2 * getFontWidth(font2);
2187 int max_width = MAX(width1, width2);
2188 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2190 pos->width = max_width;
2192 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2193 max_width, max_height);
2199 /* correct text size if "digits" is zero or less */
2201 size = strlen(int2str(value, size));
2203 /* dynamically correct text alignment */
2204 pos->width = size * getFontWidth(font);
2207 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2209 else if (type == TYPE_ELEMENT)
2211 int element, graphic;
2215 int dst_x = PANEL_XPOS(pos);
2216 int dst_y = PANEL_YPOS(pos);
2218 if (value == EL_UNDEFINED || value == EL_EMPTY)
2220 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2221 graphic = el2panelimg(element);
2223 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2224 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2225 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2230 graphic = el2panelimg(value);
2232 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2235 width = graphic_info[graphic].width * size / TILESIZE;
2236 height = graphic_info[graphic].height * size / TILESIZE;
2238 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2240 else if (type == TYPE_STRING)
2242 boolean active = (value != 0);
2243 char *state_normal = "off";
2244 char *state_active = "on";
2245 char *state = (active ? state_active : state_normal);
2246 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2247 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2248 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2249 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2251 if (nr == GAME_PANEL_GRAVITY_STATE)
2253 int font1 = pos->font; /* (used for normal state) */
2254 int font2 = pos->font_alt; /* (used for active state) */
2255 int size1 = strlen(state_normal);
2256 int size2 = strlen(state_active);
2257 int width1 = size1 * getFontWidth(font1);
2258 int width2 = size2 * getFontWidth(font2);
2259 int max_width = MAX(width1, width2);
2260 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2262 pos->width = max_width;
2264 /* clear background for values that may have changed its size */
2265 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2266 max_width, max_height);
2268 font = (active ? font2 : font1);
2278 /* don't truncate output if "chars" is zero or less */
2281 /* dynamically correct text alignment */
2282 pos->width = size * getFontWidth(font);
2286 s_cut = getStringCopyN(s, size);
2288 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2294 redraw_mask |= REDRAW_DOOR_1;
2297 game_status = GAME_MODE_PLAYING;
2300 void DrawGameValue_Emeralds(int value)
2302 struct TextPosInfo *pos = &game.panel.gems;
2304 int font_nr = pos->font;
2306 int font_nr = FONT_TEXT_2;
2308 int font_width = getFontWidth(font_nr);
2309 int chars = pos->size;
2312 return; /* !!! USE NEW STUFF !!! */
2315 if (PANEL_DEACTIVATED(pos))
2318 pos->width = chars * font_width;
2320 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2323 void DrawGameValue_Dynamite(int value)
2325 struct TextPosInfo *pos = &game.panel.inventory_count;
2327 int font_nr = pos->font;
2329 int font_nr = FONT_TEXT_2;
2331 int font_width = getFontWidth(font_nr);
2332 int chars = pos->size;
2335 return; /* !!! USE NEW STUFF !!! */
2338 if (PANEL_DEACTIVATED(pos))
2341 pos->width = chars * font_width;
2343 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2346 void DrawGameValue_Score(int value)
2348 struct TextPosInfo *pos = &game.panel.score;
2350 int font_nr = pos->font;
2352 int font_nr = FONT_TEXT_2;
2354 int font_width = getFontWidth(font_nr);
2355 int chars = pos->size;
2358 return; /* !!! USE NEW STUFF !!! */
2361 if (PANEL_DEACTIVATED(pos))
2364 pos->width = chars * font_width;
2366 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2369 void DrawGameValue_Time(int value)
2371 struct TextPosInfo *pos = &game.panel.time;
2372 static int last_value = -1;
2375 int chars = pos->size;
2377 int font1_nr = pos->font;
2378 int font2_nr = pos->font_alt;
2380 int font1_nr = FONT_TEXT_2;
2381 int font2_nr = FONT_TEXT_1;
2383 int font_nr = font1_nr;
2384 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2387 return; /* !!! USE NEW STUFF !!! */
2390 if (PANEL_DEACTIVATED(pos))
2393 if (use_dynamic_chars) /* use dynamic number of chars */
2395 chars = (value < 1000 ? chars1 : chars2);
2396 font_nr = (value < 1000 ? font1_nr : font2_nr);
2399 /* clear background if value just changed its size (dynamic chars only) */
2400 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2402 int width1 = chars1 * getFontWidth(font1_nr);
2403 int width2 = chars2 * getFontWidth(font2_nr);
2404 int max_width = MAX(width1, width2);
2405 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2407 pos->width = max_width;
2409 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2410 max_width, max_height);
2413 pos->width = chars * getFontWidth(font_nr);
2415 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2420 void DrawGameValue_Level(int value)
2422 struct TextPosInfo *pos = &game.panel.level_number;
2425 int chars = pos->size;
2427 int font1_nr = pos->font;
2428 int font2_nr = pos->font_alt;
2430 int font1_nr = FONT_TEXT_2;
2431 int font2_nr = FONT_TEXT_1;
2433 int font_nr = font1_nr;
2434 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2437 return; /* !!! USE NEW STUFF !!! */
2440 if (PANEL_DEACTIVATED(pos))
2443 if (use_dynamic_chars) /* use dynamic number of chars */
2445 chars = (level_nr < 100 ? chars1 : chars2);
2446 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2449 pos->width = chars * getFontWidth(font_nr);
2451 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2454 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2457 struct TextPosInfo *pos = &game.panel.keys;
2460 int base_key_graphic = EL_KEY_1;
2465 return; /* !!! USE NEW STUFF !!! */
2469 if (PANEL_DEACTIVATED(pos))
2474 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2475 base_key_graphic = EL_EM_KEY_1;
2479 pos->width = 4 * MINI_TILEX;
2483 for (i = 0; i < MAX_NUM_KEYS; i++)
2485 /* currently only 4 of 8 possible keys are displayed */
2486 for (i = 0; i < STD_NUM_KEYS; i++)
2490 struct TextPosInfo *pos = &game.panel.key[i];
2492 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2493 int src_y = DOOR_GFX_PAGEY1 + 123;
2495 int dst_x = PANEL_XPOS(pos);
2496 int dst_y = PANEL_YPOS(pos);
2498 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2499 int dst_y = PANEL_YPOS(pos);
2503 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2504 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2506 int graphic = el2edimg(element);
2510 if (PANEL_DEACTIVATED(pos))
2515 /* masked blit with tiles from half-size scaled bitmap does not work yet
2516 (no mask bitmap created for these sizes after loading and scaling) --
2517 solution: load without creating mask, scale, then create final mask */
2519 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2520 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2525 int graphic = el2edimg(base_key_graphic + i);
2530 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2532 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2533 dst_x - src_x, dst_y - src_y);
2534 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2540 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2542 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2543 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2546 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2548 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2549 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2557 void DrawGameValue_Emeralds(int value)
2559 int font_nr = FONT_TEXT_2;
2560 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2562 if (PANEL_DEACTIVATED(game.panel.gems))
2565 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2568 void DrawGameValue_Dynamite(int value)
2570 int font_nr = FONT_TEXT_2;
2571 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2573 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2576 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2579 void DrawGameValue_Score(int value)
2581 int font_nr = FONT_TEXT_2;
2582 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2584 if (PANEL_DEACTIVATED(game.panel.score))
2587 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2590 void DrawGameValue_Time(int value)
2592 int font1_nr = FONT_TEXT_2;
2594 int font2_nr = FONT_TEXT_1;
2596 int font2_nr = FONT_LEVEL_NUMBER;
2598 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2599 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2601 if (PANEL_DEACTIVATED(game.panel.time))
2604 /* clear background if value just changed its size */
2605 if (value == 999 || value == 1000)
2606 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2609 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2611 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2614 void DrawGameValue_Level(int value)
2616 int font1_nr = FONT_TEXT_2;
2618 int font2_nr = FONT_TEXT_1;
2620 int font2_nr = FONT_LEVEL_NUMBER;
2623 if (PANEL_DEACTIVATED(game.panel.level))
2627 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2629 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2632 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2634 int base_key_graphic = EL_KEY_1;
2637 if (PANEL_DEACTIVATED(game.panel.keys))
2640 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2641 base_key_graphic = EL_EM_KEY_1;
2643 /* currently only 4 of 8 possible keys are displayed */
2644 for (i = 0; i < STD_NUM_KEYS; i++)
2646 int x = XX_KEYS + i * MINI_TILEX;
2650 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2652 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2653 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2659 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2662 int key[MAX_NUM_KEYS];
2665 /* prevent EM engine from updating time/score values parallel to GameWon() */
2666 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2667 local_player->LevelSolved)
2670 for (i = 0; i < MAX_NUM_KEYS; i++)
2671 key[i] = key_bits & (1 << i);
2673 DrawGameValue_Level(level_nr);
2675 DrawGameValue_Emeralds(emeralds);
2676 DrawGameValue_Dynamite(dynamite);
2677 DrawGameValue_Score(score);
2678 DrawGameValue_Time(time);
2680 DrawGameValue_Keys(key);
2683 void UpdateGameDoorValues()
2685 UpdateGameControlValues();
2688 void DrawGameDoorValues()
2690 DisplayGameControlValues();
2693 void DrawGameDoorValues_OLD()
2695 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2696 int dynamite_value = 0;
2697 int score_value = (local_player->LevelSolved ? local_player->score_final :
2698 local_player->score);
2699 int gems_value = local_player->gems_still_needed;
2703 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2705 DrawGameDoorValues_EM();
2710 if (game.centered_player_nr == -1)
2712 for (i = 0; i < MAX_PLAYERS; i++)
2714 for (j = 0; j < MAX_NUM_KEYS; j++)
2715 if (stored_player[i].key[j])
2716 key_bits |= (1 << j);
2718 dynamite_value += stored_player[i].inventory_size;
2723 int player_nr = game.centered_player_nr;
2725 for (i = 0; i < MAX_NUM_KEYS; i++)
2726 if (stored_player[player_nr].key[i])
2727 key_bits |= (1 << i);
2729 dynamite_value = stored_player[player_nr].inventory_size;
2732 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2738 =============================================================================
2740 -----------------------------------------------------------------------------
2741 initialize game engine due to level / tape version number
2742 =============================================================================
2745 static void InitGameEngine()
2747 int i, j, k, l, x, y;
2749 /* set game engine from tape file when re-playing, else from level file */
2750 game.engine_version = (tape.playing ? tape.engine_version :
2751 level.game_version);
2753 /* ---------------------------------------------------------------------- */
2754 /* set flags for bugs and changes according to active game engine version */
2755 /* ---------------------------------------------------------------------- */
2758 Summary of bugfix/change:
2759 Fixed handling for custom elements that change when pushed by the player.
2761 Fixed/changed in version:
2765 Before 3.1.0, custom elements that "change when pushing" changed directly
2766 after the player started pushing them (until then handled in "DigField()").
2767 Since 3.1.0, these custom elements are not changed until the "pushing"
2768 move of the element is finished (now handled in "ContinueMoving()").
2770 Affected levels/tapes:
2771 The first condition is generally needed for all levels/tapes before version
2772 3.1.0, which might use the old behaviour before it was changed; known tapes
2773 that are affected are some tapes from the level set "Walpurgis Gardens" by
2775 The second condition is an exception from the above case and is needed for
2776 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2777 above (including some development versions of 3.1.0), but before it was
2778 known that this change would break tapes like the above and was fixed in
2779 3.1.1, so that the changed behaviour was active although the engine version
2780 while recording maybe was before 3.1.0. There is at least one tape that is
2781 affected by this exception, which is the tape for the one-level set "Bug
2782 Machine" by Juergen Bonhagen.
2785 game.use_change_when_pushing_bug =
2786 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2788 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2789 tape.game_version < VERSION_IDENT(3,1,1,0)));
2792 Summary of bugfix/change:
2793 Fixed handling for blocking the field the player leaves when moving.
2795 Fixed/changed in version:
2799 Before 3.1.1, when "block last field when moving" was enabled, the field
2800 the player is leaving when moving was blocked for the time of the move,
2801 and was directly unblocked afterwards. This resulted in the last field
2802 being blocked for exactly one less than the number of frames of one player
2803 move. Additionally, even when blocking was disabled, the last field was
2804 blocked for exactly one frame.
2805 Since 3.1.1, due to changes in player movement handling, the last field
2806 is not blocked at all when blocking is disabled. When blocking is enabled,
2807 the last field is blocked for exactly the number of frames of one player
2808 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2809 last field is blocked for exactly one more than the number of frames of
2812 Affected levels/tapes:
2813 (!!! yet to be determined -- probably many !!!)
2816 game.use_block_last_field_bug =
2817 (game.engine_version < VERSION_IDENT(3,1,1,0));
2820 Summary of bugfix/change:
2821 Changed behaviour of CE changes with multiple changes per single frame.
2823 Fixed/changed in version:
2827 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2828 This resulted in race conditions where CEs seem to behave strange in some
2829 situations (where triggered CE changes were just skipped because there was
2830 already a CE change on that tile in the playfield in that engine frame).
2831 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2832 (The number of changes per frame must be limited in any case, because else
2833 it is easily possible to define CE changes that would result in an infinite
2834 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2835 should be set large enough so that it would only be reached in cases where
2836 the corresponding CE change conditions run into a loop. Therefore, it seems
2837 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2838 maximal number of change pages for custom elements.)
2840 Affected levels/tapes:
2844 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2845 game.max_num_changes_per_frame = 1;
2847 game.max_num_changes_per_frame =
2848 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2851 /* ---------------------------------------------------------------------- */
2853 /* default scan direction: scan playfield from top/left to bottom/right */
2854 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2856 /* dynamically adjust element properties according to game engine version */
2857 InitElementPropertiesEngine(game.engine_version);
2860 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2861 printf(" tape version == %06d [%s] [file: %06d]\n",
2862 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2864 printf(" => game.engine_version == %06d\n", game.engine_version);
2867 /* ---------- initialize player's initial move delay --------------------- */
2869 /* dynamically adjust player properties according to level information */
2870 for (i = 0; i < MAX_PLAYERS; i++)
2871 game.initial_move_delay_value[i] =
2872 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2874 /* dynamically adjust player properties according to game engine version */
2875 for (i = 0; i < MAX_PLAYERS; i++)
2876 game.initial_move_delay[i] =
2877 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2878 game.initial_move_delay_value[i] : 0);
2880 /* ---------- initialize player's initial push delay --------------------- */
2882 /* dynamically adjust player properties according to game engine version */
2883 game.initial_push_delay_value =
2884 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2886 /* ---------- initialize changing elements ------------------------------- */
2888 /* initialize changing elements information */
2889 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2891 struct ElementInfo *ei = &element_info[i];
2893 /* this pointer might have been changed in the level editor */
2894 ei->change = &ei->change_page[0];
2896 if (!IS_CUSTOM_ELEMENT(i))
2898 ei->change->target_element = EL_EMPTY_SPACE;
2899 ei->change->delay_fixed = 0;
2900 ei->change->delay_random = 0;
2901 ei->change->delay_frames = 1;
2904 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2906 ei->has_change_event[j] = FALSE;
2908 ei->event_page_nr[j] = 0;
2909 ei->event_page[j] = &ei->change_page[0];
2913 /* add changing elements from pre-defined list */
2914 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2916 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2917 struct ElementInfo *ei = &element_info[ch_delay->element];
2919 ei->change->target_element = ch_delay->target_element;
2920 ei->change->delay_fixed = ch_delay->change_delay;
2922 ei->change->pre_change_function = ch_delay->pre_change_function;
2923 ei->change->change_function = ch_delay->change_function;
2924 ei->change->post_change_function = ch_delay->post_change_function;
2926 ei->change->can_change = TRUE;
2927 ei->change->can_change_or_has_action = TRUE;
2929 ei->has_change_event[CE_DELAY] = TRUE;
2931 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2932 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2935 /* ---------- initialize internal run-time variables ------------- */
2937 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2939 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2941 for (j = 0; j < ei->num_change_pages; j++)
2943 ei->change_page[j].can_change_or_has_action =
2944 (ei->change_page[j].can_change |
2945 ei->change_page[j].has_action);
2949 /* add change events from custom element configuration */
2950 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2952 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2954 for (j = 0; j < ei->num_change_pages; j++)
2956 if (!ei->change_page[j].can_change_or_has_action)
2959 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2961 /* only add event page for the first page found with this event */
2962 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2964 ei->has_change_event[k] = TRUE;
2966 ei->event_page_nr[k] = j;
2967 ei->event_page[k] = &ei->change_page[j];
2973 /* ---------- initialize run-time trigger player and element ------------- */
2975 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2977 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2979 for (j = 0; j < ei->num_change_pages; j++)
2981 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2982 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2983 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2984 ei->change_page[j].actual_trigger_ce_value = 0;
2985 ei->change_page[j].actual_trigger_ce_score = 0;
2989 /* ---------- initialize trigger events ---------------------------------- */
2991 /* initialize trigger events information */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2994 trigger_events[i][j] = FALSE;
2996 /* add trigger events from element change event properties */
2997 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2999 struct ElementInfo *ei = &element_info[i];
3001 for (j = 0; j < ei->num_change_pages; j++)
3003 if (!ei->change_page[j].can_change_or_has_action)
3006 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3008 int trigger_element = ei->change_page[j].trigger_element;
3010 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3012 if (ei->change_page[j].has_event[k])
3014 if (IS_GROUP_ELEMENT(trigger_element))
3016 struct ElementGroupInfo *group =
3017 element_info[trigger_element].group;
3019 for (l = 0; l < group->num_elements_resolved; l++)
3020 trigger_events[group->element_resolved[l]][k] = TRUE;
3022 else if (trigger_element == EL_ANY_ELEMENT)
3023 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3024 trigger_events[l][k] = TRUE;
3026 trigger_events[trigger_element][k] = TRUE;
3033 /* ---------- initialize push delay -------------------------------------- */
3035 /* initialize push delay values to default */
3036 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3038 if (!IS_CUSTOM_ELEMENT(i))
3040 /* set default push delay values (corrected since version 3.0.7-1) */
3041 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3043 element_info[i].push_delay_fixed = 2;
3044 element_info[i].push_delay_random = 8;
3048 element_info[i].push_delay_fixed = 8;
3049 element_info[i].push_delay_random = 8;
3054 /* set push delay value for certain elements from pre-defined list */
3055 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3057 int e = push_delay_list[i].element;
3059 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3060 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3063 /* set push delay value for Supaplex elements for newer engine versions */
3064 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3066 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3068 if (IS_SP_ELEMENT(i))
3070 /* set SP push delay to just enough to push under a falling zonk */
3071 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3073 element_info[i].push_delay_fixed = delay;
3074 element_info[i].push_delay_random = 0;
3079 /* ---------- initialize move stepsize ----------------------------------- */
3081 /* initialize move stepsize values to default */
3082 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083 if (!IS_CUSTOM_ELEMENT(i))
3084 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3086 /* set move stepsize value for certain elements from pre-defined list */
3087 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3089 int e = move_stepsize_list[i].element;
3091 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3094 /* ---------- initialize collect score ----------------------------------- */
3096 /* initialize collect score values for custom elements from initial value */
3097 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3098 if (IS_CUSTOM_ELEMENT(i))
3099 element_info[i].collect_score = element_info[i].collect_score_initial;
3101 /* ---------- initialize collect count ----------------------------------- */
3103 /* initialize collect count values for non-custom elements */
3104 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3105 if (!IS_CUSTOM_ELEMENT(i))
3106 element_info[i].collect_count_initial = 0;
3108 /* add collect count values for all elements from pre-defined list */
3109 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3110 element_info[collect_count_list[i].element].collect_count_initial =
3111 collect_count_list[i].count;
3113 /* ---------- initialize access direction -------------------------------- */
3115 /* initialize access direction values to default (access from every side) */
3116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3117 if (!IS_CUSTOM_ELEMENT(i))
3118 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3120 /* set access direction value for certain elements from pre-defined list */
3121 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3122 element_info[access_direction_list[i].element].access_direction =
3123 access_direction_list[i].direction;
3125 /* ---------- initialize explosion content ------------------------------- */
3126 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3128 if (IS_CUSTOM_ELEMENT(i))
3131 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3133 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3135 element_info[i].content.e[x][y] =
3136 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3137 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3138 i == EL_PLAYER_3 ? EL_EMERALD :
3139 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3140 i == EL_MOLE ? EL_EMERALD_RED :
3141 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3142 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3143 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3144 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3145 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3146 i == EL_WALL_EMERALD ? EL_EMERALD :
3147 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3148 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3149 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3150 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3151 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3152 i == EL_WALL_PEARL ? EL_PEARL :
3153 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3158 /* ---------- initialize recursion detection ------------------------------ */
3159 recursion_loop_depth = 0;
3160 recursion_loop_detected = FALSE;
3161 recursion_loop_element = EL_UNDEFINED;
3163 /* ---------- initialize graphics engine ---------------------------------- */
3164 game.scroll_delay_value =
3165 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3166 setup.scroll_delay ? setup.scroll_delay_value : 0);
3167 game.scroll_delay_value =
3168 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3171 int get_num_special_action(int element, int action_first, int action_last)
3173 int num_special_action = 0;
3176 for (i = action_first; i <= action_last; i++)
3178 boolean found = FALSE;
3180 for (j = 0; j < NUM_DIRECTIONS; j++)
3181 if (el_act_dir2img(element, i, j) !=
3182 el_act_dir2img(element, ACTION_DEFAULT, j))
3186 num_special_action++;
3191 return num_special_action;
3196 =============================================================================
3198 -----------------------------------------------------------------------------
3199 initialize and start new game
3200 =============================================================================
3205 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3206 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3207 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3209 boolean do_fading = (game_status == GAME_MODE_MAIN);
3213 game_status = GAME_MODE_PLAYING;
3216 InitGameControlValues();
3218 /* don't play tapes over network */
3219 network_playing = (options.network && !tape.playing);
3221 for (i = 0; i < MAX_PLAYERS; i++)
3223 struct PlayerInfo *player = &stored_player[i];
3225 player->index_nr = i;
3226 player->index_bit = (1 << i);
3227 player->element_nr = EL_PLAYER_1 + i;
3229 player->present = FALSE;
3230 player->active = FALSE;
3231 player->killed = FALSE;
3234 player->effective_action = 0;
3235 player->programmed_action = 0;
3238 player->score_final = 0;
3240 player->gems_still_needed = level.gems_needed;
3241 player->sokobanfields_still_needed = 0;
3242 player->lights_still_needed = 0;
3243 player->friends_still_needed = 0;
3245 for (j = 0; j < MAX_NUM_KEYS; j++)
3246 player->key[j] = FALSE;
3248 player->num_white_keys = 0;
3250 player->dynabomb_count = 0;
3251 player->dynabomb_size = 1;
3252 player->dynabombs_left = 0;
3253 player->dynabomb_xl = FALSE;
3255 player->MovDir = MV_NONE;
3258 player->GfxDir = MV_NONE;
3259 player->GfxAction = ACTION_DEFAULT;
3261 player->StepFrame = 0;
3263 player->use_murphy = FALSE;
3264 player->artwork_element =
3265 (level.use_artwork_element[i] ? level.artwork_element[i] :
3266 player->element_nr);
3268 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3269 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3271 player->gravity = level.initial_player_gravity[i];
3273 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3275 player->actual_frame_counter = 0;
3277 player->step_counter = 0;
3279 player->last_move_dir = MV_NONE;
3281 player->is_active = FALSE;
3283 player->is_waiting = FALSE;
3284 player->is_moving = FALSE;
3285 player->is_auto_moving = FALSE;
3286 player->is_digging = FALSE;
3287 player->is_snapping = FALSE;
3288 player->is_collecting = FALSE;
3289 player->is_pushing = FALSE;
3290 player->is_switching = FALSE;
3291 player->is_dropping = FALSE;
3292 player->is_dropping_pressed = FALSE;
3294 player->is_bored = FALSE;
3295 player->is_sleeping = FALSE;
3297 player->frame_counter_bored = -1;
3298 player->frame_counter_sleeping = -1;
3300 player->anim_delay_counter = 0;
3301 player->post_delay_counter = 0;
3303 player->dir_waiting = MV_NONE;
3304 player->action_waiting = ACTION_DEFAULT;
3305 player->last_action_waiting = ACTION_DEFAULT;
3306 player->special_action_bored = ACTION_DEFAULT;
3307 player->special_action_sleeping = ACTION_DEFAULT;
3309 player->switch_x = -1;
3310 player->switch_y = -1;
3312 player->drop_x = -1;
3313 player->drop_y = -1;
3315 player->show_envelope = 0;
3317 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3319 player->push_delay = -1; /* initialized when pushing starts */
3320 player->push_delay_value = game.initial_push_delay_value;
3322 player->drop_delay = 0;
3323 player->drop_pressed_delay = 0;
3325 player->last_jx = -1;
3326 player->last_jy = -1;
3330 player->shield_normal_time_left = 0;
3331 player->shield_deadly_time_left = 0;
3333 player->inventory_infinite_element = EL_UNDEFINED;
3334 player->inventory_size = 0;
3336 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3337 SnapField(player, 0, 0);
3339 player->LevelSolved = FALSE;
3340 player->GameOver = FALSE;
3342 player->LevelSolved_GameWon = FALSE;
3343 player->LevelSolved_GameEnd = FALSE;
3344 player->LevelSolved_PanelOff = FALSE;
3345 player->LevelSolved_SaveTape = FALSE;
3346 player->LevelSolved_SaveScore = FALSE;
3349 network_player_action_received = FALSE;
3351 #if defined(NETWORK_AVALIABLE)
3352 /* initial null action */
3353 if (network_playing)
3354 SendToServer_MovePlayer(MV_NONE);
3363 TimeLeft = level.time;
3366 ScreenMovDir = MV_NONE;
3370 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3372 AllPlayersGone = FALSE;
3374 game.yamyam_content_nr = 0;
3375 game.robot_wheel_active = FALSE;
3376 game.magic_wall_active = FALSE;
3377 game.magic_wall_time_left = 0;
3378 game.light_time_left = 0;
3379 game.timegate_time_left = 0;
3380 game.switchgate_pos = 0;
3381 game.wind_direction = level.wind_direction_initial;
3383 #if !USE_PLAYER_GRAVITY
3384 game.gravity = FALSE;
3385 game.explosions_delayed = TRUE;
3388 game.lenses_time_left = 0;
3389 game.magnify_time_left = 0;
3391 game.ball_state = level.ball_state_initial;
3392 game.ball_content_nr = 0;
3394 game.envelope_active = FALSE;
3396 /* set focus to local player for network games, else to all players */
3397 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3398 game.centered_player_nr_next = game.centered_player_nr;
3399 game.set_centered_player = FALSE;
3401 if (network_playing && tape.recording)
3403 /* store client dependent player focus when recording network games */
3404 tape.centered_player_nr_next = game.centered_player_nr_next;
3405 tape.set_centered_player = TRUE;
3408 for (i = 0; i < NUM_BELTS; i++)
3410 game.belt_dir[i] = MV_NONE;
3411 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3414 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3415 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3417 SCAN_PLAYFIELD(x, y)
3419 Feld[x][y] = level.field[x][y];
3420 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3421 ChangeDelay[x][y] = 0;
3422 ChangePage[x][y] = -1;
3423 #if USE_NEW_CUSTOM_VALUE
3424 CustomValue[x][y] = 0; /* initialized in InitField() */
3426 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3428 WasJustMoving[x][y] = 0;
3429 WasJustFalling[x][y] = 0;
3430 CheckCollision[x][y] = 0;
3431 CheckImpact[x][y] = 0;
3433 Pushed[x][y] = FALSE;
3435 ChangeCount[x][y] = 0;
3436 ChangeEvent[x][y] = -1;
3438 ExplodePhase[x][y] = 0;
3439 ExplodeDelay[x][y] = 0;
3440 ExplodeField[x][y] = EX_TYPE_NONE;
3442 RunnerVisit[x][y] = 0;
3443 PlayerVisit[x][y] = 0;
3446 GfxRandom[x][y] = INIT_GFX_RANDOM();
3447 GfxElement[x][y] = EL_UNDEFINED;
3448 GfxAction[x][y] = ACTION_DEFAULT;
3449 GfxDir[x][y] = MV_NONE;
3452 SCAN_PLAYFIELD(x, y)
3454 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3456 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3458 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3461 InitField(x, y, TRUE);
3466 for (i = 0; i < MAX_PLAYERS; i++)
3468 struct PlayerInfo *player = &stored_player[i];
3470 /* set number of special actions for bored and sleeping animation */
3471 player->num_special_action_bored =
3472 get_num_special_action(player->artwork_element,
3473 ACTION_BORING_1, ACTION_BORING_LAST);
3474 player->num_special_action_sleeping =
3475 get_num_special_action(player->artwork_element,
3476 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3479 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3480 emulate_sb ? EMU_SOKOBAN :
3481 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3483 #if USE_NEW_ALL_SLIPPERY
3484 /* initialize type of slippery elements */
3485 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3487 if (!IS_CUSTOM_ELEMENT(i))
3489 /* default: elements slip down either to the left or right randomly */
3490 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3492 /* SP style elements prefer to slip down on the left side */
3493 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3494 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3496 /* BD style elements prefer to slip down on the left side */
3497 if (game.emulation == EMU_BOULDERDASH)
3498 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3503 /* initialize explosion and ignition delay */
3504 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3506 if (!IS_CUSTOM_ELEMENT(i))
3509 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3510 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3511 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3512 int last_phase = (num_phase + 1) * delay;
3513 int half_phase = (num_phase / 2) * delay;
3515 element_info[i].explosion_delay = last_phase - 1;
3516 element_info[i].ignition_delay = half_phase;
3518 if (i == EL_BLACK_ORB)
3519 element_info[i].ignition_delay = 1;
3523 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3524 element_info[i].explosion_delay = 1;
3526 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3527 element_info[i].ignition_delay = 1;
3531 /* correct non-moving belts to start moving left */
3532 for (i = 0; i < NUM_BELTS; i++)
3533 if (game.belt_dir[i] == MV_NONE)
3534 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3536 /* check if any connected player was not found in playfield */
3537 for (i = 0; i < MAX_PLAYERS; i++)
3539 struct PlayerInfo *player = &stored_player[i];
3541 if (player->connected && !player->present)
3543 for (j = 0; j < MAX_PLAYERS; j++)
3545 struct PlayerInfo *some_player = &stored_player[j];
3546 int jx = some_player->jx, jy = some_player->jy;
3548 /* assign first free player found that is present in the playfield */
3549 if (some_player->present && !some_player->connected)
3551 player->present = TRUE;
3552 player->active = TRUE;
3554 some_player->present = FALSE;
3555 some_player->active = FALSE;
3557 player->artwork_element = some_player->artwork_element;
3559 player->block_last_field = some_player->block_last_field;
3560 player->block_delay_adjustment = some_player->block_delay_adjustment;
3562 StorePlayer[jx][jy] = player->element_nr;
3563 player->jx = player->last_jx = jx;
3564 player->jy = player->last_jy = jy;
3574 /* when playing a tape, eliminate all players who do not participate */
3576 for (i = 0; i < MAX_PLAYERS; i++)
3578 if (stored_player[i].active && !tape.player_participates[i])
3580 struct PlayerInfo *player = &stored_player[i];
3581 int jx = player->jx, jy = player->jy;
3583 player->active = FALSE;
3584 StorePlayer[jx][jy] = 0;
3585 Feld[jx][jy] = EL_EMPTY;
3589 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3591 /* when in single player mode, eliminate all but the first active player */
3593 for (i = 0; i < MAX_PLAYERS; i++)
3595 if (stored_player[i].active)
3597 for (j = i + 1; j < MAX_PLAYERS; j++)
3599 if (stored_player[j].active)
3601 struct PlayerInfo *player = &stored_player[j];
3602 int jx = player->jx, jy = player->jy;
3604 player->active = FALSE;
3605 player->present = FALSE;
3607 StorePlayer[jx][jy] = 0;
3608 Feld[jx][jy] = EL_EMPTY;
3615 /* when recording the game, store which players take part in the game */
3618 for (i = 0; i < MAX_PLAYERS; i++)
3619 if (stored_player[i].active)
3620 tape.player_participates[i] = TRUE;
3625 for (i = 0; i < MAX_PLAYERS; i++)
3627 struct PlayerInfo *player = &stored_player[i];
3629 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3634 if (local_player == player)
3635 printf("Player %d is local player.\n", i+1);
3639 if (BorderElement == EL_EMPTY)
3642 SBX_Right = lev_fieldx - SCR_FIELDX;
3644 SBY_Lower = lev_fieldy - SCR_FIELDY;
3649 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3651 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3654 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3655 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3657 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3658 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3660 /* if local player not found, look for custom element that might create
3661 the player (make some assumptions about the right custom element) */
3662 if (!local_player->present)
3664 int start_x = 0, start_y = 0;
3665 int found_rating = 0;
3666 int found_element = EL_UNDEFINED;
3667 int player_nr = local_player->index_nr;
3669 SCAN_PLAYFIELD(x, y)
3671 int element = Feld[x][y];
3676 if (level.use_start_element[player_nr] &&
3677 level.start_element[player_nr] == element &&
3684 found_element = element;
3687 if (!IS_CUSTOM_ELEMENT(element))
3690 if (CAN_CHANGE(element))
3692 for (i = 0; i < element_info[element].num_change_pages; i++)
3694 /* check for player created from custom element as single target */
3695 content = element_info[element].change_page[i].target_element;
3696 is_player = ELEM_IS_PLAYER(content);
3698 if (is_player && (found_rating < 3 ||
3699 (found_rating == 3 && element < found_element)))
3705 found_element = element;
3710 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3712 /* check for player created from custom element as explosion content */
3713 content = element_info[element].content.e[xx][yy];
3714 is_player = ELEM_IS_PLAYER(content);
3716 if (is_player && (found_rating < 2 ||
3717 (found_rating == 2 && element < found_element)))
3719 start_x = x + xx - 1;
3720 start_y = y + yy - 1;
3723 found_element = element;
3726 if (!CAN_CHANGE(element))
3729 for (i = 0; i < element_info[element].num_change_pages; i++)
3731 /* check for player created from custom element as extended target */
3733 element_info[element].change_page[i].target_content.e[xx][yy];
3735 is_player = ELEM_IS_PLAYER(content);
3737 if (is_player && (found_rating < 1 ||
3738 (found_rating == 1 && element < found_element)))
3740 start_x = x + xx - 1;
3741 start_y = y + yy - 1;
3744 found_element = element;
3750 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3751 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3754 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3755 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3760 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3761 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3762 local_player->jx - MIDPOSX);
3764 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3765 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3766 local_player->jy - MIDPOSY);
3769 /* do not use PLAYING mask for fading out from main screen */
3770 game_status = GAME_MODE_MAIN;
3774 if (!game.restart_level)
3775 CloseDoor(DOOR_CLOSE_1);
3778 if (level_editor_test_game)
3779 FadeSkipNextFadeIn();
3781 FadeSetEnterScreen();
3783 if (level_editor_test_game)
3784 fading = fading_none;
3786 fading = menu.destination;
3790 FadeOut(REDRAW_FIELD);
3793 FadeOut(REDRAW_FIELD);
3796 game_status = GAME_MODE_PLAYING;
3798 /* !!! FIX THIS (START) !!! */
3799 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3801 InitGameEngine_EM();
3803 /* blit playfield from scroll buffer to normal back buffer for fading in */
3804 BlitScreenToBitmap_EM(backbuffer);
3811 /* after drawing the level, correct some elements */
3812 if (game.timegate_time_left == 0)
3813 CloseAllOpenTimegates();
3815 /* blit playfield from scroll buffer to normal back buffer for fading in */
3816 if (setup.soft_scrolling)
3817 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3819 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3821 /* !!! FIX THIS (END) !!! */
3824 FadeIn(REDRAW_FIELD);
3827 FadeIn(REDRAW_FIELD);
3832 if (!game.restart_level)
3834 /* copy default game door content to main double buffer */
3835 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3836 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3839 SetPanelBackground();
3840 SetDrawBackgroundMask(REDRAW_DOOR_1);
3842 UpdateGameDoorValues();
3843 DrawGameDoorValues();
3845 if (!game.restart_level)
3849 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3850 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3851 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3855 /* copy actual game door content to door double buffer for OpenDoor() */
3856 BlitBitmap(drawto, bitmap_db_door,
3857 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3859 OpenDoor(DOOR_OPEN_ALL);
3861 PlaySound(SND_GAME_STARTING);
3863 if (setup.sound_music)
3866 KeyboardAutoRepeatOffUnlessAutoplay();
3870 for (i = 0; i < MAX_PLAYERS; i++)
3871 printf("Player %d %sactive.\n",
3872 i + 1, (stored_player[i].active ? "" : "not "));
3883 game.restart_level = FALSE;
3886 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3888 /* this is used for non-R'n'D game engines to update certain engine values */
3890 /* needed to determine if sounds are played within the visible screen area */
3891 scroll_x = actual_scroll_x;
3892 scroll_y = actual_scroll_y;
3895 void InitMovDir(int x, int y)
3897 int i, element = Feld[x][y];
3898 static int xy[4][2] =
3905 static int direction[3][4] =
3907 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3908 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3909 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3918 Feld[x][y] = EL_BUG;
3919 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3922 case EL_SPACESHIP_RIGHT:
3923 case EL_SPACESHIP_UP:
3924 case EL_SPACESHIP_LEFT:
3925 case EL_SPACESHIP_DOWN:
3926 Feld[x][y] = EL_SPACESHIP;
3927 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3930 case EL_BD_BUTTERFLY_RIGHT:
3931 case EL_BD_BUTTERFLY_UP:
3932 case EL_BD_BUTTERFLY_LEFT:
3933 case EL_BD_BUTTERFLY_DOWN:
3934 Feld[x][y] = EL_BD_BUTTERFLY;
3935 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3938 case EL_BD_FIREFLY_RIGHT:
3939 case EL_BD_FIREFLY_UP:
3940 case EL_BD_FIREFLY_LEFT:
3941 case EL_BD_FIREFLY_DOWN:
3942 Feld[x][y] = EL_BD_FIREFLY;
3943 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3946 case EL_PACMAN_RIGHT:
3948 case EL_PACMAN_LEFT:
3949 case EL_PACMAN_DOWN:
3950 Feld[x][y] = EL_PACMAN;
3951 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3954 case EL_YAMYAM_LEFT:
3955 case EL_YAMYAM_RIGHT:
3957 case EL_YAMYAM_DOWN:
3958 Feld[x][y] = EL_YAMYAM;
3959 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3962 case EL_SP_SNIKSNAK:
3963 MovDir[x][y] = MV_UP;
3966 case EL_SP_ELECTRON:
3967 MovDir[x][y] = MV_LEFT;
3974 Feld[x][y] = EL_MOLE;
3975 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3979 if (IS_CUSTOM_ELEMENT(element))
3981 struct ElementInfo *ei = &element_info[element];
3982 int move_direction_initial = ei->move_direction_initial;
3983 int move_pattern = ei->move_pattern;
3985 if (move_direction_initial == MV_START_PREVIOUS)
3987 if (MovDir[x][y] != MV_NONE)
3990 move_direction_initial = MV_START_AUTOMATIC;
3993 if (move_direction_initial == MV_START_RANDOM)
3994 MovDir[x][y] = 1 << RND(4);
3995 else if (move_direction_initial & MV_ANY_DIRECTION)
3996 MovDir[x][y] = move_direction_initial;
3997 else if (move_pattern == MV_ALL_DIRECTIONS ||
3998 move_pattern == MV_TURNING_LEFT ||
3999 move_pattern == MV_TURNING_RIGHT ||
4000 move_pattern == MV_TURNING_LEFT_RIGHT ||
4001 move_pattern == MV_TURNING_RIGHT_LEFT ||
4002 move_pattern == MV_TURNING_RANDOM)
4003 MovDir[x][y] = 1 << RND(4);
4004 else if (move_pattern == MV_HORIZONTAL)
4005 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4006 else if (move_pattern == MV_VERTICAL)
4007 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4008 else if (move_pattern & MV_ANY_DIRECTION)
4009 MovDir[x][y] = element_info[element].move_pattern;
4010 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4011 move_pattern == MV_ALONG_RIGHT_SIDE)
4013 /* use random direction as default start direction */
4014 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4015 MovDir[x][y] = 1 << RND(4);
4017 for (i = 0; i < NUM_DIRECTIONS; i++)
4019 int x1 = x + xy[i][0];
4020 int y1 = y + xy[i][1];
4022 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4024 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4025 MovDir[x][y] = direction[0][i];
4027 MovDir[x][y] = direction[1][i];
4036 MovDir[x][y] = 1 << RND(4);
4038 if (element != EL_BUG &&
4039 element != EL_SPACESHIP &&
4040 element != EL_BD_BUTTERFLY &&
4041 element != EL_BD_FIREFLY)
4044 for (i = 0; i < NUM_DIRECTIONS; i++)
4046 int x1 = x + xy[i][0];
4047 int y1 = y + xy[i][1];
4049 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4051 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4053 MovDir[x][y] = direction[0][i];
4056 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4057 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4059 MovDir[x][y] = direction[1][i];
4068 GfxDir[x][y] = MovDir[x][y];
4071 void InitAmoebaNr(int x, int y)
4074 int group_nr = AmoebeNachbarNr(x, y);
4078 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4080 if (AmoebaCnt[i] == 0)
4088 AmoebaNr[x][y] = group_nr;
4089 AmoebaCnt[group_nr]++;
4090 AmoebaCnt2[group_nr]++;
4093 static void PlayerWins(struct PlayerInfo *player)
4095 player->LevelSolved = TRUE;
4096 player->GameOver = TRUE;
4098 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4099 level.native_em_level->lev->score : player->score);
4104 static int time, time_final;
4105 static int score, score_final;
4106 static int game_over_delay_1 = 0;
4107 static int game_over_delay_2 = 0;
4108 int game_over_delay_value_1 = 50;
4109 int game_over_delay_value_2 = 50;
4111 if (!local_player->LevelSolved_GameWon)
4115 /* do not start end game actions before the player stops moving (to exit) */
4116 if (local_player->MovPos)
4119 local_player->LevelSolved_GameWon = TRUE;
4120 local_player->LevelSolved_SaveTape = tape.recording;
4121 local_player->LevelSolved_SaveScore = !tape.playing;
4123 if (tape.auto_play) /* tape might already be stopped here */
4124 tape.auto_play_level_solved = TRUE;
4130 game_over_delay_1 = game_over_delay_value_1;
4131 game_over_delay_2 = game_over_delay_value_2;
4133 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4134 score = score_final = local_player->score_final;
4139 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4141 else if (level.time == 0 && TimePlayed < 999)
4144 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4147 local_player->score_final = score_final;
4149 if (level_editor_test_game)
4152 score = score_final;
4155 game_panel_controls[GAME_PANEL_TIME].value = time;
4156 game_panel_controls[GAME_PANEL_SCORE].value = score;
4158 DisplayGameControlValues();
4160 DrawGameValue_Time(time);
4161 DrawGameValue_Score(score);
4165 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4167 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4169 /* close exit door after last player */
4170 if ((AllPlayersGone &&
4171 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4172 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4173 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4174 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4175 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4177 int element = Feld[ExitX][ExitY];
4180 if (element == EL_EM_EXIT_OPEN ||
4181 element == EL_EM_STEEL_EXIT_OPEN)
4188 Feld[ExitX][ExitY] =
4189 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4190 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4191 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4192 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4193 EL_EM_STEEL_EXIT_CLOSING);
4195 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4199 /* player disappears */
4200 DrawLevelField(ExitX, ExitY);
4203 for (i = 0; i < MAX_PLAYERS; i++)
4205 struct PlayerInfo *player = &stored_player[i];
4207 if (player->present)
4209 RemovePlayer(player);
4211 /* player disappears */
4212 DrawLevelField(player->jx, player->jy);
4217 PlaySound(SND_GAME_WINNING);
4220 if (game_over_delay_1 > 0)
4222 game_over_delay_1--;
4227 if (time != time_final)
4229 int time_to_go = ABS(time_final - time);
4230 int time_count_dir = (time < time_final ? +1 : -1);
4231 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4233 time += time_count_steps * time_count_dir;
4234 score += time_count_steps * level.score[SC_TIME_BONUS];
4237 game_panel_controls[GAME_PANEL_TIME].value = time;
4238 game_panel_controls[GAME_PANEL_SCORE].value = score;
4240 DisplayGameControlValues();
4242 DrawGameValue_Time(time);
4243 DrawGameValue_Score(score);
4246 if (time == time_final)
4247 StopSound(SND_GAME_LEVELTIME_BONUS);
4248 else if (setup.sound_loops)
4249 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4251 PlaySound(SND_GAME_LEVELTIME_BONUS);
4256 local_player->LevelSolved_PanelOff = TRUE;
4258 if (game_over_delay_2 > 0)
4260 game_over_delay_2--;
4273 boolean raise_level = FALSE;
4275 local_player->LevelSolved_GameEnd = TRUE;
4277 CloseDoor(DOOR_CLOSE_1);
4279 if (local_player->LevelSolved_SaveTape)
4286 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4288 SaveTape(tape.level_nr); /* ask to save tape */
4292 if (level_editor_test_game)
4294 game_status = GAME_MODE_MAIN;
4297 DrawAndFadeInMainMenu(REDRAW_FIELD);
4305 if (!local_player->LevelSolved_SaveScore)
4308 FadeOut(REDRAW_FIELD);
4311 game_status = GAME_MODE_MAIN;
4313 DrawAndFadeInMainMenu(REDRAW_FIELD);
4318 if (level_nr == leveldir_current->handicap_level)
4320 leveldir_current->handicap_level++;
4321 SaveLevelSetup_SeriesInfo();
4324 if (level_nr < leveldir_current->last_level)
4325 raise_level = TRUE; /* advance to next level */
4327 if ((hi_pos = NewHiScore()) >= 0)
4329 game_status = GAME_MODE_SCORES;
4331 DrawHallOfFame(hi_pos);
4342 FadeOut(REDRAW_FIELD);
4345 game_status = GAME_MODE_MAIN;
4353 DrawAndFadeInMainMenu(REDRAW_FIELD);
4362 LoadScore(level_nr);
4364 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4365 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4368 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4370 if (local_player->score_final > highscore[k].Score)
4372 /* player has made it to the hall of fame */
4374 if (k < MAX_SCORE_ENTRIES - 1)
4376 int m = MAX_SCORE_ENTRIES - 1;
4379 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4380 if (strEqual(setup.player_name, highscore[l].Name))
4382 if (m == k) /* player's new highscore overwrites his old one */
4386 for (l = m; l > k; l--)
4388 strcpy(highscore[l].Name, highscore[l - 1].Name);
4389 highscore[l].Score = highscore[l - 1].Score;
4396 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4397 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4398 highscore[k].Score = local_player->score_final;
4404 else if (!strncmp(setup.player_name, highscore[k].Name,
4405 MAX_PLAYER_NAME_LEN))
4406 break; /* player already there with a higher score */
4412 SaveScore(level_nr);
4417 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4419 int element = Feld[x][y];
4420 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4421 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4422 int horiz_move = (dx != 0);
4423 int sign = (horiz_move ? dx : dy);
4424 int step = sign * element_info[element].move_stepsize;
4426 /* special values for move stepsize for spring and things on conveyor belt */
4429 if (CAN_FALL(element) &&
4430 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4431 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4432 else if (element == EL_SPRING)
4433 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4439 inline static int getElementMoveStepsize(int x, int y)
4441 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4444 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4446 if (player->GfxAction != action || player->GfxDir != dir)
4449 printf("Player frame reset! (%d => %d, %d => %d)\n",
4450 player->GfxAction, action, player->GfxDir, dir);
4453 player->GfxAction = action;
4454 player->GfxDir = dir;
4456 player->StepFrame = 0;
4460 #if USE_GFX_RESET_GFX_ANIMATION
4461 static void ResetGfxFrame(int x, int y, boolean redraw)
4463 int element = Feld[x][y];
4464 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4465 int last_gfx_frame = GfxFrame[x][y];
4467 if (graphic_info[graphic].anim_global_sync)
4468 GfxFrame[x][y] = FrameCounter;
4469 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4470 GfxFrame[x][y] = CustomValue[x][y];
4471 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4472 GfxFrame[x][y] = element_info[element].collect_score;
4473 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4474 GfxFrame[x][y] = ChangeDelay[x][y];
4476 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4477 DrawLevelGraphicAnimation(x, y, graphic);
4481 static void ResetGfxAnimation(int x, int y)
4483 GfxAction[x][y] = ACTION_DEFAULT;
4484 GfxDir[x][y] = MovDir[x][y];
4487 #if USE_GFX_RESET_GFX_ANIMATION
4488 ResetGfxFrame(x, y, FALSE);
4492 static void ResetRandomAnimationValue(int x, int y)
4494 GfxRandom[x][y] = INIT_GFX_RANDOM();
4497 void InitMovingField(int x, int y, int direction)
4499 int element = Feld[x][y];
4500 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4501 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4504 boolean is_moving_before, is_moving_after;
4506 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4509 /* check if element was/is moving or being moved before/after mode change */
4512 is_moving_before = (WasJustMoving[x][y] != 0);
4514 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4515 is_moving_before = WasJustMoving[x][y];
4518 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4520 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4522 /* reset animation only for moving elements which change direction of moving
4523 or which just started or stopped moving
4524 (else CEs with property "can move" / "not moving" are reset each frame) */
4525 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4527 if (is_moving_before != is_moving_after ||
4528 direction != MovDir[x][y])
4529 ResetGfxAnimation(x, y);
4531 if ((is_moving_before || is_moving_after) && !continues_moving)
4532 ResetGfxAnimation(x, y);
4535 if (!continues_moving)
4536 ResetGfxAnimation(x, y);
4539 MovDir[x][y] = direction;
4540 GfxDir[x][y] = direction;
4542 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4543 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4544 direction == MV_DOWN && CAN_FALL(element) ?
4545 ACTION_FALLING : ACTION_MOVING);
4547 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4548 ACTION_FALLING : ACTION_MOVING);
4551 /* this is needed for CEs with property "can move" / "not moving" */
4553 if (is_moving_after)
4555 if (Feld[newx][newy] == EL_EMPTY)
4556 Feld[newx][newy] = EL_BLOCKED;
4558 MovDir[newx][newy] = MovDir[x][y];
4560 #if USE_NEW_CUSTOM_VALUE
4561 CustomValue[newx][newy] = CustomValue[x][y];
4564 GfxFrame[newx][newy] = GfxFrame[x][y];
4565 GfxRandom[newx][newy] = GfxRandom[x][y];
4566 GfxAction[newx][newy] = GfxAction[x][y];
4567 GfxDir[newx][newy] = GfxDir[x][y];
4571 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4573 int direction = MovDir[x][y];
4574 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4575 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4581 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4583 int oldx = x, oldy = y;
4584 int direction = MovDir[x][y];
4586 if (direction == MV_LEFT)
4588 else if (direction == MV_RIGHT)
4590 else if (direction == MV_UP)
4592 else if (direction == MV_DOWN)
4595 *comes_from_x = oldx;
4596 *comes_from_y = oldy;
4599 int MovingOrBlocked2Element(int x, int y)
4601 int element = Feld[x][y];
4603 if (element == EL_BLOCKED)
4607 Blocked2Moving(x, y, &oldx, &oldy);
4608 return Feld[oldx][oldy];
4614 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4616 /* like MovingOrBlocked2Element(), but if element is moving
4617 and (x,y) is the field the moving element is just leaving,
4618 return EL_BLOCKED instead of the element value */
4619 int element = Feld[x][y];
4621 if (IS_MOVING(x, y))
4623 if (element == EL_BLOCKED)
4627 Blocked2Moving(x, y, &oldx, &oldy);
4628 return Feld[oldx][oldy];
4637 static void RemoveField(int x, int y)
4639 Feld[x][y] = EL_EMPTY;
4645 #if USE_NEW_CUSTOM_VALUE
4646 CustomValue[x][y] = 0;
4650 ChangeDelay[x][y] = 0;
4651 ChangePage[x][y] = -1;
4652 Pushed[x][y] = FALSE;
4655 ExplodeField[x][y] = EX_TYPE_NONE;
4658 GfxElement[x][y] = EL_UNDEFINED;
4659 GfxAction[x][y] = ACTION_DEFAULT;
4660 GfxDir[x][y] = MV_NONE;
4663 void RemoveMovingField(int x, int y)
4665 int oldx = x, oldy = y, newx = x, newy = y;
4666 int element = Feld[x][y];
4667 int next_element = EL_UNDEFINED;
4669 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4672 if (IS_MOVING(x, y))
4674 Moving2Blocked(x, y, &newx, &newy);
4676 if (Feld[newx][newy] != EL_BLOCKED)
4678 /* element is moving, but target field is not free (blocked), but
4679 already occupied by something different (example: acid pool);
4680 in this case, only remove the moving field, but not the target */
4682 RemoveField(oldx, oldy);
4684 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4686 DrawLevelField(oldx, oldy);
4691 else if (element == EL_BLOCKED)
4693 Blocked2Moving(x, y, &oldx, &oldy);
4694 if (!IS_MOVING(oldx, oldy))
4698 if (element == EL_BLOCKED &&
4699 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4700 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4701 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4702 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4703 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4704 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4705 next_element = get_next_element(Feld[oldx][oldy]);
4707 RemoveField(oldx, oldy);
4708 RemoveField(newx, newy);
4710 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4712 if (next_element != EL_UNDEFINED)
4713 Feld[oldx][oldy] = next_element;
4715 DrawLevelField(oldx, oldy);
4716 DrawLevelField(newx, newy);
4719 void DrawDynamite(int x, int y)
4721 int sx = SCREENX(x), sy = SCREENY(y);
4722 int graphic = el2img(Feld[x][y]);
4725 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4728 if (IS_WALKABLE_INSIDE(Back[x][y]))
4732 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4733 else if (Store[x][y])
4734 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4736 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4738 if (Back[x][y] || Store[x][y])
4739 DrawGraphicThruMask(sx, sy, graphic, frame);
4741 DrawGraphic(sx, sy, graphic, frame);
4744 void CheckDynamite(int x, int y)
4746 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4750 if (MovDelay[x][y] != 0)
4753 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4759 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4764 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4766 boolean num_checked_players = 0;
4769 for (i = 0; i < MAX_PLAYERS; i++)
4771 if (stored_player[i].active)
4773 int sx = stored_player[i].jx;
4774 int sy = stored_player[i].jy;
4776 if (num_checked_players == 0)
4783 *sx1 = MIN(*sx1, sx);
4784 *sy1 = MIN(*sy1, sy);
4785 *sx2 = MAX(*sx2, sx);
4786 *sy2 = MAX(*sy2, sy);
4789 num_checked_players++;
4794 static boolean checkIfAllPlayersFitToScreen_RND()
4796 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4798 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4800 return (sx2 - sx1 < SCR_FIELDX &&
4801 sy2 - sy1 < SCR_FIELDY);
4804 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4806 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4808 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4810 *sx = (sx1 + sx2) / 2;
4811 *sy = (sy1 + sy2) / 2;
4814 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4815 boolean center_screen, boolean quick_relocation)
4817 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4818 boolean no_delay = (tape.warp_forward);
4819 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4820 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4822 if (quick_relocation)
4824 int offset = game.scroll_delay_value;
4826 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4828 if (!level.shifted_relocation || center_screen)
4830 /* quick relocation (without scrolling), with centering of screen */
4832 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4833 x > SBX_Right + MIDPOSX ? SBX_Right :
4836 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4837 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4842 /* quick relocation (without scrolling), but do not center screen */
4844 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4845 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4848 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4849 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4852 int offset_x = x + (scroll_x - center_scroll_x);
4853 int offset_y = y + (scroll_y - center_scroll_y);
4855 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4856 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4857 offset_x - MIDPOSX);
4859 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4860 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4861 offset_y - MIDPOSY);
4866 /* quick relocation (without scrolling), inside visible screen area */
4868 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4869 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4870 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4872 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4873 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4874 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4876 /* don't scroll over playfield boundaries */
4877 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4878 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4880 /* don't scroll over playfield boundaries */
4881 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4882 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4885 RedrawPlayfield(TRUE, 0,0,0,0);
4890 int scroll_xx, scroll_yy;
4892 if (!level.shifted_relocation || center_screen)
4894 /* visible relocation (with scrolling), with centering of screen */
4896 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4897 x > SBX_Right + MIDPOSX ? SBX_Right :
4900 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4901 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4906 /* visible relocation (with scrolling), but do not center screen */
4908 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4909 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4912 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4913 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4916 int offset_x = x + (scroll_x - center_scroll_x);
4917 int offset_y = y + (scroll_y - center_scroll_y);
4919 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4920 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4921 offset_x - MIDPOSX);
4923 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4924 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4925 offset_y - MIDPOSY);
4930 /* visible relocation (with scrolling), with centering of screen */
4932 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4933 x > SBX_Right + MIDPOSX ? SBX_Right :
4936 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4937 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4941 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4943 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4946 int fx = FX, fy = FY;
4948 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4949 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4951 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4957 fx += dx * TILEX / 2;
4958 fy += dy * TILEY / 2;
4960 ScrollLevel(dx, dy);
4963 /* scroll in two steps of half tile size to make things smoother */
4964 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4966 Delay(wait_delay_value);
4968 /* scroll second step to align at full tile size */
4970 Delay(wait_delay_value);
4975 Delay(wait_delay_value);
4979 void RelocatePlayer(int jx, int jy, int el_player_raw)
4981 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4982 int player_nr = GET_PLAYER_NR(el_player);
4983 struct PlayerInfo *player = &stored_player[player_nr];
4984 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4985 boolean no_delay = (tape.warp_forward);
4986 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4987 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4988 int old_jx = player->jx;
4989 int old_jy = player->jy;
4990 int old_element = Feld[old_jx][old_jy];
4991 int element = Feld[jx][jy];
4992 boolean player_relocated = (old_jx != jx || old_jy != jy);
4994 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4995 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4996 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4997 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4998 int leave_side_horiz = move_dir_horiz;
4999 int leave_side_vert = move_dir_vert;
5000 int enter_side = enter_side_horiz | enter_side_vert;
5001 int leave_side = leave_side_horiz | leave_side_vert;
5003 if (player->GameOver) /* do not reanimate dead player */
5006 if (!player_relocated) /* no need to relocate the player */
5009 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5011 RemoveField(jx, jy); /* temporarily remove newly placed player */
5012 DrawLevelField(jx, jy);
5015 if (player->present)
5017 while (player->MovPos)
5019 ScrollPlayer(player, SCROLL_GO_ON);
5020 ScrollScreen(NULL, SCROLL_GO_ON);
5022 AdvanceFrameAndPlayerCounters(player->index_nr);
5027 Delay(wait_delay_value);
5030 DrawPlayer(player); /* needed here only to cleanup last field */
5031 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5033 player->is_moving = FALSE;
5036 if (IS_CUSTOM_ELEMENT(old_element))
5037 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5039 player->index_bit, leave_side);
5041 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5043 player->index_bit, leave_side);
5045 Feld[jx][jy] = el_player;
5046 InitPlayerField(jx, jy, el_player, TRUE);
5048 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5050 Feld[jx][jy] = element;
5051 InitField(jx, jy, FALSE);
5054 /* only visually relocate centered player */
5055 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5056 FALSE, level.instant_relocation);
5058 TestIfPlayerTouchesBadThing(jx, jy);
5059 TestIfPlayerTouchesCustomElement(jx, jy);
5061 if (IS_CUSTOM_ELEMENT(element))
5062 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5063 player->index_bit, enter_side);
5065 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5066 player->index_bit, enter_side);
5069 void Explode(int ex, int ey, int phase, int mode)
5075 /* !!! eliminate this variable !!! */
5076 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5078 if (game.explosions_delayed)
5080 ExplodeField[ex][ey] = mode;
5084 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5086 int center_element = Feld[ex][ey];
5087 int artwork_element, explosion_element; /* set these values later */
5090 /* --- This is only really needed (and now handled) in "Impact()". --- */
5091 /* do not explode moving elements that left the explode field in time */
5092 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5093 center_element == EL_EMPTY &&
5094 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5099 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5100 if (mode == EX_TYPE_NORMAL ||
5101 mode == EX_TYPE_CENTER ||
5102 mode == EX_TYPE_CROSS)
5103 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5106 /* remove things displayed in background while burning dynamite */
5107 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5110 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5112 /* put moving element to center field (and let it explode there) */
5113 center_element = MovingOrBlocked2Element(ex, ey);
5114 RemoveMovingField(ex, ey);
5115 Feld[ex][ey] = center_element;
5118 /* now "center_element" is finally determined -- set related values now */
5119 artwork_element = center_element; /* for custom player artwork */
5120 explosion_element = center_element; /* for custom player artwork */
5122 if (IS_PLAYER(ex, ey))
5124 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5126 artwork_element = stored_player[player_nr].artwork_element;
5128 if (level.use_explosion_element[player_nr])
5130 explosion_element = level.explosion_element[player_nr];
5131 artwork_element = explosion_element;
5136 if (mode == EX_TYPE_NORMAL ||
5137 mode == EX_TYPE_CENTER ||
5138 mode == EX_TYPE_CROSS)
5139 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5142 last_phase = element_info[explosion_element].explosion_delay + 1;
5144 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5146 int xx = x - ex + 1;
5147 int yy = y - ey + 1;
5150 if (!IN_LEV_FIELD(x, y) ||
5151 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5152 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5155 element = Feld[x][y];
5157 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5159 element = MovingOrBlocked2Element(x, y);
5161 if (!IS_EXPLOSION_PROOF(element))
5162 RemoveMovingField(x, y);
5165 /* indestructible elements can only explode in center (but not flames) */
5166 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5167 mode == EX_TYPE_BORDER)) ||
5168 element == EL_FLAMES)
5171 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5172 behaviour, for example when touching a yamyam that explodes to rocks
5173 with active deadly shield, a rock is created under the player !!! */
5174 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5176 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5177 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5178 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5180 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5183 if (IS_ACTIVE_BOMB(element))
5185 /* re-activate things under the bomb like gate or penguin */
5186 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5193 /* save walkable background elements while explosion on same tile */
5194 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5195 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5196 Back[x][y] = element;
5198 /* ignite explodable elements reached by other explosion */
5199 if (element == EL_EXPLOSION)
5200 element = Store2[x][y];
5202 if (AmoebaNr[x][y] &&
5203 (element == EL_AMOEBA_FULL ||
5204 element == EL_BD_AMOEBA ||
5205 element == EL_AMOEBA_GROWING))
5207 AmoebaCnt[AmoebaNr[x][y]]--;
5208 AmoebaCnt2[AmoebaNr[x][y]]--;
5213 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5215 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5217 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5219 if (PLAYERINFO(ex, ey)->use_murphy)
5220 Store[x][y] = EL_EMPTY;
5223 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5224 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5225 else if (ELEM_IS_PLAYER(center_element))
5226 Store[x][y] = EL_EMPTY;
5227 else if (center_element == EL_YAMYAM)
5228 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5229 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5230 Store[x][y] = element_info[center_element].content.e[xx][yy];
5232 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5233 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5234 otherwise) -- FIX THIS !!! */
5235 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5236 Store[x][y] = element_info[element].content.e[1][1];
5238 else if (!CAN_EXPLODE(element))
5239 Store[x][y] = element_info[element].content.e[1][1];
5242 Store[x][y] = EL_EMPTY;
5244 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5245 center_element == EL_AMOEBA_TO_DIAMOND)
5246 Store2[x][y] = element;
5248 Feld[x][y] = EL_EXPLOSION;
5249 GfxElement[x][y] = artwork_element;
5251 ExplodePhase[x][y] = 1;
5252 ExplodeDelay[x][y] = last_phase;
5257 if (center_element == EL_YAMYAM)
5258 game.yamyam_content_nr =
5259 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5271 GfxFrame[x][y] = 0; /* restart explosion animation */
5273 last_phase = ExplodeDelay[x][y];
5275 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5279 /* activate this even in non-DEBUG version until cause for crash in
5280 getGraphicAnimationFrame() (see below) is found and eliminated */
5286 /* this can happen if the player leaves an explosion just in time */
5287 if (GfxElement[x][y] == EL_UNDEFINED)
5288 GfxElement[x][y] = EL_EMPTY;
5290 if (GfxElement[x][y] == EL_UNDEFINED)
5293 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5294 printf("Explode(): This should never happen!\n");
5297 GfxElement[x][y] = EL_EMPTY;
5303 border_element = Store2[x][y];
5304 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5305 border_element = StorePlayer[x][y];
5307 if (phase == element_info[border_element].ignition_delay ||
5308 phase == last_phase)
5310 boolean border_explosion = FALSE;
5312 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5313 !PLAYER_EXPLOSION_PROTECTED(x, y))
5315 KillPlayerUnlessExplosionProtected(x, y);
5316 border_explosion = TRUE;
5318 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5320 Feld[x][y] = Store2[x][y];
5323 border_explosion = TRUE;
5325 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5327 AmoebeUmwandeln(x, y);
5329 border_explosion = TRUE;
5332 /* if an element just explodes due to another explosion (chain-reaction),
5333 do not immediately end the new explosion when it was the last frame of
5334 the explosion (as it would be done in the following "if"-statement!) */
5335 if (border_explosion && phase == last_phase)
5339 if (phase == last_phase)
5343 element = Feld[x][y] = Store[x][y];
5344 Store[x][y] = Store2[x][y] = 0;
5345 GfxElement[x][y] = EL_UNDEFINED;
5347 /* player can escape from explosions and might therefore be still alive */
5348 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5349 element <= EL_PLAYER_IS_EXPLODING_4)
5351 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5352 int explosion_element = EL_PLAYER_1 + player_nr;
5353 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5354 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5356 if (level.use_explosion_element[player_nr])
5357 explosion_element = level.explosion_element[player_nr];
5359 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5360 element_info[explosion_element].content.e[xx][yy]);
5363 /* restore probably existing indestructible background element */
5364 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5365 element = Feld[x][y] = Back[x][y];
5368 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5369 GfxDir[x][y] = MV_NONE;
5370 ChangeDelay[x][y] = 0;
5371 ChangePage[x][y] = -1;
5373 #if USE_NEW_CUSTOM_VALUE
5374 CustomValue[x][y] = 0;
5377 InitField_WithBug2(x, y, FALSE);
5379 DrawLevelField(x, y);
5381 TestIfElementTouchesCustomElement(x, y);
5383 if (GFX_CRUMBLED(element))
5384 DrawLevelFieldCrumbledSandNeighbours(x, y);
5386 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5387 StorePlayer[x][y] = 0;
5389 if (ELEM_IS_PLAYER(element))
5390 RelocatePlayer(x, y, element);
5392 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5394 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5395 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5398 DrawLevelFieldCrumbledSand(x, y);
5400 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5402 DrawLevelElement(x, y, Back[x][y]);
5403 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5405 else if (IS_WALKABLE_UNDER(Back[x][y]))
5407 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5408 DrawLevelElementThruMask(x, y, Back[x][y]);
5410 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5411 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5415 void DynaExplode(int ex, int ey)
5418 int dynabomb_element = Feld[ex][ey];
5419 int dynabomb_size = 1;
5420 boolean dynabomb_xl = FALSE;
5421 struct PlayerInfo *player;
5422 static int xy[4][2] =
5430 if (IS_ACTIVE_BOMB(dynabomb_element))
5432 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5433 dynabomb_size = player->dynabomb_size;
5434 dynabomb_xl = player->dynabomb_xl;
5435 player->dynabombs_left++;
5438 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5440 for (i = 0; i < NUM_DIRECTIONS; i++)
5442 for (j = 1; j <= dynabomb_size; j++)
5444 int x = ex + j * xy[i][0];
5445 int y = ey + j * xy[i][1];
5448 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5451 element = Feld[x][y];
5453 /* do not restart explosions of fields with active bombs */
5454 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5457 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5459 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5460 !IS_DIGGABLE(element) && !dynabomb_xl)
5466 void Bang(int x, int y)
5468 int element = MovingOrBlocked2Element(x, y);
5469 int explosion_type = EX_TYPE_NORMAL;
5471 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5473 struct PlayerInfo *player = PLAYERINFO(x, y);
5475 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5476 player->element_nr);
5478 if (level.use_explosion_element[player->index_nr])
5480 int explosion_element = level.explosion_element[player->index_nr];
5482 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5483 explosion_type = EX_TYPE_CROSS;
5484 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5485 explosion_type = EX_TYPE_CENTER;
5493 case EL_BD_BUTTERFLY:
5496 case EL_DARK_YAMYAM:
5500 RaiseScoreElement(element);
5503 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5504 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5505 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5506 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5507 case EL_DYNABOMB_INCREASE_NUMBER:
5508 case EL_DYNABOMB_INCREASE_SIZE:
5509 case EL_DYNABOMB_INCREASE_POWER:
5510 explosion_type = EX_TYPE_DYNA;
5513 case EL_DC_LANDMINE:
5515 case EL_EM_EXIT_OPEN:
5516 case EL_EM_STEEL_EXIT_OPEN:
5518 explosion_type = EX_TYPE_CENTER;
5523 case EL_LAMP_ACTIVE:
5524 case EL_AMOEBA_TO_DIAMOND:
5525 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5526 explosion_type = EX_TYPE_CENTER;
5530 if (element_info[element].explosion_type == EXPLODES_CROSS)
5531 explosion_type = EX_TYPE_CROSS;
5532 else if (element_info[element].explosion_type == EXPLODES_1X1)
5533 explosion_type = EX_TYPE_CENTER;
5537 if (explosion_type == EX_TYPE_DYNA)
5540 Explode(x, y, EX_PHASE_START, explosion_type);
5542 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5545 void SplashAcid(int x, int y)
5547 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5548 (!IN_LEV_FIELD(x - 1, y - 2) ||
5549 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5550 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5552 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5553 (!IN_LEV_FIELD(x + 1, y - 2) ||
5554 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5555 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5557 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5560 static void InitBeltMovement()
5562 static int belt_base_element[4] =
5564 EL_CONVEYOR_BELT_1_LEFT,
5565 EL_CONVEYOR_BELT_2_LEFT,
5566 EL_CONVEYOR_BELT_3_LEFT,
5567 EL_CONVEYOR_BELT_4_LEFT
5569 static int belt_base_active_element[4] =
5571 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5572 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5573 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5574 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5579 /* set frame order for belt animation graphic according to belt direction */
5580 for (i = 0; i < NUM_BELTS; i++)
5584 for (j = 0; j < NUM_BELT_PARTS; j++)
5586 int element = belt_base_active_element[belt_nr] + j;
5587 int graphic_1 = el2img(element);
5588 int graphic_2 = el2panelimg(element);
5590 if (game.belt_dir[i] == MV_LEFT)
5592 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5593 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5597 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5598 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5603 SCAN_PLAYFIELD(x, y)
5605 int element = Feld[x][y];
5607 for (i = 0; i < NUM_BELTS; i++)
5609 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5611 int e_belt_nr = getBeltNrFromBeltElement(element);
5614 if (e_belt_nr == belt_nr)
5616 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5618 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5625 static void ToggleBeltSwitch(int x, int y)
5627 static int belt_base_element[4] =
5629 EL_CONVEYOR_BELT_1_LEFT,
5630 EL_CONVEYOR_BELT_2_LEFT,
5631 EL_CONVEYOR_BELT_3_LEFT,
5632 EL_CONVEYOR_BELT_4_LEFT
5634 static int belt_base_active_element[4] =
5636 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5637 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5638 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5639 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5641 static int belt_base_switch_element[4] =
5643 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5644 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5645 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5646 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5648 static int belt_move_dir[4] =
5656 int element = Feld[x][y];
5657 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5658 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5659 int belt_dir = belt_move_dir[belt_dir_nr];
5662 if (!IS_BELT_SWITCH(element))
5665 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5666 game.belt_dir[belt_nr] = belt_dir;
5668 if (belt_dir_nr == 3)
5671 /* set frame order for belt animation graphic according to belt direction */
5672 for (i = 0; i < NUM_BELT_PARTS; i++)
5674 int element = belt_base_active_element[belt_nr] + i;
5675 int graphic_1 = el2img(element);
5676 int graphic_2 = el2panelimg(element);
5678 if (belt_dir == MV_LEFT)
5680 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5681 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5685 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5686 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5690 SCAN_PLAYFIELD(xx, yy)
5692 int element = Feld[xx][yy];
5694 if (IS_BELT_SWITCH(element))
5696 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5698 if (e_belt_nr == belt_nr)
5700 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5701 DrawLevelField(xx, yy);
5704 else if (IS_BELT(element) && belt_dir != MV_NONE)
5706 int e_belt_nr = getBeltNrFromBeltElement(element);
5708 if (e_belt_nr == belt_nr)
5710 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5712 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5713 DrawLevelField(xx, yy);
5716 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5718 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5720 if (e_belt_nr == belt_nr)
5722 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5724 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5725 DrawLevelField(xx, yy);
5731 static void ToggleSwitchgateSwitch(int x, int y)
5735 game.switchgate_pos = !game.switchgate_pos;
5737 SCAN_PLAYFIELD(xx, yy)
5739 int element = Feld[xx][yy];
5741 #if !USE_BOTH_SWITCHGATE_SWITCHES
5742 if (element == EL_SWITCHGATE_SWITCH_UP ||
5743 element == EL_SWITCHGATE_SWITCH_DOWN)
5745 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5746 DrawLevelField(xx, yy);
5748 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5749 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5751 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5752 DrawLevelField(xx, yy);
5755 if (element == EL_SWITCHGATE_SWITCH_UP)
5757 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5758 DrawLevelField(xx, yy);
5760 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5762 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5763 DrawLevelField(xx, yy);
5765 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5767 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5768 DrawLevelField(xx, yy);
5770 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5772 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5773 DrawLevelField(xx, yy);
5776 else if (element == EL_SWITCHGATE_OPEN ||
5777 element == EL_SWITCHGATE_OPENING)
5779 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5781 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5783 else if (element == EL_SWITCHGATE_CLOSED ||
5784 element == EL_SWITCHGATE_CLOSING)
5786 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5788 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5793 static int getInvisibleActiveFromInvisibleElement(int element)
5795 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5796 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5797 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5801 static int getInvisibleFromInvisibleActiveElement(int element)
5803 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5804 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5805 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5809 static void RedrawAllLightSwitchesAndInvisibleElements()
5813 SCAN_PLAYFIELD(x, y)
5815 int element = Feld[x][y];
5817 if (element == EL_LIGHT_SWITCH &&
5818 game.light_time_left > 0)
5820 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5821 DrawLevelField(x, y);
5823 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5824 game.light_time_left == 0)
5826 Feld[x][y] = EL_LIGHT_SWITCH;
5827 DrawLevelField(x, y);
5829 else if (element == EL_EMC_DRIPPER &&
5830 game.light_time_left > 0)
5832 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5833 DrawLevelField(x, y);
5835 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5836 game.light_time_left == 0)
5838 Feld[x][y] = EL_EMC_DRIPPER;
5839 DrawLevelField(x, y);
5841 else if (element == EL_INVISIBLE_STEELWALL ||
5842 element == EL_INVISIBLE_WALL ||
5843 element == EL_INVISIBLE_SAND)
5845 if (game.light_time_left > 0)
5846 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5848 DrawLevelField(x, y);
5850 /* uncrumble neighbour fields, if needed */
5851 if (element == EL_INVISIBLE_SAND)
5852 DrawLevelFieldCrumbledSandNeighbours(x, y);
5854 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5855 element == EL_INVISIBLE_WALL_ACTIVE ||
5856 element == EL_INVISIBLE_SAND_ACTIVE)
5858 if (game.light_time_left == 0)
5859 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5861 DrawLevelField(x, y);
5863 /* re-crumble neighbour fields, if needed */
5864 if (element == EL_INVISIBLE_SAND)
5865 DrawLevelFieldCrumbledSandNeighbours(x, y);
5870 static void RedrawAllInvisibleElementsForLenses()
5874 SCAN_PLAYFIELD(x, y)
5876 int element = Feld[x][y];
5878 if (element == EL_EMC_DRIPPER &&
5879 game.lenses_time_left > 0)
5881 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5882 DrawLevelField(x, y);
5884 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5885 game.lenses_time_left == 0)
5887 Feld[x][y] = EL_EMC_DRIPPER;
5888 DrawLevelField(x, y);
5890 else if (element == EL_INVISIBLE_STEELWALL ||
5891 element == EL_INVISIBLE_WALL ||
5892 element == EL_INVISIBLE_SAND)
5894 if (game.lenses_time_left > 0)
5895 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5897 DrawLevelField(x, y);
5899 /* uncrumble neighbour fields, if needed */
5900 if (element == EL_INVISIBLE_SAND)
5901 DrawLevelFieldCrumbledSandNeighbours(x, y);
5903 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5904 element == EL_INVISIBLE_WALL_ACTIVE ||
5905 element == EL_INVISIBLE_SAND_ACTIVE)
5907 if (game.lenses_time_left == 0)
5908 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5910 DrawLevelField(x, y);
5912 /* re-crumble neighbour fields, if needed */
5913 if (element == EL_INVISIBLE_SAND)
5914 DrawLevelFieldCrumbledSandNeighbours(x, y);
5919 static void RedrawAllInvisibleElementsForMagnifier()
5923 SCAN_PLAYFIELD(x, y)
5925 int element = Feld[x][y];
5927 if (element == EL_EMC_FAKE_GRASS &&
5928 game.magnify_time_left > 0)
5930 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5931 DrawLevelField(x, y);
5933 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5934 game.magnify_time_left == 0)
5936 Feld[x][y] = EL_EMC_FAKE_GRASS;
5937 DrawLevelField(x, y);
5939 else if (IS_GATE_GRAY(element) &&
5940 game.magnify_time_left > 0)
5942 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5943 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5944 IS_EM_GATE_GRAY(element) ?
5945 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5946 IS_EMC_GATE_GRAY(element) ?
5947 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5949 DrawLevelField(x, y);
5951 else if (IS_GATE_GRAY_ACTIVE(element) &&
5952 game.magnify_time_left == 0)
5954 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5955 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5956 IS_EM_GATE_GRAY_ACTIVE(element) ?
5957 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5958 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5959 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5961 DrawLevelField(x, y);
5966 static void ToggleLightSwitch(int x, int y)
5968 int element = Feld[x][y];
5970 game.light_time_left =
5971 (element == EL_LIGHT_SWITCH ?
5972 level.time_light * FRAMES_PER_SECOND : 0);
5974 RedrawAllLightSwitchesAndInvisibleElements();
5977 static void ActivateTimegateSwitch(int x, int y)
5981 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5983 SCAN_PLAYFIELD(xx, yy)
5985 int element = Feld[xx][yy];
5987 if (element == EL_TIMEGATE_CLOSED ||
5988 element == EL_TIMEGATE_CLOSING)
5990 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5991 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5995 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5997 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5998 DrawLevelField(xx, yy);
6005 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6006 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6008 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6012 void Impact(int x, int y)
6014 boolean last_line = (y == lev_fieldy - 1);
6015 boolean object_hit = FALSE;
6016 boolean impact = (last_line || object_hit);
6017 int element = Feld[x][y];
6018 int smashed = EL_STEELWALL;
6020 if (!last_line) /* check if element below was hit */
6022 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6025 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6026 MovDir[x][y + 1] != MV_DOWN ||
6027 MovPos[x][y + 1] <= TILEY / 2));
6029 /* do not smash moving elements that left the smashed field in time */
6030 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6031 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6034 #if USE_QUICKSAND_IMPACT_BUGFIX
6035 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6037 RemoveMovingField(x, y + 1);
6038 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6039 Feld[x][y + 2] = EL_ROCK;
6040 DrawLevelField(x, y + 2);
6045 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6047 RemoveMovingField(x, y + 1);
6048 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6049 Feld[x][y + 2] = EL_ROCK;
6050 DrawLevelField(x, y + 2);
6057 smashed = MovingOrBlocked2Element(x, y + 1);
6059 impact = (last_line || object_hit);
6062 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6064 SplashAcid(x, y + 1);
6068 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6069 /* only reset graphic animation if graphic really changes after impact */
6071 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6073 ResetGfxAnimation(x, y);
6074 DrawLevelField(x, y);
6077 if (impact && CAN_EXPLODE_IMPACT(element))
6082 else if (impact && element == EL_PEARL &&
6083 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6085 ResetGfxAnimation(x, y);
6087 Feld[x][y] = EL_PEARL_BREAKING;
6088 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6091 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6093 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6098 if (impact && element == EL_AMOEBA_DROP)
6100 if (object_hit && IS_PLAYER(x, y + 1))
6101 KillPlayerUnlessEnemyProtected(x, y + 1);
6102 else if (object_hit && smashed == EL_PENGUIN)
6106 Feld[x][y] = EL_AMOEBA_GROWING;
6107 Store[x][y] = EL_AMOEBA_WET;
6109 ResetRandomAnimationValue(x, y);
6114 if (object_hit) /* check which object was hit */
6116 if ((CAN_PASS_MAGIC_WALL(element) &&
6117 (smashed == EL_MAGIC_WALL ||
6118 smashed == EL_BD_MAGIC_WALL)) ||
6119 (CAN_PASS_DC_MAGIC_WALL(element) &&
6120 smashed == EL_DC_MAGIC_WALL))
6123 int activated_magic_wall =
6124 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6125 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6126 EL_DC_MAGIC_WALL_ACTIVE);
6128 /* activate magic wall / mill */
6129 SCAN_PLAYFIELD(xx, yy)
6131 if (Feld[xx][yy] == smashed)
6132 Feld[xx][yy] = activated_magic_wall;
6135 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6136 game.magic_wall_active = TRUE;
6138 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6139 SND_MAGIC_WALL_ACTIVATING :
6140 smashed == EL_BD_MAGIC_WALL ?
6141 SND_BD_MAGIC_WALL_ACTIVATING :
6142 SND_DC_MAGIC_WALL_ACTIVATING));
6145 if (IS_PLAYER(x, y + 1))
6147 if (CAN_SMASH_PLAYER(element))
6149 KillPlayerUnlessEnemyProtected(x, y + 1);
6153 else if (smashed == EL_PENGUIN)
6155 if (CAN_SMASH_PLAYER(element))
6161 else if (element == EL_BD_DIAMOND)
6163 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6169 else if (((element == EL_SP_INFOTRON ||
6170 element == EL_SP_ZONK) &&
6171 (smashed == EL_SP_SNIKSNAK ||
6172 smashed == EL_SP_ELECTRON ||
6173 smashed == EL_SP_DISK_ORANGE)) ||
6174 (element == EL_SP_INFOTRON &&
6175 smashed == EL_SP_DISK_YELLOW))
6180 else if (CAN_SMASH_EVERYTHING(element))
6182 if (IS_CLASSIC_ENEMY(smashed) ||
6183 CAN_EXPLODE_SMASHED(smashed))
6188 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6190 if (smashed == EL_LAMP ||
6191 smashed == EL_LAMP_ACTIVE)
6196 else if (smashed == EL_NUT)
6198 Feld[x][y + 1] = EL_NUT_BREAKING;
6199 PlayLevelSound(x, y, SND_NUT_BREAKING);
6200 RaiseScoreElement(EL_NUT);
6203 else if (smashed == EL_PEARL)
6205 ResetGfxAnimation(x, y);
6207 Feld[x][y + 1] = EL_PEARL_BREAKING;
6208 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6211 else if (smashed == EL_DIAMOND)
6213 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6214 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6217 else if (IS_BELT_SWITCH(smashed))
6219 ToggleBeltSwitch(x, y + 1);
6221 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6222 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6223 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6224 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6226 ToggleSwitchgateSwitch(x, y + 1);
6228 else if (smashed == EL_LIGHT_SWITCH ||
6229 smashed == EL_LIGHT_SWITCH_ACTIVE)
6231 ToggleLightSwitch(x, y + 1);
6236 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6239 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6241 CheckElementChangeBySide(x, y + 1, smashed, element,
6242 CE_SWITCHED, CH_SIDE_TOP);
6243 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6249 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6254 /* play sound of magic wall / mill */
6256 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6257 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6258 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6260 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6261 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6262 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6263 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6264 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6265 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6270 /* play sound of object that hits the ground */
6271 if (last_line || object_hit)
6272 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6275 inline static void TurnRoundExt(int x, int y)
6287 { 0, 0 }, { 0, 0 }, { 0, 0 },
6292 int left, right, back;
6296 { MV_DOWN, MV_UP, MV_RIGHT },
6297 { MV_UP, MV_DOWN, MV_LEFT },
6299 { MV_LEFT, MV_RIGHT, MV_DOWN },
6303 { MV_RIGHT, MV_LEFT, MV_UP }
6306 int element = Feld[x][y];
6307 int move_pattern = element_info[element].move_pattern;
6309 int old_move_dir = MovDir[x][y];
6310 int left_dir = turn[old_move_dir].left;
6311 int right_dir = turn[old_move_dir].right;
6312 int back_dir = turn[old_move_dir].back;
6314 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6315 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6316 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6317 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6319 int left_x = x + left_dx, left_y = y + left_dy;
6320 int right_x = x + right_dx, right_y = y + right_dy;
6321 int move_x = x + move_dx, move_y = y + move_dy;
6325 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6327 TestIfBadThingTouchesOtherBadThing(x, y);
6329 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6330 MovDir[x][y] = right_dir;
6331 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6332 MovDir[x][y] = left_dir;
6334 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6336 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6339 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6341 TestIfBadThingTouchesOtherBadThing(x, y);
6343 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6344 MovDir[x][y] = left_dir;
6345 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6346 MovDir[x][y] = right_dir;
6348 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6350 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6353 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6355 TestIfBadThingTouchesOtherBadThing(x, y);
6357 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6358 MovDir[x][y] = left_dir;
6359 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6360 MovDir[x][y] = right_dir;
6362 if (MovDir[x][y] != old_move_dir)
6365 else if (element == EL_YAMYAM)
6367 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6368 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6370 if (can_turn_left && can_turn_right)
6371 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6372 else if (can_turn_left)
6373 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6374 else if (can_turn_right)
6375 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6377 MovDir[x][y] = back_dir;
6379 MovDelay[x][y] = 16 + 16 * RND(3);
6381 else if (element == EL_DARK_YAMYAM)
6383 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6385 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6388 if (can_turn_left && can_turn_right)
6389 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6390 else if (can_turn_left)
6391 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6392 else if (can_turn_right)
6393 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6395 MovDir[x][y] = back_dir;
6397 MovDelay[x][y] = 16 + 16 * RND(3);
6399 else if (element == EL_PACMAN)
6401 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6402 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6404 if (can_turn_left && can_turn_right)
6405 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6406 else if (can_turn_left)
6407 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6408 else if (can_turn_right)
6409 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6411 MovDir[x][y] = back_dir;
6413 MovDelay[x][y] = 6 + RND(40);
6415 else if (element == EL_PIG)
6417 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6418 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6419 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6420 boolean should_turn_left, should_turn_right, should_move_on;
6422 int rnd = RND(rnd_value);
6424 should_turn_left = (can_turn_left &&
6426 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6427 y + back_dy + left_dy)));
6428 should_turn_right = (can_turn_right &&
6430 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6431 y + back_dy + right_dy)));
6432 should_move_on = (can_move_on &&
6435 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6436 y + move_dy + left_dy) ||
6437 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6438 y + move_dy + right_dy)));
6440 if (should_turn_left || should_turn_right || should_move_on)
6442 if (should_turn_left && should_turn_right && should_move_on)
6443 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6444 rnd < 2 * rnd_value / 3 ? right_dir :
6446 else if (should_turn_left && should_turn_right)
6447 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6448 else if (should_turn_left && should_move_on)
6449 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6450 else if (should_turn_right && should_move_on)
6451 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6452 else if (should_turn_left)
6453 MovDir[x][y] = left_dir;
6454 else if (should_turn_right)
6455 MovDir[x][y] = right_dir;
6456 else if (should_move_on)
6457 MovDir[x][y] = old_move_dir;
6459 else if (can_move_on && rnd > rnd_value / 8)
6460 MovDir[x][y] = old_move_dir;
6461 else if (can_turn_left && can_turn_right)
6462 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6463 else if (can_turn_left && rnd > rnd_value / 8)
6464 MovDir[x][y] = left_dir;
6465 else if (can_turn_right && rnd > rnd_value/8)
6466 MovDir[x][y] = right_dir;
6468 MovDir[x][y] = back_dir;
6470 xx = x + move_xy[MovDir[x][y]].dx;
6471 yy = y + move_xy[MovDir[x][y]].dy;
6473 if (!IN_LEV_FIELD(xx, yy) ||
6474 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6475 MovDir[x][y] = old_move_dir;
6479 else if (element == EL_DRAGON)
6481 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6482 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6483 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6485 int rnd = RND(rnd_value);
6487 if (can_move_on && rnd > rnd_value / 8)
6488 MovDir[x][y] = old_move_dir;
6489 else if (can_turn_left && can_turn_right)
6490 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6491 else if (can_turn_left && rnd > rnd_value / 8)
6492 MovDir[x][y] = left_dir;
6493 else if (can_turn_right && rnd > rnd_value / 8)
6494 MovDir[x][y] = right_dir;
6496 MovDir[x][y] = back_dir;
6498 xx = x + move_xy[MovDir[x][y]].dx;
6499 yy = y + move_xy[MovDir[x][y]].dy;
6501 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6502 MovDir[x][y] = old_move_dir;
6506 else if (element == EL_MOLE)
6508 boolean can_move_on =
6509 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6510 IS_AMOEBOID(Feld[move_x][move_y]) ||
6511 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6514 boolean can_turn_left =
6515 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6516 IS_AMOEBOID(Feld[left_x][left_y])));
6518 boolean can_turn_right =
6519 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6520 IS_AMOEBOID(Feld[right_x][right_y])));
6522 if (can_turn_left && can_turn_right)
6523 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6524 else if (can_turn_left)
6525 MovDir[x][y] = left_dir;
6527 MovDir[x][y] = right_dir;
6530 if (MovDir[x][y] != old_move_dir)
6533 else if (element == EL_BALLOON)
6535 MovDir[x][y] = game.wind_direction;
6538 else if (element == EL_SPRING)
6540 #if USE_NEW_SPRING_BUMPER
6541 if (MovDir[x][y] & MV_HORIZONTAL)
6543 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6544 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6546 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6547 ResetGfxAnimation(move_x, move_y);
6548 DrawLevelField(move_x, move_y);
6550 MovDir[x][y] = back_dir;
6552 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6553 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6554 MovDir[x][y] = MV_NONE;
6557 if (MovDir[x][y] & MV_HORIZONTAL &&
6558 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6559 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6560 MovDir[x][y] = MV_NONE;
6565 else if (element == EL_ROBOT ||
6566 element == EL_SATELLITE ||
6567 element == EL_PENGUIN ||
6568 element == EL_EMC_ANDROID)
6570 int attr_x = -1, attr_y = -1;
6581 for (i = 0; i < MAX_PLAYERS; i++)
6583 struct PlayerInfo *player = &stored_player[i];
6584 int jx = player->jx, jy = player->jy;
6586 if (!player->active)
6590 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6598 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6599 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6600 game.engine_version < VERSION_IDENT(3,1,0,0)))
6606 if (element == EL_PENGUIN)
6609 static int xy[4][2] =
6617 for (i = 0; i < NUM_DIRECTIONS; i++)
6619 int ex = x + xy[i][0];
6620 int ey = y + xy[i][1];
6622 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6623 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6624 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6625 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6634 MovDir[x][y] = MV_NONE;
6636 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6637 else if (attr_x > x)
6638 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6640 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6641 else if (attr_y > y)
6642 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6644 if (element == EL_ROBOT)
6648 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6649 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6650 Moving2Blocked(x, y, &newx, &newy);
6652 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6653 MovDelay[x][y] = 8 + 8 * !RND(3);
6655 MovDelay[x][y] = 16;
6657 else if (element == EL_PENGUIN)
6663 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6665 boolean first_horiz = RND(2);
6666 int new_move_dir = MovDir[x][y];
6669 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6670 Moving2Blocked(x, y, &newx, &newy);
6672 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6676 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6677 Moving2Blocked(x, y, &newx, &newy);
6679 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6682 MovDir[x][y] = old_move_dir;
6686 else if (element == EL_SATELLITE)
6692 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6694 boolean first_horiz = RND(2);
6695 int new_move_dir = MovDir[x][y];
6698 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6699 Moving2Blocked(x, y, &newx, &newy);
6701 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6705 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6706 Moving2Blocked(x, y, &newx, &newy);
6708 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6711 MovDir[x][y] = old_move_dir;
6715 else if (element == EL_EMC_ANDROID)
6717 static int check_pos[16] =
6719 -1, /* 0 => (invalid) */
6720 7, /* 1 => MV_LEFT */
6721 3, /* 2 => MV_RIGHT */
6722 -1, /* 3 => (invalid) */
6724 0, /* 5 => MV_LEFT | MV_UP */
6725 2, /* 6 => MV_RIGHT | MV_UP */
6726 -1, /* 7 => (invalid) */
6727 5, /* 8 => MV_DOWN */
6728 6, /* 9 => MV_LEFT | MV_DOWN */
6729 4, /* 10 => MV_RIGHT | MV_DOWN */
6730 -1, /* 11 => (invalid) */
6731 -1, /* 12 => (invalid) */
6732 -1, /* 13 => (invalid) */
6733 -1, /* 14 => (invalid) */
6734 -1, /* 15 => (invalid) */
6742 { -1, -1, MV_LEFT | MV_UP },
6744 { +1, -1, MV_RIGHT | MV_UP },
6745 { +1, 0, MV_RIGHT },
6746 { +1, +1, MV_RIGHT | MV_DOWN },
6748 { -1, +1, MV_LEFT | MV_DOWN },
6751 int start_pos, check_order;
6752 boolean can_clone = FALSE;
6755 /* check if there is any free field around current position */
6756 for (i = 0; i < 8; i++)
6758 int newx = x + check_xy[i].dx;
6759 int newy = y + check_xy[i].dy;
6761 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6769 if (can_clone) /* randomly find an element to clone */
6773 start_pos = check_pos[RND(8)];
6774 check_order = (RND(2) ? -1 : +1);
6776 for (i = 0; i < 8; i++)
6778 int pos_raw = start_pos + i * check_order;
6779 int pos = (pos_raw + 8) % 8;
6780 int newx = x + check_xy[pos].dx;
6781 int newy = y + check_xy[pos].dy;
6783 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6785 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6786 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6788 Store[x][y] = Feld[newx][newy];
6797 if (can_clone) /* randomly find a direction to move */
6801 start_pos = check_pos[RND(8)];
6802 check_order = (RND(2) ? -1 : +1);
6804 for (i = 0; i < 8; i++)
6806 int pos_raw = start_pos + i * check_order;
6807 int pos = (pos_raw + 8) % 8;
6808 int newx = x + check_xy[pos].dx;
6809 int newy = y + check_xy[pos].dy;
6810 int new_move_dir = check_xy[pos].dir;
6812 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6814 MovDir[x][y] = new_move_dir;
6815 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6824 if (can_clone) /* cloning and moving successful */
6827 /* cannot clone -- try to move towards player */
6829 start_pos = check_pos[MovDir[x][y] & 0x0f];
6830 check_order = (RND(2) ? -1 : +1);
6832 for (i = 0; i < 3; i++)
6834 /* first check start_pos, then previous/next or (next/previous) pos */
6835 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6836 int pos = (pos_raw + 8) % 8;
6837 int newx = x + check_xy[pos].dx;
6838 int newy = y + check_xy[pos].dy;
6839 int new_move_dir = check_xy[pos].dir;
6841 if (IS_PLAYER(newx, newy))
6844 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6846 MovDir[x][y] = new_move_dir;
6847 MovDelay[x][y] = level.android_move_time * 8 + 1;
6854 else if (move_pattern == MV_TURNING_LEFT ||
6855 move_pattern == MV_TURNING_RIGHT ||
6856 move_pattern == MV_TURNING_LEFT_RIGHT ||
6857 move_pattern == MV_TURNING_RIGHT_LEFT ||
6858 move_pattern == MV_TURNING_RANDOM ||
6859 move_pattern == MV_ALL_DIRECTIONS)
6861 boolean can_turn_left =
6862 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6863 boolean can_turn_right =
6864 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6866 if (element_info[element].move_stepsize == 0) /* "not moving" */
6869 if (move_pattern == MV_TURNING_LEFT)
6870 MovDir[x][y] = left_dir;
6871 else if (move_pattern == MV_TURNING_RIGHT)
6872 MovDir[x][y] = right_dir;
6873 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6874 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6875 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6876 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6877 else if (move_pattern == MV_TURNING_RANDOM)
6878 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6879 can_turn_right && !can_turn_left ? right_dir :
6880 RND(2) ? left_dir : right_dir);
6881 else if (can_turn_left && can_turn_right)
6882 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6883 else if (can_turn_left)
6884 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6885 else if (can_turn_right)
6886 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6888 MovDir[x][y] = back_dir;
6890 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6892 else if (move_pattern == MV_HORIZONTAL ||
6893 move_pattern == MV_VERTICAL)
6895 if (move_pattern & old_move_dir)
6896 MovDir[x][y] = back_dir;
6897 else if (move_pattern == MV_HORIZONTAL)
6898 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6899 else if (move_pattern == MV_VERTICAL)
6900 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6902 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6904 else if (move_pattern & MV_ANY_DIRECTION)
6906 MovDir[x][y] = move_pattern;
6907 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6909 else if (move_pattern & MV_WIND_DIRECTION)
6911 MovDir[x][y] = game.wind_direction;
6912 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6916 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6917 MovDir[x][y] = left_dir;
6918 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6919 MovDir[x][y] = right_dir;
6921 if (MovDir[x][y] != old_move_dir)
6922 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6924 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6926 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6927 MovDir[x][y] = right_dir;
6928 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6929 MovDir[x][y] = left_dir;
6931 if (MovDir[x][y] != old_move_dir)
6932 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6934 else if (move_pattern == MV_TOWARDS_PLAYER ||
6935 move_pattern == MV_AWAY_FROM_PLAYER)
6937 int attr_x = -1, attr_y = -1;
6939 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6950 for (i = 0; i < MAX_PLAYERS; i++)
6952 struct PlayerInfo *player = &stored_player[i];
6953 int jx = player->jx, jy = player->jy;
6955 if (!player->active)
6959 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6967 MovDir[x][y] = MV_NONE;
6969 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6970 else if (attr_x > x)
6971 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6973 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6974 else if (attr_y > y)
6975 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6977 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6979 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6981 boolean first_horiz = RND(2);
6982 int new_move_dir = MovDir[x][y];
6984 if (element_info[element].move_stepsize == 0) /* "not moving" */
6986 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6987 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6993 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6994 Moving2Blocked(x, y, &newx, &newy);
6996 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7000 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7001 Moving2Blocked(x, y, &newx, &newy);
7003 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7006 MovDir[x][y] = old_move_dir;
7009 else if (move_pattern == MV_WHEN_PUSHED ||
7010 move_pattern == MV_WHEN_DROPPED)
7012 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7013 MovDir[x][y] = MV_NONE;
7017 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7019 static int test_xy[7][2] =
7029 static int test_dir[7] =
7039 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7040 int move_preference = -1000000; /* start with very low preference */
7041 int new_move_dir = MV_NONE;
7042 int start_test = RND(4);
7045 for (i = 0; i < NUM_DIRECTIONS; i++)
7047 int move_dir = test_dir[start_test + i];
7048 int move_dir_preference;
7050 xx = x + test_xy[start_test + i][0];
7051 yy = y + test_xy[start_test + i][1];
7053 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7054 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7056 new_move_dir = move_dir;
7061 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7064 move_dir_preference = -1 * RunnerVisit[xx][yy];
7065 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7066 move_dir_preference = PlayerVisit[xx][yy];
7068 if (move_dir_preference > move_preference)
7070 /* prefer field that has not been visited for the longest time */
7071 move_preference = move_dir_preference;
7072 new_move_dir = move_dir;
7074 else if (move_dir_preference == move_preference &&
7075 move_dir == old_move_dir)
7077 /* prefer last direction when all directions are preferred equally */
7078 move_preference = move_dir_preference;
7079 new_move_dir = move_dir;
7083 MovDir[x][y] = new_move_dir;
7084 if (old_move_dir != new_move_dir)
7085 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7089 static void TurnRound(int x, int y)
7091 int direction = MovDir[x][y];
7095 GfxDir[x][y] = MovDir[x][y];
7097 if (direction != MovDir[x][y])
7101 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7103 ResetGfxFrame(x, y, FALSE);
7106 static boolean JustBeingPushed(int x, int y)
7110 for (i = 0; i < MAX_PLAYERS; i++)
7112 struct PlayerInfo *player = &stored_player[i];
7114 if (player->active && player->is_pushing && player->MovPos)
7116 int next_jx = player->jx + (player->jx - player->last_jx);
7117 int next_jy = player->jy + (player->jy - player->last_jy);
7119 if (x == next_jx && y == next_jy)
7127 void StartMoving(int x, int y)
7129 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7130 int element = Feld[x][y];
7135 if (MovDelay[x][y] == 0)
7136 GfxAction[x][y] = ACTION_DEFAULT;
7138 if (CAN_FALL(element) && y < lev_fieldy - 1)
7140 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7141 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7142 if (JustBeingPushed(x, y))
7145 if (element == EL_QUICKSAND_FULL)
7147 if (IS_FREE(x, y + 1))
7149 InitMovingField(x, y, MV_DOWN);
7150 started_moving = TRUE;
7152 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7153 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7154 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7155 Store[x][y] = EL_ROCK;
7157 Store[x][y] = EL_ROCK;
7160 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7162 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7164 if (!MovDelay[x][y])
7165 MovDelay[x][y] = TILEY + 1;
7174 Feld[x][y] = EL_QUICKSAND_EMPTY;
7175 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7176 Store[x][y + 1] = Store[x][y];
7179 PlayLevelSoundAction(x, y, ACTION_FILLING);
7182 else if (element == EL_QUICKSAND_FAST_FULL)
7184 if (IS_FREE(x, y + 1))
7186 InitMovingField(x, y, MV_DOWN);
7187 started_moving = TRUE;
7189 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7190 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7191 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7192 Store[x][y] = EL_ROCK;
7194 Store[x][y] = EL_ROCK;
7197 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7199 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7201 if (!MovDelay[x][y])
7202 MovDelay[x][y] = TILEY + 1;
7211 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7212 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7213 Store[x][y + 1] = Store[x][y];
7216 PlayLevelSoundAction(x, y, ACTION_FILLING);
7219 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7220 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7222 InitMovingField(x, y, MV_DOWN);
7223 started_moving = TRUE;
7225 Feld[x][y] = EL_QUICKSAND_FILLING;
7226 Store[x][y] = element;
7228 PlayLevelSoundAction(x, y, ACTION_FILLING);
7230 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7231 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7233 InitMovingField(x, y, MV_DOWN);
7234 started_moving = TRUE;
7236 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7237 Store[x][y] = element;
7239 PlayLevelSoundAction(x, y, ACTION_FILLING);
7241 else if (element == EL_MAGIC_WALL_FULL)
7243 if (IS_FREE(x, y + 1))
7245 InitMovingField(x, y, MV_DOWN);
7246 started_moving = TRUE;
7248 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7249 Store[x][y] = EL_CHANGED(Store[x][y]);
7251 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7253 if (!MovDelay[x][y])
7254 MovDelay[x][y] = TILEY/4 + 1;
7263 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7264 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7265 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7269 else if (element == EL_BD_MAGIC_WALL_FULL)
7271 if (IS_FREE(x, y + 1))
7273 InitMovingField(x, y, MV_DOWN);
7274 started_moving = TRUE;
7276 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7277 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7279 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7281 if (!MovDelay[x][y])
7282 MovDelay[x][y] = TILEY/4 + 1;
7291 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7292 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7293 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7297 else if (element == EL_DC_MAGIC_WALL_FULL)
7299 if (IS_FREE(x, y + 1))
7301 InitMovingField(x, y, MV_DOWN);
7302 started_moving = TRUE;
7304 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7305 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7307 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7309 if (!MovDelay[x][y])
7310 MovDelay[x][y] = TILEY/4 + 1;
7319 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7320 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7321 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7325 else if ((CAN_PASS_MAGIC_WALL(element) &&
7326 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7327 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7328 (CAN_PASS_DC_MAGIC_WALL(element) &&
7329 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7332 InitMovingField(x, y, MV_DOWN);
7333 started_moving = TRUE;
7336 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7337 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7338 EL_DC_MAGIC_WALL_FILLING);
7339 Store[x][y] = element;
7341 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7343 SplashAcid(x, y + 1);
7345 InitMovingField(x, y, MV_DOWN);
7346 started_moving = TRUE;
7348 Store[x][y] = EL_ACID;
7351 #if USE_FIX_IMPACT_COLLISION
7352 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7353 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7355 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7356 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7358 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7359 CAN_FALL(element) && WasJustFalling[x][y] &&
7360 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7362 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7363 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7364 (Feld[x][y + 1] == EL_BLOCKED)))
7366 /* this is needed for a special case not covered by calling "Impact()"
7367 from "ContinueMoving()": if an element moves to a tile directly below
7368 another element which was just falling on that tile (which was empty
7369 in the previous frame), the falling element above would just stop
7370 instead of smashing the element below (in previous version, the above
7371 element was just checked for "moving" instead of "falling", resulting
7372 in incorrect smashes caused by horizontal movement of the above
7373 element; also, the case of the player being the element to smash was
7374 simply not covered here... :-/ ) */
7376 CheckCollision[x][y] = 0;
7377 CheckImpact[x][y] = 0;
7381 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7383 if (MovDir[x][y] == MV_NONE)
7385 InitMovingField(x, y, MV_DOWN);
7386 started_moving = TRUE;
7389 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7391 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7392 MovDir[x][y] = MV_DOWN;
7394 InitMovingField(x, y, MV_DOWN);
7395 started_moving = TRUE;
7397 else if (element == EL_AMOEBA_DROP)
7399 Feld[x][y] = EL_AMOEBA_GROWING;
7400 Store[x][y] = EL_AMOEBA_WET;
7402 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7403 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7404 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7405 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7407 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7408 (IS_FREE(x - 1, y + 1) ||
7409 Feld[x - 1][y + 1] == EL_ACID));
7410 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7411 (IS_FREE(x + 1, y + 1) ||
7412 Feld[x + 1][y + 1] == EL_ACID));
7413 boolean can_fall_any = (can_fall_left || can_fall_right);
7414 boolean can_fall_both = (can_fall_left && can_fall_right);
7415 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7417 #if USE_NEW_ALL_SLIPPERY
7418 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7420 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7421 can_fall_right = FALSE;
7422 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7423 can_fall_left = FALSE;
7424 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7425 can_fall_right = FALSE;
7426 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7427 can_fall_left = FALSE;
7429 can_fall_any = (can_fall_left || can_fall_right);
7430 can_fall_both = FALSE;
7433 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7435 if (slippery_type == SLIPPERY_ONLY_LEFT)
7436 can_fall_right = FALSE;
7437 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7438 can_fall_left = FALSE;
7439 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7440 can_fall_right = FALSE;
7441 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7442 can_fall_left = FALSE;
7444 can_fall_any = (can_fall_left || can_fall_right);
7445 can_fall_both = (can_fall_left && can_fall_right);
7449 #if USE_NEW_ALL_SLIPPERY
7451 #if USE_NEW_SP_SLIPPERY
7452 /* !!! better use the same properties as for custom elements here !!! */
7453 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7454 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7456 can_fall_right = FALSE; /* slip down on left side */
7457 can_fall_both = FALSE;
7462 #if USE_NEW_ALL_SLIPPERY
7465 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7466 can_fall_right = FALSE; /* slip down on left side */
7468 can_fall_left = !(can_fall_right = RND(2));
7470 can_fall_both = FALSE;
7475 if (game.emulation == EMU_BOULDERDASH ||
7476 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7477 can_fall_right = FALSE; /* slip down on left side */
7479 can_fall_left = !(can_fall_right = RND(2));
7481 can_fall_both = FALSE;
7487 /* if not determined otherwise, prefer left side for slipping down */
7488 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7489 started_moving = TRUE;
7493 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7495 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7498 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7499 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7500 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7501 int belt_dir = game.belt_dir[belt_nr];
7503 if ((belt_dir == MV_LEFT && left_is_free) ||
7504 (belt_dir == MV_RIGHT && right_is_free))
7506 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7508 InitMovingField(x, y, belt_dir);
7509 started_moving = TRUE;
7511 Pushed[x][y] = TRUE;
7512 Pushed[nextx][y] = TRUE;
7514 GfxAction[x][y] = ACTION_DEFAULT;
7518 MovDir[x][y] = 0; /* if element was moving, stop it */
7523 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7525 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7527 if (CAN_MOVE(element) && !started_moving)
7530 int move_pattern = element_info[element].move_pattern;
7535 if (MovDir[x][y] == MV_NONE)
7537 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7538 x, y, element, element_info[element].token_name);
7539 printf("StartMoving(): This should never happen!\n");
7544 Moving2Blocked(x, y, &newx, &newy);
7546 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7549 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7550 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7552 WasJustMoving[x][y] = 0;
7553 CheckCollision[x][y] = 0;
7555 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7557 if (Feld[x][y] != element) /* element has changed */
7561 if (!MovDelay[x][y]) /* start new movement phase */
7563 /* all objects that can change their move direction after each step
7564 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7566 if (element != EL_YAMYAM &&
7567 element != EL_DARK_YAMYAM &&
7568 element != EL_PACMAN &&
7569 !(move_pattern & MV_ANY_DIRECTION) &&
7570 move_pattern != MV_TURNING_LEFT &&
7571 move_pattern != MV_TURNING_RIGHT &&
7572 move_pattern != MV_TURNING_LEFT_RIGHT &&
7573 move_pattern != MV_TURNING_RIGHT_LEFT &&
7574 move_pattern != MV_TURNING_RANDOM)
7578 if (MovDelay[x][y] && (element == EL_BUG ||
7579 element == EL_SPACESHIP ||
7580 element == EL_SP_SNIKSNAK ||
7581 element == EL_SP_ELECTRON ||
7582 element == EL_MOLE))
7583 DrawLevelField(x, y);
7587 if (MovDelay[x][y]) /* wait some time before next movement */
7591 if (element == EL_ROBOT ||
7592 element == EL_YAMYAM ||
7593 element == EL_DARK_YAMYAM)
7595 DrawLevelElementAnimationIfNeeded(x, y, element);
7596 PlayLevelSoundAction(x, y, ACTION_WAITING);
7598 else if (element == EL_SP_ELECTRON)
7599 DrawLevelElementAnimationIfNeeded(x, y, element);
7600 else if (element == EL_DRAGON)
7603 int dir = MovDir[x][y];
7604 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7605 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7606 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7607 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7608 dir == MV_UP ? IMG_FLAMES_1_UP :
7609 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7610 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7612 GfxAction[x][y] = ACTION_ATTACKING;
7614 if (IS_PLAYER(x, y))
7615 DrawPlayerField(x, y);
7617 DrawLevelField(x, y);
7619 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7621 for (i = 1; i <= 3; i++)
7623 int xx = x + i * dx;
7624 int yy = y + i * dy;
7625 int sx = SCREENX(xx);
7626 int sy = SCREENY(yy);
7627 int flame_graphic = graphic + (i - 1);
7629 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7634 int flamed = MovingOrBlocked2Element(xx, yy);
7638 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7640 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7641 RemoveMovingField(xx, yy);
7643 RemoveField(xx, yy);
7645 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7648 RemoveMovingField(xx, yy);
7651 ChangeDelay[xx][yy] = 0;
7653 Feld[xx][yy] = EL_FLAMES;
7655 if (IN_SCR_FIELD(sx, sy))
7657 DrawLevelFieldCrumbledSand(xx, yy);
7658 DrawGraphic(sx, sy, flame_graphic, frame);
7663 if (Feld[xx][yy] == EL_FLAMES)
7664 Feld[xx][yy] = EL_EMPTY;
7665 DrawLevelField(xx, yy);
7670 if (MovDelay[x][y]) /* element still has to wait some time */
7672 PlayLevelSoundAction(x, y, ACTION_WAITING);
7678 /* now make next step */
7680 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7682 if (DONT_COLLIDE_WITH(element) &&
7683 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7684 !PLAYER_ENEMY_PROTECTED(newx, newy))
7686 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7691 else if (CAN_MOVE_INTO_ACID(element) &&
7692 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7693 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7694 (MovDir[x][y] == MV_DOWN ||
7695 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7697 SplashAcid(newx, newy);
7698 Store[x][y] = EL_ACID;
7700 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7702 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7703 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7704 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7705 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7708 DrawLevelField(x, y);
7710 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7711 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7712 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7714 local_player->friends_still_needed--;
7715 if (!local_player->friends_still_needed &&
7716 !local_player->GameOver && AllPlayersGone)
7717 PlayerWins(local_player);
7721 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7723 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7724 DrawLevelField(newx, newy);
7726 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7728 else if (!IS_FREE(newx, newy))
7730 GfxAction[x][y] = ACTION_WAITING;
7732 if (IS_PLAYER(x, y))
7733 DrawPlayerField(x, y);
7735 DrawLevelField(x, y);
7740 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7742 if (IS_FOOD_PIG(Feld[newx][newy]))
7744 if (IS_MOVING(newx, newy))
7745 RemoveMovingField(newx, newy);
7748 Feld[newx][newy] = EL_EMPTY;
7749 DrawLevelField(newx, newy);
7752 PlayLevelSound(x, y, SND_PIG_DIGGING);
7754 else if (!IS_FREE(newx, newy))
7756 if (IS_PLAYER(x, y))
7757 DrawPlayerField(x, y);
7759 DrawLevelField(x, y);
7764 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7766 if (Store[x][y] != EL_EMPTY)
7768 boolean can_clone = FALSE;
7771 /* check if element to clone is still there */
7772 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7774 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7782 /* cannot clone or target field not free anymore -- do not clone */
7783 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7784 Store[x][y] = EL_EMPTY;
7787 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7789 if (IS_MV_DIAGONAL(MovDir[x][y]))
7791 int diagonal_move_dir = MovDir[x][y];
7792 int stored = Store[x][y];
7793 int change_delay = 8;
7796 /* android is moving diagonally */
7798 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7800 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7801 GfxElement[x][y] = EL_EMC_ANDROID;
7802 GfxAction[x][y] = ACTION_SHRINKING;
7803 GfxDir[x][y] = diagonal_move_dir;
7804 ChangeDelay[x][y] = change_delay;
7806 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7809 DrawLevelGraphicAnimation(x, y, graphic);
7810 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7812 if (Feld[newx][newy] == EL_ACID)
7814 SplashAcid(newx, newy);
7819 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7821 Store[newx][newy] = EL_EMC_ANDROID;
7822 GfxElement[newx][newy] = EL_EMC_ANDROID;
7823 GfxAction[newx][newy] = ACTION_GROWING;
7824 GfxDir[newx][newy] = diagonal_move_dir;
7825 ChangeDelay[newx][newy] = change_delay;
7827 graphic = el_act_dir2img(GfxElement[newx][newy],
7828 GfxAction[newx][newy], GfxDir[newx][newy]);
7830 DrawLevelGraphicAnimation(newx, newy, graphic);
7831 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7837 Feld[newx][newy] = EL_EMPTY;
7838 DrawLevelField(newx, newy);
7840 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7843 else if (!IS_FREE(newx, newy))
7846 if (IS_PLAYER(x, y))
7847 DrawPlayerField(x, y);
7849 DrawLevelField(x, y);
7855 else if (IS_CUSTOM_ELEMENT(element) &&
7856 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7858 int new_element = Feld[newx][newy];
7860 if (!IS_FREE(newx, newy))
7862 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7863 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7866 /* no element can dig solid indestructible elements */
7867 if (IS_INDESTRUCTIBLE(new_element) &&
7868 !IS_DIGGABLE(new_element) &&
7869 !IS_COLLECTIBLE(new_element))
7872 if (AmoebaNr[newx][newy] &&
7873 (new_element == EL_AMOEBA_FULL ||
7874 new_element == EL_BD_AMOEBA ||
7875 new_element == EL_AMOEBA_GROWING))
7877 AmoebaCnt[AmoebaNr[newx][newy]]--;
7878 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7881 if (IS_MOVING(newx, newy))
7882 RemoveMovingField(newx, newy);
7885 RemoveField(newx, newy);
7886 DrawLevelField(newx, newy);
7889 /* if digged element was about to explode, prevent the explosion */
7890 ExplodeField[newx][newy] = EX_TYPE_NONE;
7892 PlayLevelSoundAction(x, y, action);
7895 Store[newx][newy] = EL_EMPTY;
7897 /* this makes it possible to leave the removed element again */
7898 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7899 Store[newx][newy] = new_element;
7901 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7903 int move_leave_element = element_info[element].move_leave_element;
7905 /* this makes it possible to leave the removed element again */
7906 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7907 new_element : move_leave_element);
7911 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7913 RunnerVisit[x][y] = FrameCounter;
7914 PlayerVisit[x][y] /= 8; /* expire player visit path */
7917 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7919 if (!IS_FREE(newx, newy))
7921 if (IS_PLAYER(x, y))
7922 DrawPlayerField(x, y);
7924 DrawLevelField(x, y);
7930 boolean wanna_flame = !RND(10);
7931 int dx = newx - x, dy = newy - y;
7932 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7933 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7934 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7935 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7936 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7937 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7940 IS_CLASSIC_ENEMY(element1) ||
7941 IS_CLASSIC_ENEMY(element2)) &&
7942 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7943 element1 != EL_FLAMES && element2 != EL_FLAMES)
7945 ResetGfxAnimation(x, y);
7946 GfxAction[x][y] = ACTION_ATTACKING;
7948 if (IS_PLAYER(x, y))
7949 DrawPlayerField(x, y);
7951 DrawLevelField(x, y);
7953 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7955 MovDelay[x][y] = 50;
7959 RemoveField(newx, newy);
7961 Feld[newx][newy] = EL_FLAMES;
7962 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7965 RemoveField(newx1, newy1);
7967 Feld[newx1][newy1] = EL_FLAMES;
7969 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7972 RemoveField(newx2, newy2);
7974 Feld[newx2][newy2] = EL_FLAMES;
7981 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7982 Feld[newx][newy] == EL_DIAMOND)
7984 if (IS_MOVING(newx, newy))
7985 RemoveMovingField(newx, newy);
7988 Feld[newx][newy] = EL_EMPTY;
7989 DrawLevelField(newx, newy);
7992 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7994 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7995 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7997 if (AmoebaNr[newx][newy])
7999 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8000 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8001 Feld[newx][newy] == EL_BD_AMOEBA)
8002 AmoebaCnt[AmoebaNr[newx][newy]]--;
8007 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8009 RemoveMovingField(newx, newy);
8012 if (IS_MOVING(newx, newy))
8014 RemoveMovingField(newx, newy);
8019 Feld[newx][newy] = EL_EMPTY;
8020 DrawLevelField(newx, newy);
8023 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8025 else if ((element == EL_PACMAN || element == EL_MOLE)
8026 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8028 if (AmoebaNr[newx][newy])
8030 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8031 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8032 Feld[newx][newy] == EL_BD_AMOEBA)
8033 AmoebaCnt[AmoebaNr[newx][newy]]--;
8036 if (element == EL_MOLE)
8038 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8039 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8041 ResetGfxAnimation(x, y);
8042 GfxAction[x][y] = ACTION_DIGGING;
8043 DrawLevelField(x, y);
8045 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8047 return; /* wait for shrinking amoeba */
8049 else /* element == EL_PACMAN */
8051 Feld[newx][newy] = EL_EMPTY;
8052 DrawLevelField(newx, newy);
8053 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8056 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8057 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8058 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8060 /* wait for shrinking amoeba to completely disappear */
8063 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8065 /* object was running against a wall */
8070 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8071 if (move_pattern & MV_ANY_DIRECTION &&
8072 move_pattern == MovDir[x][y])
8074 int blocking_element =
8075 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8077 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8080 element = Feld[x][y]; /* element might have changed */
8084 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8085 DrawLevelElementAnimation(x, y, element);
8087 if (DONT_TOUCH(element))
8088 TestIfBadThingTouchesPlayer(x, y);
8093 InitMovingField(x, y, MovDir[x][y]);
8095 PlayLevelSoundAction(x, y, ACTION_MOVING);
8099 ContinueMoving(x, y);
8102 void ContinueMoving(int x, int y)
8104 int element = Feld[x][y];
8105 struct ElementInfo *ei = &element_info[element];
8106 int direction = MovDir[x][y];
8107 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8108 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8109 int newx = x + dx, newy = y + dy;
8110 int stored = Store[x][y];
8111 int stored_new = Store[newx][newy];
8112 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8113 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8114 boolean last_line = (newy == lev_fieldy - 1);
8116 MovPos[x][y] += getElementMoveStepsize(x, y);
8118 if (pushed_by_player) /* special case: moving object pushed by player */
8119 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8121 if (ABS(MovPos[x][y]) < TILEX)
8124 int ee = Feld[x][y];
8125 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8126 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8128 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8129 x, y, ABS(MovPos[x][y]),
8131 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8134 DrawLevelField(x, y);
8136 return; /* element is still moving */
8139 /* element reached destination field */
8141 Feld[x][y] = EL_EMPTY;
8142 Feld[newx][newy] = element;
8143 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8145 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8147 element = Feld[newx][newy] = EL_ACID;
8149 else if (element == EL_MOLE)
8151 Feld[x][y] = EL_SAND;
8153 DrawLevelFieldCrumbledSandNeighbours(x, y);
8155 else if (element == EL_QUICKSAND_FILLING)
8157 element = Feld[newx][newy] = get_next_element(element);
8158 Store[newx][newy] = Store[x][y];
8160 else if (element == EL_QUICKSAND_EMPTYING)
8162 Feld[x][y] = get_next_element(element);
8163 element = Feld[newx][newy] = Store[x][y];
8165 else if (element == EL_QUICKSAND_FAST_FILLING)
8167 element = Feld[newx][newy] = get_next_element(element);
8168 Store[newx][newy] = Store[x][y];
8170 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8172 Feld[x][y] = get_next_element(element);
8173 element = Feld[newx][newy] = Store[x][y];
8175 else if (element == EL_MAGIC_WALL_FILLING)
8177 element = Feld[newx][newy] = get_next_element(element);
8178 if (!game.magic_wall_active)
8179 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8180 Store[newx][newy] = Store[x][y];
8182 else if (element == EL_MAGIC_WALL_EMPTYING)
8184 Feld[x][y] = get_next_element(element);
8185 if (!game.magic_wall_active)
8186 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8187 element = Feld[newx][newy] = Store[x][y];
8189 #if USE_NEW_CUSTOM_VALUE
8190 InitField(newx, newy, FALSE);
8193 else if (element == EL_BD_MAGIC_WALL_FILLING)
8195 element = Feld[newx][newy] = get_next_element(element);
8196 if (!game.magic_wall_active)
8197 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8198 Store[newx][newy] = Store[x][y];
8200 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8202 Feld[x][y] = get_next_element(element);
8203 if (!game.magic_wall_active)
8204 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8205 element = Feld[newx][newy] = Store[x][y];
8207 #if USE_NEW_CUSTOM_VALUE
8208 InitField(newx, newy, FALSE);
8211 else if (element == EL_DC_MAGIC_WALL_FILLING)
8213 element = Feld[newx][newy] = get_next_element(element);
8214 if (!game.magic_wall_active)
8215 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8216 Store[newx][newy] = Store[x][y];
8218 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8220 Feld[x][y] = get_next_element(element);
8221 if (!game.magic_wall_active)
8222 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8223 element = Feld[newx][newy] = Store[x][y];
8225 #if USE_NEW_CUSTOM_VALUE
8226 InitField(newx, newy, FALSE);
8229 else if (element == EL_AMOEBA_DROPPING)
8231 Feld[x][y] = get_next_element(element);
8232 element = Feld[newx][newy] = Store[x][y];
8234 else if (element == EL_SOKOBAN_OBJECT)
8237 Feld[x][y] = Back[x][y];
8239 if (Back[newx][newy])
8240 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8242 Back[x][y] = Back[newx][newy] = 0;
8245 Store[x][y] = EL_EMPTY;
8250 MovDelay[newx][newy] = 0;
8252 if (CAN_CHANGE_OR_HAS_ACTION(element))
8254 /* copy element change control values to new field */
8255 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8256 ChangePage[newx][newy] = ChangePage[x][y];
8257 ChangeCount[newx][newy] = ChangeCount[x][y];
8258 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8261 #if USE_NEW_CUSTOM_VALUE
8262 CustomValue[newx][newy] = CustomValue[x][y];
8265 ChangeDelay[x][y] = 0;
8266 ChangePage[x][y] = -1;
8267 ChangeCount[x][y] = 0;
8268 ChangeEvent[x][y] = -1;
8270 #if USE_NEW_CUSTOM_VALUE
8271 CustomValue[x][y] = 0;
8274 /* copy animation control values to new field */
8275 GfxFrame[newx][newy] = GfxFrame[x][y];
8276 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8277 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8278 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8280 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8282 /* some elements can leave other elements behind after moving */
8284 if (ei->move_leave_element != EL_EMPTY &&
8285 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8286 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8288 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8289 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8290 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8293 int move_leave_element = ei->move_leave_element;
8297 /* this makes it possible to leave the removed element again */
8298 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8299 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8301 /* this makes it possible to leave the removed element again */
8302 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8303 move_leave_element = stored;
8306 /* this makes it possible to leave the removed element again */
8307 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8308 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8309 move_leave_element = stored;
8312 Feld[x][y] = move_leave_element;
8314 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8315 MovDir[x][y] = direction;
8317 InitField(x, y, FALSE);
8319 if (GFX_CRUMBLED(Feld[x][y]))
8320 DrawLevelFieldCrumbledSandNeighbours(x, y);
8322 if (ELEM_IS_PLAYER(move_leave_element))
8323 RelocatePlayer(x, y, move_leave_element);
8326 /* do this after checking for left-behind element */
8327 ResetGfxAnimation(x, y); /* reset animation values for old field */
8329 if (!CAN_MOVE(element) ||
8330 (CAN_FALL(element) && direction == MV_DOWN &&
8331 (element == EL_SPRING ||
8332 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8333 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8334 GfxDir[x][y] = MovDir[newx][newy] = 0;
8336 DrawLevelField(x, y);
8337 DrawLevelField(newx, newy);
8339 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8341 /* prevent pushed element from moving on in pushed direction */
8342 if (pushed_by_player && CAN_MOVE(element) &&
8343 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8344 !(element_info[element].move_pattern & direction))
8345 TurnRound(newx, newy);
8347 /* prevent elements on conveyor belt from moving on in last direction */
8348 if (pushed_by_conveyor && CAN_FALL(element) &&
8349 direction & MV_HORIZONTAL)
8350 MovDir[newx][newy] = 0;
8352 if (!pushed_by_player)
8354 int nextx = newx + dx, nexty = newy + dy;
8355 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8357 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8359 if (CAN_FALL(element) && direction == MV_DOWN)
8360 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8362 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8363 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8365 #if USE_FIX_IMPACT_COLLISION
8366 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8367 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8371 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8373 TestIfBadThingTouchesPlayer(newx, newy);
8374 TestIfBadThingTouchesFriend(newx, newy);
8376 if (!IS_CUSTOM_ELEMENT(element))
8377 TestIfBadThingTouchesOtherBadThing(newx, newy);
8379 else if (element == EL_PENGUIN)
8380 TestIfFriendTouchesBadThing(newx, newy);
8382 /* give the player one last chance (one more frame) to move away */
8383 if (CAN_FALL(element) && direction == MV_DOWN &&
8384 (last_line || (!IS_FREE(x, newy + 1) &&
8385 (!IS_PLAYER(x, newy + 1) ||
8386 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8389 if (pushed_by_player && !game.use_change_when_pushing_bug)
8391 int push_side = MV_DIR_OPPOSITE(direction);
8392 struct PlayerInfo *player = PLAYERINFO(x, y);
8394 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8395 player->index_bit, push_side);
8396 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8397 player->index_bit, push_side);
8400 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8401 MovDelay[newx][newy] = 1;
8403 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8405 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8408 if (ChangePage[newx][newy] != -1) /* delayed change */
8410 int page = ChangePage[newx][newy];
8411 struct ElementChangeInfo *change = &ei->change_page[page];
8413 ChangePage[newx][newy] = -1;
8415 if (change->can_change)
8417 if (ChangeElement(newx, newy, element, page))
8419 if (change->post_change_function)
8420 change->post_change_function(newx, newy);
8424 if (change->has_action)
8425 ExecuteCustomElementAction(newx, newy, element, page);
8429 TestIfElementHitsCustomElement(newx, newy, direction);
8430 TestIfPlayerTouchesCustomElement(newx, newy);
8431 TestIfElementTouchesCustomElement(newx, newy);
8433 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8434 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8435 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8436 MV_DIR_OPPOSITE(direction));
8439 int AmoebeNachbarNr(int ax, int ay)
8442 int element = Feld[ax][ay];
8444 static int xy[4][2] =
8452 for (i = 0; i < NUM_DIRECTIONS; i++)
8454 int x = ax + xy[i][0];
8455 int y = ay + xy[i][1];
8457 if (!IN_LEV_FIELD(x, y))
8460 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8461 group_nr = AmoebaNr[x][y];
8467 void AmoebenVereinigen(int ax, int ay)
8469 int i, x, y, xx, yy;
8470 int new_group_nr = AmoebaNr[ax][ay];
8471 static int xy[4][2] =
8479 if (new_group_nr == 0)
8482 for (i = 0; i < NUM_DIRECTIONS; i++)
8487 if (!IN_LEV_FIELD(x, y))
8490 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8491 Feld[x][y] == EL_BD_AMOEBA ||
8492 Feld[x][y] == EL_AMOEBA_DEAD) &&
8493 AmoebaNr[x][y] != new_group_nr)
8495 int old_group_nr = AmoebaNr[x][y];
8497 if (old_group_nr == 0)
8500 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8501 AmoebaCnt[old_group_nr] = 0;
8502 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8503 AmoebaCnt2[old_group_nr] = 0;
8505 SCAN_PLAYFIELD(xx, yy)
8507 if (AmoebaNr[xx][yy] == old_group_nr)
8508 AmoebaNr[xx][yy] = new_group_nr;
8514 void AmoebeUmwandeln(int ax, int ay)
8518 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8520 int group_nr = AmoebaNr[ax][ay];
8525 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8526 printf("AmoebeUmwandeln(): This should never happen!\n");
8531 SCAN_PLAYFIELD(x, y)
8533 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8536 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8540 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8541 SND_AMOEBA_TURNING_TO_GEM :
8542 SND_AMOEBA_TURNING_TO_ROCK));
8547 static int xy[4][2] =
8555 for (i = 0; i < NUM_DIRECTIONS; i++)
8560 if (!IN_LEV_FIELD(x, y))
8563 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8565 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8566 SND_AMOEBA_TURNING_TO_GEM :
8567 SND_AMOEBA_TURNING_TO_ROCK));
8574 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8577 int group_nr = AmoebaNr[ax][ay];
8578 boolean done = FALSE;
8583 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8584 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8589 SCAN_PLAYFIELD(x, y)
8591 if (AmoebaNr[x][y] == group_nr &&
8592 (Feld[x][y] == EL_AMOEBA_DEAD ||
8593 Feld[x][y] == EL_BD_AMOEBA ||
8594 Feld[x][y] == EL_AMOEBA_GROWING))
8597 Feld[x][y] = new_element;
8598 InitField(x, y, FALSE);
8599 DrawLevelField(x, y);
8605 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8606 SND_BD_AMOEBA_TURNING_TO_ROCK :
8607 SND_BD_AMOEBA_TURNING_TO_GEM));
8610 void AmoebeWaechst(int x, int y)
8612 static unsigned long sound_delay = 0;
8613 static unsigned long sound_delay_value = 0;
8615 if (!MovDelay[x][y]) /* start new growing cycle */
8619 if (DelayReached(&sound_delay, sound_delay_value))
8621 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8622 sound_delay_value = 30;
8626 if (MovDelay[x][y]) /* wait some time before growing bigger */
8629 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8631 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8632 6 - MovDelay[x][y]);
8634 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8637 if (!MovDelay[x][y])
8639 Feld[x][y] = Store[x][y];
8641 DrawLevelField(x, y);
8646 void AmoebaDisappearing(int x, int y)
8648 static unsigned long sound_delay = 0;
8649 static unsigned long sound_delay_value = 0;
8651 if (!MovDelay[x][y]) /* start new shrinking cycle */
8655 if (DelayReached(&sound_delay, sound_delay_value))
8656 sound_delay_value = 30;
8659 if (MovDelay[x][y]) /* wait some time before shrinking */
8662 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8664 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8665 6 - MovDelay[x][y]);
8667 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8670 if (!MovDelay[x][y])
8672 Feld[x][y] = EL_EMPTY;
8673 DrawLevelField(x, y);
8675 /* don't let mole enter this field in this cycle;
8676 (give priority to objects falling to this field from above) */
8682 void AmoebeAbleger(int ax, int ay)
8685 int element = Feld[ax][ay];
8686 int graphic = el2img(element);
8687 int newax = ax, neway = ay;
8688 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8689 static int xy[4][2] =
8697 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8699 Feld[ax][ay] = EL_AMOEBA_DEAD;
8700 DrawLevelField(ax, ay);
8704 if (IS_ANIMATED(graphic))
8705 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8707 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8708 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8710 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8713 if (MovDelay[ax][ay])
8717 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8720 int x = ax + xy[start][0];
8721 int y = ay + xy[start][1];
8723 if (!IN_LEV_FIELD(x, y))
8726 if (IS_FREE(x, y) ||
8727 CAN_GROW_INTO(Feld[x][y]) ||
8728 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8729 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8735 if (newax == ax && neway == ay)
8738 else /* normal or "filled" (BD style) amoeba */
8741 boolean waiting_for_player = FALSE;
8743 for (i = 0; i < NUM_DIRECTIONS; i++)
8745 int j = (start + i) % 4;
8746 int x = ax + xy[j][0];
8747 int y = ay + xy[j][1];
8749 if (!IN_LEV_FIELD(x, y))
8752 if (IS_FREE(x, y) ||
8753 CAN_GROW_INTO(Feld[x][y]) ||
8754 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8755 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8761 else if (IS_PLAYER(x, y))
8762 waiting_for_player = TRUE;
8765 if (newax == ax && neway == ay) /* amoeba cannot grow */
8767 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8769 Feld[ax][ay] = EL_AMOEBA_DEAD;
8770 DrawLevelField(ax, ay);
8771 AmoebaCnt[AmoebaNr[ax][ay]]--;
8773 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8775 if (element == EL_AMOEBA_FULL)
8776 AmoebeUmwandeln(ax, ay);
8777 else if (element == EL_BD_AMOEBA)
8778 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8783 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8785 /* amoeba gets larger by growing in some direction */
8787 int new_group_nr = AmoebaNr[ax][ay];
8790 if (new_group_nr == 0)
8792 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8793 printf("AmoebeAbleger(): This should never happen!\n");
8798 AmoebaNr[newax][neway] = new_group_nr;
8799 AmoebaCnt[new_group_nr]++;
8800 AmoebaCnt2[new_group_nr]++;
8802 /* if amoeba touches other amoeba(s) after growing, unify them */
8803 AmoebenVereinigen(newax, neway);
8805 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8807 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8813 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8814 (neway == lev_fieldy - 1 && newax != ax))
8816 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8817 Store[newax][neway] = element;
8819 else if (neway == ay || element == EL_EMC_DRIPPER)
8821 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8823 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8827 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8828 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8829 Store[ax][ay] = EL_AMOEBA_DROP;
8830 ContinueMoving(ax, ay);
8834 DrawLevelField(newax, neway);
8837 void Life(int ax, int ay)
8841 int element = Feld[ax][ay];
8842 int graphic = el2img(element);
8843 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8845 boolean changed = FALSE;
8847 if (IS_ANIMATED(graphic))
8848 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8853 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8854 MovDelay[ax][ay] = life_time;
8856 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8859 if (MovDelay[ax][ay])
8863 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8865 int xx = ax+x1, yy = ay+y1;
8868 if (!IN_LEV_FIELD(xx, yy))
8871 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8873 int x = xx+x2, y = yy+y2;
8875 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8878 if (((Feld[x][y] == element ||
8879 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8881 (IS_FREE(x, y) && Stop[x][y]))
8885 if (xx == ax && yy == ay) /* field in the middle */
8887 if (nachbarn < life_parameter[0] ||
8888 nachbarn > life_parameter[1])
8890 Feld[xx][yy] = EL_EMPTY;
8892 DrawLevelField(xx, yy);
8893 Stop[xx][yy] = TRUE;
8897 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8898 { /* free border field */
8899 if (nachbarn >= life_parameter[2] &&
8900 nachbarn <= life_parameter[3])
8902 Feld[xx][yy] = element;
8903 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8905 DrawLevelField(xx, yy);
8906 Stop[xx][yy] = TRUE;
8913 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8914 SND_GAME_OF_LIFE_GROWING);
8917 static void InitRobotWheel(int x, int y)
8919 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8922 static void RunRobotWheel(int x, int y)
8924 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8927 static void StopRobotWheel(int x, int y)
8929 if (ZX == x && ZY == y)
8933 game.robot_wheel_active = FALSE;
8937 static void InitTimegateWheel(int x, int y)
8939 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8942 static void RunTimegateWheel(int x, int y)
8944 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8947 static void InitMagicBallDelay(int x, int y)
8950 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8952 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8956 static void ActivateMagicBall(int bx, int by)
8960 if (level.ball_random)
8962 int pos_border = RND(8); /* select one of the eight border elements */
8963 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8964 int xx = pos_content % 3;
8965 int yy = pos_content / 3;
8970 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8971 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8975 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8977 int xx = x - bx + 1;
8978 int yy = y - by + 1;
8980 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8981 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8985 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8988 void CheckExit(int x, int y)
8990 if (local_player->gems_still_needed > 0 ||
8991 local_player->sokobanfields_still_needed > 0 ||
8992 local_player->lights_still_needed > 0)
8994 int element = Feld[x][y];
8995 int graphic = el2img(element);
8997 if (IS_ANIMATED(graphic))
8998 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9003 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9006 Feld[x][y] = EL_EXIT_OPENING;
9008 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9011 void CheckExitEM(int x, int y)
9013 if (local_player->gems_still_needed > 0 ||
9014 local_player->sokobanfields_still_needed > 0 ||
9015 local_player->lights_still_needed > 0)
9017 int element = Feld[x][y];
9018 int graphic = el2img(element);
9020 if (IS_ANIMATED(graphic))
9021 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9026 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9029 Feld[x][y] = EL_EM_EXIT_OPENING;
9031 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9034 void CheckExitSteel(int x, int y)
9036 if (local_player->gems_still_needed > 0 ||
9037 local_player->sokobanfields_still_needed > 0 ||
9038 local_player->lights_still_needed > 0)
9040 int element = Feld[x][y];
9041 int graphic = el2img(element);
9043 if (IS_ANIMATED(graphic))
9044 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9049 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9052 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9054 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9057 void CheckExitSteelEM(int x, int y)
9059 if (local_player->gems_still_needed > 0 ||
9060 local_player->sokobanfields_still_needed > 0 ||
9061 local_player->lights_still_needed > 0)
9063 int element = Feld[x][y];
9064 int graphic = el2img(element);
9066 if (IS_ANIMATED(graphic))
9067 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9072 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9075 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9077 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9080 void CheckExitSP(int x, int y)
9082 if (local_player->gems_still_needed > 0)
9084 int element = Feld[x][y];
9085 int graphic = el2img(element);
9087 if (IS_ANIMATED(graphic))
9088 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9093 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9096 Feld[x][y] = EL_SP_EXIT_OPENING;
9098 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9101 static void CloseAllOpenTimegates()
9105 SCAN_PLAYFIELD(x, y)
9107 int element = Feld[x][y];
9109 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9111 Feld[x][y] = EL_TIMEGATE_CLOSING;
9113 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9118 void DrawTwinkleOnField(int x, int y)
9120 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9123 if (Feld[x][y] == EL_BD_DIAMOND)
9126 if (MovDelay[x][y] == 0) /* next animation frame */
9127 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9129 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9133 if (setup.direct_draw && MovDelay[x][y])
9134 SetDrawtoField(DRAW_BUFFERED);
9136 DrawLevelElementAnimation(x, y, Feld[x][y]);
9138 if (MovDelay[x][y] != 0)
9140 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9141 10 - MovDelay[x][y]);
9143 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9145 if (setup.direct_draw)
9149 dest_x = FX + SCREENX(x) * TILEX;
9150 dest_y = FY + SCREENY(y) * TILEY;
9152 BlitBitmap(drawto_field, window,
9153 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9154 SetDrawtoField(DRAW_DIRECT);
9160 void MauerWaechst(int x, int y)
9164 if (!MovDelay[x][y]) /* next animation frame */
9165 MovDelay[x][y] = 3 * delay;
9167 if (MovDelay[x][y]) /* wait some time before next frame */
9171 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9173 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9174 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9176 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9179 if (!MovDelay[x][y])
9181 if (MovDir[x][y] == MV_LEFT)
9183 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9184 DrawLevelField(x - 1, y);
9186 else if (MovDir[x][y] == MV_RIGHT)
9188 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9189 DrawLevelField(x + 1, y);
9191 else if (MovDir[x][y] == MV_UP)
9193 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9194 DrawLevelField(x, y - 1);
9198 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9199 DrawLevelField(x, y + 1);
9202 Feld[x][y] = Store[x][y];
9204 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9205 DrawLevelField(x, y);
9210 void MauerAbleger(int ax, int ay)
9212 int element = Feld[ax][ay];
9213 int graphic = el2img(element);
9214 boolean oben_frei = FALSE, unten_frei = FALSE;
9215 boolean links_frei = FALSE, rechts_frei = FALSE;
9216 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9217 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9218 boolean new_wall = FALSE;
9220 if (IS_ANIMATED(graphic))
9221 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9223 if (!MovDelay[ax][ay]) /* start building new wall */
9224 MovDelay[ax][ay] = 6;
9226 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9229 if (MovDelay[ax][ay])
9233 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9235 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9237 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9239 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9242 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9243 element == EL_EXPANDABLE_WALL_ANY)
9247 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9248 Store[ax][ay-1] = element;
9249 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9250 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9251 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9252 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9257 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9258 Store[ax][ay+1] = element;
9259 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9260 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9261 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9262 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9267 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9268 element == EL_EXPANDABLE_WALL_ANY ||
9269 element == EL_EXPANDABLE_WALL ||
9270 element == EL_BD_EXPANDABLE_WALL)
9274 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9275 Store[ax-1][ay] = element;
9276 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9277 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9278 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9279 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9285 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9286 Store[ax+1][ay] = element;
9287 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9288 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9289 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9290 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9295 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9296 DrawLevelField(ax, ay);
9298 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9300 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9301 unten_massiv = TRUE;
9302 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9303 links_massiv = TRUE;
9304 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9305 rechts_massiv = TRUE;
9307 if (((oben_massiv && unten_massiv) ||
9308 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9309 element == EL_EXPANDABLE_WALL) &&
9310 ((links_massiv && rechts_massiv) ||
9311 element == EL_EXPANDABLE_WALL_VERTICAL))
9312 Feld[ax][ay] = EL_WALL;
9315 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9318 void MauerAblegerStahl(int ax, int ay)
9320 int element = Feld[ax][ay];
9321 int graphic = el2img(element);
9322 boolean oben_frei = FALSE, unten_frei = FALSE;
9323 boolean links_frei = FALSE, rechts_frei = FALSE;
9324 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9325 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9326 boolean new_wall = FALSE;
9328 if (IS_ANIMATED(graphic))
9329 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9331 if (!MovDelay[ax][ay]) /* start building new wall */
9332 MovDelay[ax][ay] = 6;
9334 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9337 if (MovDelay[ax][ay])
9341 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9343 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9345 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9347 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9350 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9351 element == EL_EXPANDABLE_STEELWALL_ANY)
9355 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9356 Store[ax][ay-1] = element;
9357 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9358 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9359 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9360 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9365 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9366 Store[ax][ay+1] = element;
9367 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9368 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9369 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9370 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9375 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9376 element == EL_EXPANDABLE_STEELWALL_ANY)
9380 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9381 Store[ax-1][ay] = element;
9382 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9383 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9384 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9385 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9391 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9392 Store[ax+1][ay] = element;
9393 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9394 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9395 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9396 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9401 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9403 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9404 unten_massiv = TRUE;
9405 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9406 links_massiv = TRUE;
9407 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9408 rechts_massiv = TRUE;
9410 if (((oben_massiv && unten_massiv) ||
9411 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9412 ((links_massiv && rechts_massiv) ||
9413 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9414 Feld[ax][ay] = EL_WALL;
9417 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9420 void CheckForDragon(int x, int y)
9423 boolean dragon_found = FALSE;
9424 static int xy[4][2] =
9432 for (i = 0; i < NUM_DIRECTIONS; i++)
9434 for (j = 0; j < 4; j++)
9436 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9438 if (IN_LEV_FIELD(xx, yy) &&
9439 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9441 if (Feld[xx][yy] == EL_DRAGON)
9442 dragon_found = TRUE;
9451 for (i = 0; i < NUM_DIRECTIONS; i++)
9453 for (j = 0; j < 3; j++)
9455 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9457 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9459 Feld[xx][yy] = EL_EMPTY;
9460 DrawLevelField(xx, yy);
9469 static void InitBuggyBase(int x, int y)
9471 int element = Feld[x][y];
9472 int activating_delay = FRAMES_PER_SECOND / 4;
9475 (element == EL_SP_BUGGY_BASE ?
9476 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9477 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9479 element == EL_SP_BUGGY_BASE_ACTIVE ?
9480 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9483 static void WarnBuggyBase(int x, int y)
9486 static int xy[4][2] =
9494 for (i = 0; i < NUM_DIRECTIONS; i++)
9496 int xx = x + xy[i][0];
9497 int yy = y + xy[i][1];
9499 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9501 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9508 static void InitTrap(int x, int y)
9510 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9513 static void ActivateTrap(int x, int y)
9515 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9518 static void ChangeActiveTrap(int x, int y)
9520 int graphic = IMG_TRAP_ACTIVE;
9522 /* if new animation frame was drawn, correct crumbled sand border */
9523 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9524 DrawLevelFieldCrumbledSand(x, y);
9527 static int getSpecialActionElement(int element, int number, int base_element)
9529 return (element != EL_EMPTY ? element :
9530 number != -1 ? base_element + number - 1 :
9534 static int getModifiedActionNumber(int value_old, int operator, int operand,
9535 int value_min, int value_max)
9537 int value_new = (operator == CA_MODE_SET ? operand :
9538 operator == CA_MODE_ADD ? value_old + operand :
9539 operator == CA_MODE_SUBTRACT ? value_old - operand :
9540 operator == CA_MODE_MULTIPLY ? value_old * operand :
9541 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9542 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9545 return (value_new < value_min ? value_min :
9546 value_new > value_max ? value_max :
9550 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9552 struct ElementInfo *ei = &element_info[element];
9553 struct ElementChangeInfo *change = &ei->change_page[page];
9554 int target_element = change->target_element;
9555 int action_type = change->action_type;
9556 int action_mode = change->action_mode;
9557 int action_arg = change->action_arg;
9560 if (!change->has_action)
9563 /* ---------- determine action paramater values -------------------------- */
9565 int level_time_value =
9566 (level.time > 0 ? TimeLeft :
9569 int action_arg_element =
9570 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9571 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9572 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9575 int action_arg_direction =
9576 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9577 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9578 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9579 change->actual_trigger_side :
9580 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9581 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9584 int action_arg_number_min =
9585 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9588 int action_arg_number_max =
9589 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9590 action_type == CA_SET_LEVEL_GEMS ? 999 :
9591 action_type == CA_SET_LEVEL_TIME ? 9999 :
9592 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9593 action_type == CA_SET_CE_VALUE ? 9999 :
9594 action_type == CA_SET_CE_SCORE ? 9999 :
9597 int action_arg_number_reset =
9598 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9599 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9600 action_type == CA_SET_LEVEL_TIME ? level.time :
9601 action_type == CA_SET_LEVEL_SCORE ? 0 :
9602 #if USE_NEW_CUSTOM_VALUE
9603 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9605 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9607 action_type == CA_SET_CE_SCORE ? 0 :
9610 int action_arg_number =
9611 (action_arg <= CA_ARG_MAX ? action_arg :
9612 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9613 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9614 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9615 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9616 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9617 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9618 #if USE_NEW_CUSTOM_VALUE
9619 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9621 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9623 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9624 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9625 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9626 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9627 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9628 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9629 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9630 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9631 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9632 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9633 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9636 int action_arg_number_old =
9637 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9638 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9639 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9640 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9641 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9644 int action_arg_number_new =
9645 getModifiedActionNumber(action_arg_number_old,
9646 action_mode, action_arg_number,
9647 action_arg_number_min, action_arg_number_max);
9649 int trigger_player_bits =
9650 (change->actual_trigger_player >= EL_PLAYER_1 &&
9651 change->actual_trigger_player <= EL_PLAYER_4 ?
9652 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9655 int action_arg_player_bits =
9656 (action_arg >= CA_ARG_PLAYER_1 &&
9657 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9658 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9661 /* ---------- execute action -------------------------------------------- */
9663 switch (action_type)
9670 /* ---------- level actions ------------------------------------------- */
9672 case CA_RESTART_LEVEL:
9674 game.restart_level = TRUE;
9679 case CA_SHOW_ENVELOPE:
9681 int element = getSpecialActionElement(action_arg_element,
9682 action_arg_number, EL_ENVELOPE_1);
9684 if (IS_ENVELOPE(element))
9685 local_player->show_envelope = element;
9690 case CA_SET_LEVEL_TIME:
9692 if (level.time > 0) /* only modify limited time value */
9694 TimeLeft = action_arg_number_new;
9697 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9699 DisplayGameControlValues();
9701 DrawGameValue_Time(TimeLeft);
9704 if (!TimeLeft && setup.time_limit)
9705 for (i = 0; i < MAX_PLAYERS; i++)
9706 KillPlayer(&stored_player[i]);
9712 case CA_SET_LEVEL_SCORE:
9714 local_player->score = action_arg_number_new;
9717 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9719 DisplayGameControlValues();
9721 DrawGameValue_Score(local_player->score);
9727 case CA_SET_LEVEL_GEMS:
9729 local_player->gems_still_needed = action_arg_number_new;
9732 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9734 DisplayGameControlValues();
9736 DrawGameValue_Emeralds(local_player->gems_still_needed);
9742 #if !USE_PLAYER_GRAVITY
9743 case CA_SET_LEVEL_GRAVITY:
9745 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9746 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9747 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9753 case CA_SET_LEVEL_WIND:
9755 game.wind_direction = action_arg_direction;
9760 /* ---------- player actions ------------------------------------------ */
9762 case CA_MOVE_PLAYER:
9764 /* automatically move to the next field in specified direction */
9765 for (i = 0; i < MAX_PLAYERS; i++)
9766 if (trigger_player_bits & (1 << i))
9767 stored_player[i].programmed_action = action_arg_direction;
9772 case CA_EXIT_PLAYER:
9774 for (i = 0; i < MAX_PLAYERS; i++)
9775 if (action_arg_player_bits & (1 << i))
9776 PlayerWins(&stored_player[i]);
9781 case CA_KILL_PLAYER:
9783 for (i = 0; i < MAX_PLAYERS; i++)
9784 if (action_arg_player_bits & (1 << i))
9785 KillPlayer(&stored_player[i]);
9790 case CA_SET_PLAYER_KEYS:
9792 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9793 int element = getSpecialActionElement(action_arg_element,
9794 action_arg_number, EL_KEY_1);
9796 if (IS_KEY(element))
9798 for (i = 0; i < MAX_PLAYERS; i++)
9800 if (trigger_player_bits & (1 << i))
9802 stored_player[i].key[KEY_NR(element)] = key_state;
9804 DrawGameDoorValues();
9812 case CA_SET_PLAYER_SPEED:
9814 for (i = 0; i < MAX_PLAYERS; i++)
9816 if (trigger_player_bits & (1 << i))
9818 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9820 if (action_arg == CA_ARG_SPEED_FASTER &&
9821 stored_player[i].cannot_move)
9823 action_arg_number = STEPSIZE_VERY_SLOW;
9825 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9826 action_arg == CA_ARG_SPEED_FASTER)
9828 action_arg_number = 2;
9829 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9832 else if (action_arg == CA_ARG_NUMBER_RESET)
9834 action_arg_number = level.initial_player_stepsize[i];
9838 getModifiedActionNumber(move_stepsize,
9841 action_arg_number_min,
9842 action_arg_number_max);
9844 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9851 case CA_SET_PLAYER_SHIELD:
9853 for (i = 0; i < MAX_PLAYERS; i++)
9855 if (trigger_player_bits & (1 << i))
9857 if (action_arg == CA_ARG_SHIELD_OFF)
9859 stored_player[i].shield_normal_time_left = 0;
9860 stored_player[i].shield_deadly_time_left = 0;
9862 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9864 stored_player[i].shield_normal_time_left = 999999;
9866 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9868 stored_player[i].shield_normal_time_left = 999999;
9869 stored_player[i].shield_deadly_time_left = 999999;
9877 #if USE_PLAYER_GRAVITY
9878 case CA_SET_PLAYER_GRAVITY:
9880 for (i = 0; i < MAX_PLAYERS; i++)
9882 if (trigger_player_bits & (1 << i))
9884 stored_player[i].gravity =
9885 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9886 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9887 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9888 stored_player[i].gravity);
9896 case CA_SET_PLAYER_ARTWORK:
9898 for (i = 0; i < MAX_PLAYERS; i++)
9900 if (trigger_player_bits & (1 << i))
9902 int artwork_element = action_arg_element;
9904 if (action_arg == CA_ARG_ELEMENT_RESET)
9906 (level.use_artwork_element[i] ? level.artwork_element[i] :
9907 stored_player[i].element_nr);
9909 #if USE_GFX_RESET_PLAYER_ARTWORK
9910 if (stored_player[i].artwork_element != artwork_element)
9911 stored_player[i].Frame = 0;
9914 stored_player[i].artwork_element = artwork_element;
9916 SetPlayerWaiting(&stored_player[i], FALSE);
9918 /* set number of special actions for bored and sleeping animation */
9919 stored_player[i].num_special_action_bored =
9920 get_num_special_action(artwork_element,
9921 ACTION_BORING_1, ACTION_BORING_LAST);
9922 stored_player[i].num_special_action_sleeping =
9923 get_num_special_action(artwork_element,
9924 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9931 /* ---------- CE actions ---------------------------------------------- */
9933 case CA_SET_CE_VALUE:
9935 #if USE_NEW_CUSTOM_VALUE
9936 int last_ce_value = CustomValue[x][y];
9938 CustomValue[x][y] = action_arg_number_new;
9940 if (CustomValue[x][y] != last_ce_value)
9942 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9943 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9945 if (CustomValue[x][y] == 0)
9947 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9948 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9956 case CA_SET_CE_SCORE:
9958 #if USE_NEW_CUSTOM_VALUE
9959 int last_ce_score = ei->collect_score;
9961 ei->collect_score = action_arg_number_new;
9963 if (ei->collect_score != last_ce_score)
9965 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9966 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9968 if (ei->collect_score == 0)
9972 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9973 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9976 This is a very special case that seems to be a mixture between
9977 CheckElementChange() and CheckTriggeredElementChange(): while
9978 the first one only affects single elements that are triggered
9979 directly, the second one affects multiple elements in the playfield
9980 that are triggered indirectly by another element. This is a third
9981 case: Changing the CE score always affects multiple identical CEs,
9982 so every affected CE must be checked, not only the single CE for
9983 which the CE score was changed in the first place (as every instance
9984 of that CE shares the same CE score, and therefore also can change)!
9986 SCAN_PLAYFIELD(xx, yy)
9988 if (Feld[xx][yy] == element)
9989 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9990 CE_SCORE_GETS_ZERO);
9999 /* ---------- engine actions ------------------------------------------ */
10001 case CA_SET_ENGINE_SCAN_MODE:
10003 InitPlayfieldScanMode(action_arg);
10013 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10015 int old_element = Feld[x][y];
10016 int new_element = GetElementFromGroupElement(element);
10017 int previous_move_direction = MovDir[x][y];
10018 #if USE_NEW_CUSTOM_VALUE
10019 int last_ce_value = CustomValue[x][y];
10021 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10022 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10023 boolean add_player_onto_element = (new_element_is_player &&
10024 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10025 /* this breaks SnakeBite when a snake is
10026 halfway through a door that closes */
10027 /* NOW FIXED AT LEVEL INIT IN files.c */
10028 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10030 IS_WALKABLE(old_element));
10033 /* check if element under the player changes from accessible to unaccessible
10034 (needed for special case of dropping element which then changes) */
10035 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10036 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10044 if (!add_player_onto_element)
10046 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10047 RemoveMovingField(x, y);
10051 Feld[x][y] = new_element;
10053 #if !USE_GFX_RESET_GFX_ANIMATION
10054 ResetGfxAnimation(x, y);
10055 ResetRandomAnimationValue(x, y);
10058 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10059 MovDir[x][y] = previous_move_direction;
10061 #if USE_NEW_CUSTOM_VALUE
10062 if (element_info[new_element].use_last_ce_value)
10063 CustomValue[x][y] = last_ce_value;
10066 InitField_WithBug1(x, y, FALSE);
10068 new_element = Feld[x][y]; /* element may have changed */
10070 #if USE_GFX_RESET_GFX_ANIMATION
10071 ResetGfxAnimation(x, y);
10072 ResetRandomAnimationValue(x, y);
10075 DrawLevelField(x, y);
10077 if (GFX_CRUMBLED(new_element))
10078 DrawLevelFieldCrumbledSandNeighbours(x, y);
10082 /* check if element under the player changes from accessible to unaccessible
10083 (needed for special case of dropping element which then changes) */
10084 /* (must be checked after creating new element for walkable group elements) */
10085 #if USE_FIX_KILLED_BY_NON_WALKABLE
10086 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10087 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10094 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10095 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10104 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10105 if (new_element_is_player)
10106 RelocatePlayer(x, y, new_element);
10109 ChangeCount[x][y]++; /* count number of changes in the same frame */
10111 TestIfBadThingTouchesPlayer(x, y);
10112 TestIfPlayerTouchesCustomElement(x, y);
10113 TestIfElementTouchesCustomElement(x, y);
10116 static void CreateField(int x, int y, int element)
10118 CreateFieldExt(x, y, element, FALSE);
10121 static void CreateElementFromChange(int x, int y, int element)
10123 element = GET_VALID_RUNTIME_ELEMENT(element);
10125 #if USE_STOP_CHANGED_ELEMENTS
10126 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10128 int old_element = Feld[x][y];
10130 /* prevent changed element from moving in same engine frame
10131 unless both old and new element can either fall or move */
10132 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10133 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10138 CreateFieldExt(x, y, element, TRUE);
10141 static boolean ChangeElement(int x, int y, int element, int page)
10143 struct ElementInfo *ei = &element_info[element];
10144 struct ElementChangeInfo *change = &ei->change_page[page];
10145 int ce_value = CustomValue[x][y];
10146 int ce_score = ei->collect_score;
10147 int target_element;
10148 int old_element = Feld[x][y];
10150 /* always use default change event to prevent running into a loop */
10151 if (ChangeEvent[x][y] == -1)
10152 ChangeEvent[x][y] = CE_DELAY;
10154 if (ChangeEvent[x][y] == CE_DELAY)
10156 /* reset actual trigger element, trigger player and action element */
10157 change->actual_trigger_element = EL_EMPTY;
10158 change->actual_trigger_player = EL_PLAYER_1;
10159 change->actual_trigger_side = CH_SIDE_NONE;
10160 change->actual_trigger_ce_value = 0;
10161 change->actual_trigger_ce_score = 0;
10164 /* do not change elements more than a specified maximum number of changes */
10165 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10168 ChangeCount[x][y]++; /* count number of changes in the same frame */
10170 if (change->explode)
10177 if (change->use_target_content)
10179 boolean complete_replace = TRUE;
10180 boolean can_replace[3][3];
10183 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10186 boolean is_walkable;
10187 boolean is_diggable;
10188 boolean is_collectible;
10189 boolean is_removable;
10190 boolean is_destructible;
10191 int ex = x + xx - 1;
10192 int ey = y + yy - 1;
10193 int content_element = change->target_content.e[xx][yy];
10196 can_replace[xx][yy] = TRUE;
10198 if (ex == x && ey == y) /* do not check changing element itself */
10201 if (content_element == EL_EMPTY_SPACE)
10203 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10208 if (!IN_LEV_FIELD(ex, ey))
10210 can_replace[xx][yy] = FALSE;
10211 complete_replace = FALSE;
10218 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10219 e = MovingOrBlocked2Element(ex, ey);
10221 is_empty = (IS_FREE(ex, ey) ||
10222 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10224 is_walkable = (is_empty || IS_WALKABLE(e));
10225 is_diggable = (is_empty || IS_DIGGABLE(e));
10226 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10227 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10228 is_removable = (is_diggable || is_collectible);
10230 can_replace[xx][yy] =
10231 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10232 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10233 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10234 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10235 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10236 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10237 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10239 if (!can_replace[xx][yy])
10240 complete_replace = FALSE;
10243 if (!change->only_if_complete || complete_replace)
10245 boolean something_has_changed = FALSE;
10247 if (change->only_if_complete && change->use_random_replace &&
10248 RND(100) < change->random_percentage)
10251 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10253 int ex = x + xx - 1;
10254 int ey = y + yy - 1;
10255 int content_element;
10257 if (can_replace[xx][yy] && (!change->use_random_replace ||
10258 RND(100) < change->random_percentage))
10260 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10261 RemoveMovingField(ex, ey);
10263 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10265 content_element = change->target_content.e[xx][yy];
10266 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10267 ce_value, ce_score);
10269 CreateElementFromChange(ex, ey, target_element);
10271 something_has_changed = TRUE;
10273 /* for symmetry reasons, freeze newly created border elements */
10274 if (ex != x || ey != y)
10275 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10279 if (something_has_changed)
10281 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10282 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10288 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10289 ce_value, ce_score);
10291 if (element == EL_DIAGONAL_GROWING ||
10292 element == EL_DIAGONAL_SHRINKING)
10294 target_element = Store[x][y];
10296 Store[x][y] = EL_EMPTY;
10299 CreateElementFromChange(x, y, target_element);
10301 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10302 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10305 /* this uses direct change before indirect change */
10306 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10311 #if USE_NEW_DELAYED_ACTION
10313 static void HandleElementChange(int x, int y, int page)
10315 int element = MovingOrBlocked2Element(x, y);
10316 struct ElementInfo *ei = &element_info[element];
10317 struct ElementChangeInfo *change = &ei->change_page[page];
10320 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10321 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10324 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10325 x, y, element, element_info[element].token_name);
10326 printf("HandleElementChange(): This should never happen!\n");
10331 /* this can happen with classic bombs on walkable, changing elements */
10332 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10335 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10336 ChangeDelay[x][y] = 0;
10342 if (ChangeDelay[x][y] == 0) /* initialize element change */
10344 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10346 if (change->can_change)
10349 /* !!! not clear why graphic animation should be reset at all here !!! */
10350 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10351 #if USE_GFX_RESET_WHEN_NOT_MOVING
10352 /* when a custom element is about to change (for example by change delay),
10353 do not reset graphic animation when the custom element is moving */
10354 if (!IS_MOVING(x, y))
10357 ResetGfxAnimation(x, y);
10358 ResetRandomAnimationValue(x, y);
10362 if (change->pre_change_function)
10363 change->pre_change_function(x, y);
10367 ChangeDelay[x][y]--;
10369 if (ChangeDelay[x][y] != 0) /* continue element change */
10371 if (change->can_change)
10373 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10375 if (IS_ANIMATED(graphic))
10376 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10378 if (change->change_function)
10379 change->change_function(x, y);
10382 else /* finish element change */
10384 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10386 page = ChangePage[x][y];
10387 ChangePage[x][y] = -1;
10389 change = &ei->change_page[page];
10392 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10394 ChangeDelay[x][y] = 1; /* try change after next move step */
10395 ChangePage[x][y] = page; /* remember page to use for change */
10400 if (change->can_change)
10402 if (ChangeElement(x, y, element, page))
10404 if (change->post_change_function)
10405 change->post_change_function(x, y);
10409 if (change->has_action)
10410 ExecuteCustomElementAction(x, y, element, page);
10416 static void HandleElementChange(int x, int y, int page)
10418 int element = MovingOrBlocked2Element(x, y);
10419 struct ElementInfo *ei = &element_info[element];
10420 struct ElementChangeInfo *change = &ei->change_page[page];
10423 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10426 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10427 x, y, element, element_info[element].token_name);
10428 printf("HandleElementChange(): This should never happen!\n");
10433 /* this can happen with classic bombs on walkable, changing elements */
10434 if (!CAN_CHANGE(element))
10437 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10438 ChangeDelay[x][y] = 0;
10444 if (ChangeDelay[x][y] == 0) /* initialize element change */
10446 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10448 ResetGfxAnimation(x, y);
10449 ResetRandomAnimationValue(x, y);
10451 if (change->pre_change_function)
10452 change->pre_change_function(x, y);
10455 ChangeDelay[x][y]--;
10457 if (ChangeDelay[x][y] != 0) /* continue element change */
10459 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10461 if (IS_ANIMATED(graphic))
10462 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10464 if (change->change_function)
10465 change->change_function(x, y);
10467 else /* finish element change */
10469 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10471 page = ChangePage[x][y];
10472 ChangePage[x][y] = -1;
10474 change = &ei->change_page[page];
10477 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10479 ChangeDelay[x][y] = 1; /* try change after next move step */
10480 ChangePage[x][y] = page; /* remember page to use for change */
10485 if (ChangeElement(x, y, element, page))
10487 if (change->post_change_function)
10488 change->post_change_function(x, y);
10495 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10496 int trigger_element,
10498 int trigger_player,
10502 boolean change_done_any = FALSE;
10503 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10506 if (!(trigger_events[trigger_element][trigger_event]))
10510 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10511 trigger_event, recursion_loop_depth, recursion_loop_detected,
10512 recursion_loop_element, EL_NAME(recursion_loop_element));
10515 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10519 int element = EL_CUSTOM_START + i;
10520 boolean change_done = FALSE;
10523 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10524 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10527 for (p = 0; p < element_info[element].num_change_pages; p++)
10529 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10531 if (change->can_change_or_has_action &&
10532 change->has_event[trigger_event] &&
10533 change->trigger_side & trigger_side &&
10534 change->trigger_player & trigger_player &&
10535 change->trigger_page & trigger_page_bits &&
10536 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10538 change->actual_trigger_element = trigger_element;
10539 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10540 change->actual_trigger_side = trigger_side;
10541 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10542 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10544 if ((change->can_change && !change_done) || change->has_action)
10548 SCAN_PLAYFIELD(x, y)
10550 if (Feld[x][y] == element)
10552 if (change->can_change && !change_done)
10554 ChangeDelay[x][y] = 1;
10555 ChangeEvent[x][y] = trigger_event;
10557 HandleElementChange(x, y, p);
10559 #if USE_NEW_DELAYED_ACTION
10560 else if (change->has_action)
10562 ExecuteCustomElementAction(x, y, element, p);
10563 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10566 if (change->has_action)
10568 ExecuteCustomElementAction(x, y, element, p);
10569 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10575 if (change->can_change)
10577 change_done = TRUE;
10578 change_done_any = TRUE;
10585 RECURSION_LOOP_DETECTION_END();
10587 return change_done_any;
10590 static boolean CheckElementChangeExt(int x, int y,
10592 int trigger_element,
10594 int trigger_player,
10597 boolean change_done = FALSE;
10600 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10601 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10604 if (Feld[x][y] == EL_BLOCKED)
10606 Blocked2Moving(x, y, &x, &y);
10607 element = Feld[x][y];
10611 /* check if element has already changed */
10612 if (Feld[x][y] != element)
10615 /* check if element has already changed or is about to change after moving */
10616 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10617 Feld[x][y] != element) ||
10619 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10620 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10621 ChangePage[x][y] != -1)))
10626 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10627 trigger_event, recursion_loop_depth, recursion_loop_detected,
10628 recursion_loop_element, EL_NAME(recursion_loop_element));
10631 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10633 for (p = 0; p < element_info[element].num_change_pages; p++)
10635 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10637 /* check trigger element for all events where the element that is checked
10638 for changing interacts with a directly adjacent element -- this is
10639 different to element changes that affect other elements to change on the
10640 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10641 boolean check_trigger_element =
10642 (trigger_event == CE_TOUCHING_X ||
10643 trigger_event == CE_HITTING_X ||
10644 trigger_event == CE_HIT_BY_X ||
10646 /* this one was forgotten until 3.2.3 */
10647 trigger_event == CE_DIGGING_X);
10650 if (change->can_change_or_has_action &&
10651 change->has_event[trigger_event] &&
10652 change->trigger_side & trigger_side &&
10653 change->trigger_player & trigger_player &&
10654 (!check_trigger_element ||
10655 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10657 change->actual_trigger_element = trigger_element;
10658 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10659 change->actual_trigger_side = trigger_side;
10660 change->actual_trigger_ce_value = CustomValue[x][y];
10661 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10663 /* special case: trigger element not at (x,y) position for some events */
10664 if (check_trigger_element)
10676 { 0, 0 }, { 0, 0 }, { 0, 0 },
10680 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10681 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10683 change->actual_trigger_ce_value = CustomValue[xx][yy];
10684 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10687 if (change->can_change && !change_done)
10689 ChangeDelay[x][y] = 1;
10690 ChangeEvent[x][y] = trigger_event;
10692 HandleElementChange(x, y, p);
10694 change_done = TRUE;
10696 #if USE_NEW_DELAYED_ACTION
10697 else if (change->has_action)
10699 ExecuteCustomElementAction(x, y, element, p);
10700 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10703 if (change->has_action)
10705 ExecuteCustomElementAction(x, y, element, p);
10706 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10712 RECURSION_LOOP_DETECTION_END();
10714 return change_done;
10717 static void PlayPlayerSound(struct PlayerInfo *player)
10719 int jx = player->jx, jy = player->jy;
10720 int sound_element = player->artwork_element;
10721 int last_action = player->last_action_waiting;
10722 int action = player->action_waiting;
10724 if (player->is_waiting)
10726 if (action != last_action)
10727 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10729 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10733 if (action != last_action)
10734 StopSound(element_info[sound_element].sound[last_action]);
10736 if (last_action == ACTION_SLEEPING)
10737 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10741 static void PlayAllPlayersSound()
10745 for (i = 0; i < MAX_PLAYERS; i++)
10746 if (stored_player[i].active)
10747 PlayPlayerSound(&stored_player[i]);
10750 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10752 boolean last_waiting = player->is_waiting;
10753 int move_dir = player->MovDir;
10755 player->dir_waiting = move_dir;
10756 player->last_action_waiting = player->action_waiting;
10760 if (!last_waiting) /* not waiting -> waiting */
10762 player->is_waiting = TRUE;
10764 player->frame_counter_bored =
10766 game.player_boring_delay_fixed +
10767 GetSimpleRandom(game.player_boring_delay_random);
10768 player->frame_counter_sleeping =
10770 game.player_sleeping_delay_fixed +
10771 GetSimpleRandom(game.player_sleeping_delay_random);
10773 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10776 if (game.player_sleeping_delay_fixed +
10777 game.player_sleeping_delay_random > 0 &&
10778 player->anim_delay_counter == 0 &&
10779 player->post_delay_counter == 0 &&
10780 FrameCounter >= player->frame_counter_sleeping)
10781 player->is_sleeping = TRUE;
10782 else if (game.player_boring_delay_fixed +
10783 game.player_boring_delay_random > 0 &&
10784 FrameCounter >= player->frame_counter_bored)
10785 player->is_bored = TRUE;
10787 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10788 player->is_bored ? ACTION_BORING :
10791 if (player->is_sleeping && player->use_murphy)
10793 /* special case for sleeping Murphy when leaning against non-free tile */
10795 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10796 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10797 !IS_MOVING(player->jx - 1, player->jy)))
10798 move_dir = MV_LEFT;
10799 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10800 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10801 !IS_MOVING(player->jx + 1, player->jy)))
10802 move_dir = MV_RIGHT;
10804 player->is_sleeping = FALSE;
10806 player->dir_waiting = move_dir;
10809 if (player->is_sleeping)
10811 if (player->num_special_action_sleeping > 0)
10813 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10815 int last_special_action = player->special_action_sleeping;
10816 int num_special_action = player->num_special_action_sleeping;
10817 int special_action =
10818 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10819 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10820 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10821 last_special_action + 1 : ACTION_SLEEPING);
10822 int special_graphic =
10823 el_act_dir2img(player->artwork_element, special_action, move_dir);
10825 player->anim_delay_counter =
10826 graphic_info[special_graphic].anim_delay_fixed +
10827 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10828 player->post_delay_counter =
10829 graphic_info[special_graphic].post_delay_fixed +
10830 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10832 player->special_action_sleeping = special_action;
10835 if (player->anim_delay_counter > 0)
10837 player->action_waiting = player->special_action_sleeping;
10838 player->anim_delay_counter--;
10840 else if (player->post_delay_counter > 0)
10842 player->post_delay_counter--;
10846 else if (player->is_bored)
10848 if (player->num_special_action_bored > 0)
10850 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10852 int special_action =
10853 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10854 int special_graphic =
10855 el_act_dir2img(player->artwork_element, special_action, move_dir);
10857 player->anim_delay_counter =
10858 graphic_info[special_graphic].anim_delay_fixed +
10859 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10860 player->post_delay_counter =
10861 graphic_info[special_graphic].post_delay_fixed +
10862 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10864 player->special_action_bored = special_action;
10867 if (player->anim_delay_counter > 0)
10869 player->action_waiting = player->special_action_bored;
10870 player->anim_delay_counter--;
10872 else if (player->post_delay_counter > 0)
10874 player->post_delay_counter--;
10879 else if (last_waiting) /* waiting -> not waiting */
10881 player->is_waiting = FALSE;
10882 player->is_bored = FALSE;
10883 player->is_sleeping = FALSE;
10885 player->frame_counter_bored = -1;
10886 player->frame_counter_sleeping = -1;
10888 player->anim_delay_counter = 0;
10889 player->post_delay_counter = 0;
10891 player->dir_waiting = player->MovDir;
10892 player->action_waiting = ACTION_DEFAULT;
10894 player->special_action_bored = ACTION_DEFAULT;
10895 player->special_action_sleeping = ACTION_DEFAULT;
10899 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10901 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10902 int left = player_action & JOY_LEFT;
10903 int right = player_action & JOY_RIGHT;
10904 int up = player_action & JOY_UP;
10905 int down = player_action & JOY_DOWN;
10906 int button1 = player_action & JOY_BUTTON_1;
10907 int button2 = player_action & JOY_BUTTON_2;
10908 int dx = (left ? -1 : right ? 1 : 0);
10909 int dy = (up ? -1 : down ? 1 : 0);
10911 if (!player->active || tape.pausing)
10917 snapped = SnapField(player, dx, dy);
10921 dropped = DropElement(player);
10923 moved = MovePlayer(player, dx, dy);
10926 if (tape.single_step && tape.recording && !tape.pausing)
10928 if (button1 || (dropped && !moved))
10930 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10931 SnapField(player, 0, 0); /* stop snapping */
10935 SetPlayerWaiting(player, FALSE);
10937 return player_action;
10941 /* no actions for this player (no input at player's configured device) */
10943 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10944 SnapField(player, 0, 0);
10945 CheckGravityMovementWhenNotMoving(player);
10947 if (player->MovPos == 0)
10948 SetPlayerWaiting(player, TRUE);
10950 if (player->MovPos == 0) /* needed for tape.playing */
10951 player->is_moving = FALSE;
10953 player->is_dropping = FALSE;
10954 player->is_dropping_pressed = FALSE;
10955 player->drop_pressed_delay = 0;
10961 static void CheckLevelTime()
10965 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10967 if (level.native_em_level->lev->home == 0) /* all players at home */
10969 PlayerWins(local_player);
10971 AllPlayersGone = TRUE;
10973 level.native_em_level->lev->home = -1;
10976 if (level.native_em_level->ply[0]->alive == 0 &&
10977 level.native_em_level->ply[1]->alive == 0 &&
10978 level.native_em_level->ply[2]->alive == 0 &&
10979 level.native_em_level->ply[3]->alive == 0) /* all dead */
10980 AllPlayersGone = TRUE;
10983 if (TimeFrames >= FRAMES_PER_SECOND)
10988 for (i = 0; i < MAX_PLAYERS; i++)
10990 struct PlayerInfo *player = &stored_player[i];
10992 if (SHIELD_ON(player))
10994 player->shield_normal_time_left--;
10996 if (player->shield_deadly_time_left > 0)
10997 player->shield_deadly_time_left--;
11001 if (!local_player->LevelSolved && !level.use_step_counter)
11009 if (TimeLeft <= 10 && setup.time_limit)
11010 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11013 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11015 DisplayGameControlValues();
11017 DrawGameValue_Time(TimeLeft);
11020 if (!TimeLeft && setup.time_limit)
11022 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11023 level.native_em_level->lev->killed_out_of_time = TRUE;
11025 for (i = 0; i < MAX_PLAYERS; i++)
11026 KillPlayer(&stored_player[i]);
11030 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11032 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11034 DisplayGameControlValues();
11037 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11038 DrawGameValue_Time(TimePlayed);
11041 level.native_em_level->lev->time =
11042 (level.time == 0 ? TimePlayed : TimeLeft);
11045 if (tape.recording || tape.playing)
11046 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11049 UpdateGameDoorValues();
11050 DrawGameDoorValues();
11053 void AdvanceFrameAndPlayerCounters(int player_nr)
11057 /* advance frame counters (global frame counter and time frame counter) */
11061 /* advance player counters (counters for move delay, move animation etc.) */
11062 for (i = 0; i < MAX_PLAYERS; i++)
11064 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11065 int move_delay_value = stored_player[i].move_delay_value;
11066 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11068 if (!advance_player_counters) /* not all players may be affected */
11071 #if USE_NEW_PLAYER_ANIM
11072 if (move_frames == 0) /* less than one move per game frame */
11074 int stepsize = TILEX / move_delay_value;
11075 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11076 int count = (stored_player[i].is_moving ?
11077 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11079 if (count % delay == 0)
11084 stored_player[i].Frame += move_frames;
11086 if (stored_player[i].MovPos != 0)
11087 stored_player[i].StepFrame += move_frames;
11089 if (stored_player[i].move_delay > 0)
11090 stored_player[i].move_delay--;
11092 /* due to bugs in previous versions, counter must count up, not down */
11093 if (stored_player[i].push_delay != -1)
11094 stored_player[i].push_delay++;
11096 if (stored_player[i].drop_delay > 0)
11097 stored_player[i].drop_delay--;
11099 if (stored_player[i].is_dropping_pressed)
11100 stored_player[i].drop_pressed_delay++;
11104 void StartGameActions(boolean init_network_game, boolean record_tape,
11107 unsigned long new_random_seed = InitRND(random_seed);
11110 TapeStartRecording(new_random_seed);
11112 #if defined(NETWORK_AVALIABLE)
11113 if (init_network_game)
11115 SendToServer_StartPlaying();
11126 static unsigned long game_frame_delay = 0;
11127 unsigned long game_frame_delay_value;
11128 byte *recorded_player_action;
11129 byte summarized_player_action = 0;
11130 byte tape_action[MAX_PLAYERS];
11133 /* detect endless loops, caused by custom element programming */
11134 if (recursion_loop_detected && recursion_loop_depth == 0)
11136 char *message = getStringCat3("Internal Error ! Element ",
11137 EL_NAME(recursion_loop_element),
11138 " caused endless loop ! Quit the game ?");
11140 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11141 EL_NAME(recursion_loop_element));
11143 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11145 recursion_loop_detected = FALSE; /* if game should be continued */
11152 if (game.restart_level)
11153 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11155 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11157 if (level.native_em_level->lev->home == 0) /* all players at home */
11159 PlayerWins(local_player);
11161 AllPlayersGone = TRUE;
11163 level.native_em_level->lev->home = -1;
11166 if (level.native_em_level->ply[0]->alive == 0 &&
11167 level.native_em_level->ply[1]->alive == 0 &&
11168 level.native_em_level->ply[2]->alive == 0 &&
11169 level.native_em_level->ply[3]->alive == 0) /* all dead */
11170 AllPlayersGone = TRUE;
11173 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11176 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11179 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11182 game_frame_delay_value =
11183 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11185 if (tape.playing && tape.warp_forward && !tape.pausing)
11186 game_frame_delay_value = 0;
11188 /* ---------- main game synchronization point ---------- */
11190 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11192 if (network_playing && !network_player_action_received)
11194 /* try to get network player actions in time */
11196 #if defined(NETWORK_AVALIABLE)
11197 /* last chance to get network player actions without main loop delay */
11198 HandleNetworking();
11201 /* game was quit by network peer */
11202 if (game_status != GAME_MODE_PLAYING)
11205 if (!network_player_action_received)
11206 return; /* failed to get network player actions in time */
11208 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11214 /* at this point we know that we really continue executing the game */
11216 network_player_action_received = FALSE;
11218 /* when playing tape, read previously recorded player input from tape data */
11219 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11222 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11227 if (tape.set_centered_player)
11229 game.centered_player_nr_next = tape.centered_player_nr_next;
11230 game.set_centered_player = TRUE;
11233 for (i = 0; i < MAX_PLAYERS; i++)
11235 summarized_player_action |= stored_player[i].action;
11237 if (!network_playing)
11238 stored_player[i].effective_action = stored_player[i].action;
11241 #if defined(NETWORK_AVALIABLE)
11242 if (network_playing)
11243 SendToServer_MovePlayer(summarized_player_action);
11246 if (!options.network && !setup.team_mode)
11247 local_player->effective_action = summarized_player_action;
11249 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11251 for (i = 0; i < MAX_PLAYERS; i++)
11252 stored_player[i].effective_action =
11253 (i == game.centered_player_nr ? summarized_player_action : 0);
11256 if (recorded_player_action != NULL)
11257 for (i = 0; i < MAX_PLAYERS; i++)
11258 stored_player[i].effective_action = recorded_player_action[i];
11260 for (i = 0; i < MAX_PLAYERS; i++)
11262 tape_action[i] = stored_player[i].effective_action;
11264 /* (this can only happen in the R'n'D game engine) */
11265 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11266 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11269 /* only record actions from input devices, but not programmed actions */
11270 if (tape.recording)
11271 TapeRecordAction(tape_action);
11273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11275 GameActions_EM_Main();
11283 void GameActions_EM_Main()
11285 byte effective_action[MAX_PLAYERS];
11286 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11289 for (i = 0; i < MAX_PLAYERS; i++)
11290 effective_action[i] = stored_player[i].effective_action;
11292 GameActions_EM(effective_action, warp_mode);
11296 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11299 void GameActions_RND()
11301 int magic_wall_x = 0, magic_wall_y = 0;
11302 int i, x, y, element, graphic;
11304 InitPlayfieldScanModeVars();
11306 #if USE_ONE_MORE_CHANGE_PER_FRAME
11307 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11309 SCAN_PLAYFIELD(x, y)
11311 ChangeCount[x][y] = 0;
11312 ChangeEvent[x][y] = -1;
11317 if (game.set_centered_player)
11319 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11321 /* switching to "all players" only possible if all players fit to screen */
11322 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11324 game.centered_player_nr_next = game.centered_player_nr;
11325 game.set_centered_player = FALSE;
11328 /* do not switch focus to non-existing (or non-active) player */
11329 if (game.centered_player_nr_next >= 0 &&
11330 !stored_player[game.centered_player_nr_next].active)
11332 game.centered_player_nr_next = game.centered_player_nr;
11333 game.set_centered_player = FALSE;
11337 if (game.set_centered_player &&
11338 ScreenMovPos == 0) /* screen currently aligned at tile position */
11342 if (game.centered_player_nr_next == -1)
11344 setScreenCenteredToAllPlayers(&sx, &sy);
11348 sx = stored_player[game.centered_player_nr_next].jx;
11349 sy = stored_player[game.centered_player_nr_next].jy;
11352 game.centered_player_nr = game.centered_player_nr_next;
11353 game.set_centered_player = FALSE;
11355 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11356 DrawGameDoorValues();
11359 for (i = 0; i < MAX_PLAYERS; i++)
11361 int actual_player_action = stored_player[i].effective_action;
11364 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11365 - rnd_equinox_tetrachloride 048
11366 - rnd_equinox_tetrachloride_ii 096
11367 - rnd_emanuel_schmieg 002
11368 - doctor_sloan_ww 001, 020
11370 if (stored_player[i].MovPos == 0)
11371 CheckGravityMovement(&stored_player[i]);
11374 /* overwrite programmed action with tape action */
11375 if (stored_player[i].programmed_action)
11376 actual_player_action = stored_player[i].programmed_action;
11378 PlayerActions(&stored_player[i], actual_player_action);
11380 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11383 ScrollScreen(NULL, SCROLL_GO_ON);
11385 /* for backwards compatibility, the following code emulates a fixed bug that
11386 occured when pushing elements (causing elements that just made their last
11387 pushing step to already (if possible) make their first falling step in the
11388 same game frame, which is bad); this code is also needed to use the famous
11389 "spring push bug" which is used in older levels and might be wanted to be
11390 used also in newer levels, but in this case the buggy pushing code is only
11391 affecting the "spring" element and no other elements */
11393 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11395 for (i = 0; i < MAX_PLAYERS; i++)
11397 struct PlayerInfo *player = &stored_player[i];
11398 int x = player->jx;
11399 int y = player->jy;
11401 if (player->active && player->is_pushing && player->is_moving &&
11403 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11404 Feld[x][y] == EL_SPRING))
11406 ContinueMoving(x, y);
11408 /* continue moving after pushing (this is actually a bug) */
11409 if (!IS_MOVING(x, y))
11410 Stop[x][y] = FALSE;
11416 debug_print_timestamp(0, "start main loop profiling");
11419 SCAN_PLAYFIELD(x, y)
11421 ChangeCount[x][y] = 0;
11422 ChangeEvent[x][y] = -1;
11424 /* this must be handled before main playfield loop */
11425 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11428 if (MovDelay[x][y] <= 0)
11432 #if USE_NEW_SNAP_DELAY
11433 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11436 if (MovDelay[x][y] <= 0)
11439 DrawLevelField(x, y);
11441 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11447 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11449 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11450 printf("GameActions(): This should never happen!\n");
11452 ChangePage[x][y] = -1;
11456 Stop[x][y] = FALSE;
11457 if (WasJustMoving[x][y] > 0)
11458 WasJustMoving[x][y]--;
11459 if (WasJustFalling[x][y] > 0)
11460 WasJustFalling[x][y]--;
11461 if (CheckCollision[x][y] > 0)
11462 CheckCollision[x][y]--;
11463 if (CheckImpact[x][y] > 0)
11464 CheckImpact[x][y]--;
11468 /* reset finished pushing action (not done in ContinueMoving() to allow
11469 continuous pushing animation for elements with zero push delay) */
11470 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11472 ResetGfxAnimation(x, y);
11473 DrawLevelField(x, y);
11477 if (IS_BLOCKED(x, y))
11481 Blocked2Moving(x, y, &oldx, &oldy);
11482 if (!IS_MOVING(oldx, oldy))
11484 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11485 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11486 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11487 printf("GameActions(): This should never happen!\n");
11494 debug_print_timestamp(0, "- time for pre-main loop:");
11497 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11498 SCAN_PLAYFIELD(x, y)
11500 element = Feld[x][y];
11501 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11506 int element2 = element;
11507 int graphic2 = graphic;
11509 int element2 = Feld[x][y];
11510 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11512 int last_gfx_frame = GfxFrame[x][y];
11514 if (graphic_info[graphic2].anim_global_sync)
11515 GfxFrame[x][y] = FrameCounter;
11516 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11517 GfxFrame[x][y] = CustomValue[x][y];
11518 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11519 GfxFrame[x][y] = element_info[element2].collect_score;
11520 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11521 GfxFrame[x][y] = ChangeDelay[x][y];
11523 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11524 DrawLevelGraphicAnimation(x, y, graphic2);
11527 ResetGfxFrame(x, y, TRUE);
11531 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11532 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11533 ResetRandomAnimationValue(x, y);
11537 SetRandomAnimationValue(x, y);
11541 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11544 #endif // -------------------- !!! TEST ONLY !!! --------------------
11547 debug_print_timestamp(0, "- time for TEST loop: -->");
11550 SCAN_PLAYFIELD(x, y)
11552 element = Feld[x][y];
11553 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11555 ResetGfxFrame(x, y, TRUE);
11557 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11558 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11559 ResetRandomAnimationValue(x, y);
11561 SetRandomAnimationValue(x, y);
11563 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11565 if (IS_INACTIVE(element))
11567 if (IS_ANIMATED(graphic))
11568 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11573 /* this may take place after moving, so 'element' may have changed */
11574 if (IS_CHANGING(x, y) &&
11575 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11577 int page = element_info[element].event_page_nr[CE_DELAY];
11580 HandleElementChange(x, y, page);
11582 if (CAN_CHANGE(element))
11583 HandleElementChange(x, y, page);
11585 if (HAS_ACTION(element))
11586 ExecuteCustomElementAction(x, y, element, page);
11589 element = Feld[x][y];
11590 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11593 #if 0 // ---------------------------------------------------------------------
11595 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11599 element = Feld[x][y];
11600 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11602 if (IS_ANIMATED(graphic) &&
11603 !IS_MOVING(x, y) &&
11605 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11607 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11608 DrawTwinkleOnField(x, y);
11610 else if (IS_MOVING(x, y))
11611 ContinueMoving(x, y);
11618 case EL_EM_EXIT_OPEN:
11619 case EL_SP_EXIT_OPEN:
11620 case EL_STEEL_EXIT_OPEN:
11621 case EL_EM_STEEL_EXIT_OPEN:
11622 case EL_SP_TERMINAL:
11623 case EL_SP_TERMINAL_ACTIVE:
11624 case EL_EXTRA_TIME:
11625 case EL_SHIELD_NORMAL:
11626 case EL_SHIELD_DEADLY:
11627 if (IS_ANIMATED(graphic))
11628 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11631 case EL_DYNAMITE_ACTIVE:
11632 case EL_EM_DYNAMITE_ACTIVE:
11633 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11634 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11635 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11636 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11637 case EL_SP_DISK_RED_ACTIVE:
11638 CheckDynamite(x, y);
11641 case EL_AMOEBA_GROWING:
11642 AmoebeWaechst(x, y);
11645 case EL_AMOEBA_SHRINKING:
11646 AmoebaDisappearing(x, y);
11649 #if !USE_NEW_AMOEBA_CODE
11650 case EL_AMOEBA_WET:
11651 case EL_AMOEBA_DRY:
11652 case EL_AMOEBA_FULL:
11654 case EL_EMC_DRIPPER:
11655 AmoebeAbleger(x, y);
11659 case EL_GAME_OF_LIFE:
11664 case EL_EXIT_CLOSED:
11668 case EL_EM_EXIT_CLOSED:
11672 case EL_STEEL_EXIT_CLOSED:
11673 CheckExitSteel(x, y);
11676 case EL_EM_STEEL_EXIT_CLOSED:
11677 CheckExitSteelEM(x, y);
11680 case EL_SP_EXIT_CLOSED:
11684 case EL_EXPANDABLE_WALL_GROWING:
11685 case EL_EXPANDABLE_STEELWALL_GROWING:
11686 MauerWaechst(x, y);
11689 case EL_EXPANDABLE_WALL:
11690 case EL_EXPANDABLE_WALL_HORIZONTAL:
11691 case EL_EXPANDABLE_WALL_VERTICAL:
11692 case EL_EXPANDABLE_WALL_ANY:
11693 case EL_BD_EXPANDABLE_WALL:
11694 MauerAbleger(x, y);
11697 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11698 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11699 case EL_EXPANDABLE_STEELWALL_ANY:
11700 MauerAblegerStahl(x, y);
11704 CheckForDragon(x, y);
11710 case EL_ELEMENT_SNAPPING:
11711 case EL_DIAGONAL_SHRINKING:
11712 case EL_DIAGONAL_GROWING:
11715 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11717 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11722 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11723 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11728 #else // ---------------------------------------------------------------------
11730 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11734 element = Feld[x][y];
11735 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11737 if (IS_ANIMATED(graphic) &&
11738 !IS_MOVING(x, y) &&
11740 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11742 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11743 DrawTwinkleOnField(x, y);
11745 else if ((element == EL_ACID ||
11746 element == EL_EXIT_OPEN ||
11747 element == EL_EM_EXIT_OPEN ||
11748 element == EL_SP_EXIT_OPEN ||
11749 element == EL_STEEL_EXIT_OPEN ||
11750 element == EL_EM_STEEL_EXIT_OPEN ||
11751 element == EL_SP_TERMINAL ||
11752 element == EL_SP_TERMINAL_ACTIVE ||
11753 element == EL_EXTRA_TIME ||
11754 element == EL_SHIELD_NORMAL ||
11755 element == EL_SHIELD_DEADLY) &&
11756 IS_ANIMATED(graphic))
11757 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11758 else if (IS_MOVING(x, y))
11759 ContinueMoving(x, y);
11760 else if (IS_ACTIVE_BOMB(element))
11761 CheckDynamite(x, y);
11762 else if (element == EL_AMOEBA_GROWING)
11763 AmoebeWaechst(x, y);
11764 else if (element == EL_AMOEBA_SHRINKING)
11765 AmoebaDisappearing(x, y);
11767 #if !USE_NEW_AMOEBA_CODE
11768 else if (IS_AMOEBALIVE(element))
11769 AmoebeAbleger(x, y);
11772 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11774 else if (element == EL_EXIT_CLOSED)
11776 else if (element == EL_EM_EXIT_CLOSED)
11778 else if (element == EL_STEEL_EXIT_CLOSED)
11779 CheckExitSteel(x, y);
11780 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11781 CheckExitSteelEM(x, y);
11782 else if (element == EL_SP_EXIT_CLOSED)
11784 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11785 element == EL_EXPANDABLE_STEELWALL_GROWING)
11786 MauerWaechst(x, y);
11787 else if (element == EL_EXPANDABLE_WALL ||
11788 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11789 element == EL_EXPANDABLE_WALL_VERTICAL ||
11790 element == EL_EXPANDABLE_WALL_ANY ||
11791 element == EL_BD_EXPANDABLE_WALL)
11792 MauerAbleger(x, y);
11793 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11794 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11795 element == EL_EXPANDABLE_STEELWALL_ANY)
11796 MauerAblegerStahl(x, y);
11797 else if (element == EL_FLAMES)
11798 CheckForDragon(x, y);
11799 else if (element == EL_EXPLOSION)
11800 ; /* drawing of correct explosion animation is handled separately */
11801 else if (element == EL_ELEMENT_SNAPPING ||
11802 element == EL_DIAGONAL_SHRINKING ||
11803 element == EL_DIAGONAL_GROWING)
11805 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11809 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11810 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11812 #endif // ---------------------------------------------------------------------
11814 if (IS_BELT_ACTIVE(element))
11815 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11817 if (game.magic_wall_active)
11819 int jx = local_player->jx, jy = local_player->jy;
11821 /* play the element sound at the position nearest to the player */
11822 if ((element == EL_MAGIC_WALL_FULL ||
11823 element == EL_MAGIC_WALL_ACTIVE ||
11824 element == EL_MAGIC_WALL_EMPTYING ||
11825 element == EL_BD_MAGIC_WALL_FULL ||
11826 element == EL_BD_MAGIC_WALL_ACTIVE ||
11827 element == EL_BD_MAGIC_WALL_EMPTYING ||
11828 element == EL_DC_MAGIC_WALL_FULL ||
11829 element == EL_DC_MAGIC_WALL_ACTIVE ||
11830 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11831 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11840 debug_print_timestamp(0, "- time for MAIN loop: -->");
11843 #if USE_NEW_AMOEBA_CODE
11844 /* new experimental amoeba growth stuff */
11845 if (!(FrameCounter % 8))
11847 static unsigned long random = 1684108901;
11849 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11851 x = RND(lev_fieldx);
11852 y = RND(lev_fieldy);
11853 element = Feld[x][y];
11855 if (!IS_PLAYER(x,y) &&
11856 (element == EL_EMPTY ||
11857 CAN_GROW_INTO(element) ||
11858 element == EL_QUICKSAND_EMPTY ||
11859 element == EL_QUICKSAND_FAST_EMPTY ||
11860 element == EL_ACID_SPLASH_LEFT ||
11861 element == EL_ACID_SPLASH_RIGHT))
11863 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11864 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11865 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11866 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11867 Feld[x][y] = EL_AMOEBA_DROP;
11870 random = random * 129 + 1;
11876 if (game.explosions_delayed)
11879 game.explosions_delayed = FALSE;
11881 SCAN_PLAYFIELD(x, y)
11883 element = Feld[x][y];
11885 if (ExplodeField[x][y])
11886 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11887 else if (element == EL_EXPLOSION)
11888 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11890 ExplodeField[x][y] = EX_TYPE_NONE;
11893 game.explosions_delayed = TRUE;
11896 if (game.magic_wall_active)
11898 if (!(game.magic_wall_time_left % 4))
11900 int element = Feld[magic_wall_x][magic_wall_y];
11902 if (element == EL_BD_MAGIC_WALL_FULL ||
11903 element == EL_BD_MAGIC_WALL_ACTIVE ||
11904 element == EL_BD_MAGIC_WALL_EMPTYING)
11905 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11906 else if (element == EL_DC_MAGIC_WALL_FULL ||
11907 element == EL_DC_MAGIC_WALL_ACTIVE ||
11908 element == EL_DC_MAGIC_WALL_EMPTYING)
11909 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11911 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11914 if (game.magic_wall_time_left > 0)
11916 game.magic_wall_time_left--;
11918 if (!game.magic_wall_time_left)
11920 SCAN_PLAYFIELD(x, y)
11922 element = Feld[x][y];
11924 if (element == EL_MAGIC_WALL_ACTIVE ||
11925 element == EL_MAGIC_WALL_FULL)
11927 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11928 DrawLevelField(x, y);
11930 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11931 element == EL_BD_MAGIC_WALL_FULL)
11933 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11934 DrawLevelField(x, y);
11936 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11937 element == EL_DC_MAGIC_WALL_FULL)
11939 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11940 DrawLevelField(x, y);
11944 game.magic_wall_active = FALSE;
11949 if (game.light_time_left > 0)
11951 game.light_time_left--;
11953 if (game.light_time_left == 0)
11954 RedrawAllLightSwitchesAndInvisibleElements();
11957 if (game.timegate_time_left > 0)
11959 game.timegate_time_left--;
11961 if (game.timegate_time_left == 0)
11962 CloseAllOpenTimegates();
11965 if (game.lenses_time_left > 0)
11967 game.lenses_time_left--;
11969 if (game.lenses_time_left == 0)
11970 RedrawAllInvisibleElementsForLenses();
11973 if (game.magnify_time_left > 0)
11975 game.magnify_time_left--;
11977 if (game.magnify_time_left == 0)
11978 RedrawAllInvisibleElementsForMagnifier();
11981 for (i = 0; i < MAX_PLAYERS; i++)
11983 struct PlayerInfo *player = &stored_player[i];
11985 if (SHIELD_ON(player))
11987 if (player->shield_deadly_time_left)
11988 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11989 else if (player->shield_normal_time_left)
11990 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11997 PlayAllPlayersSound();
11999 if (options.debug) /* calculate frames per second */
12001 static unsigned long fps_counter = 0;
12002 static int fps_frames = 0;
12003 unsigned long fps_delay_ms = Counter() - fps_counter;
12007 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12009 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12012 fps_counter = Counter();
12015 redraw_mask |= REDRAW_FPS;
12018 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12020 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12022 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12024 local_player->show_envelope = 0;
12028 debug_print_timestamp(0, "stop main loop profiling ");
12029 printf("----------------------------------------------------------\n");
12032 /* use random number generator in every frame to make it less predictable */
12033 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12037 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12039 int min_x = x, min_y = y, max_x = x, max_y = y;
12042 for (i = 0; i < MAX_PLAYERS; i++)
12044 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12046 if (!stored_player[i].active || &stored_player[i] == player)
12049 min_x = MIN(min_x, jx);
12050 min_y = MIN(min_y, jy);
12051 max_x = MAX(max_x, jx);
12052 max_y = MAX(max_y, jy);
12055 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12058 static boolean AllPlayersInVisibleScreen()
12062 for (i = 0; i < MAX_PLAYERS; i++)
12064 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12066 if (!stored_player[i].active)
12069 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12076 void ScrollLevel(int dx, int dy)
12079 static Bitmap *bitmap_db_field2 = NULL;
12080 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12087 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12088 /* only horizontal XOR vertical scroll direction allowed */
12089 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12094 if (bitmap_db_field2 == NULL)
12095 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12097 /* needed when blitting directly to same bitmap -- should not be needed with
12098 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12099 BlitBitmap(drawto_field, bitmap_db_field2,
12100 FX + TILEX * (dx == -1) - softscroll_offset,
12101 FY + TILEY * (dy == -1) - softscroll_offset,
12102 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12103 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12104 FX + TILEX * (dx == 1) - softscroll_offset,
12105 FY + TILEY * (dy == 1) - softscroll_offset);
12106 BlitBitmap(bitmap_db_field2, drawto_field,
12107 FX + TILEX * (dx == 1) - softscroll_offset,
12108 FY + TILEY * (dy == 1) - softscroll_offset,
12109 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12110 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12111 FX + TILEX * (dx == 1) - softscroll_offset,
12112 FY + TILEY * (dy == 1) - softscroll_offset);
12117 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12118 int xsize = (BX2 - BX1 + 1);
12119 int ysize = (BY2 - BY1 + 1);
12120 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12121 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12122 int step = (start < end ? +1 : -1);
12124 for (i = start; i != end; i += step)
12126 BlitBitmap(drawto_field, drawto_field,
12127 FX + TILEX * (dx != 0 ? i + step : 0),
12128 FY + TILEY * (dy != 0 ? i + step : 0),
12129 TILEX * (dx != 0 ? 1 : xsize),
12130 TILEY * (dy != 0 ? 1 : ysize),
12131 FX + TILEX * (dx != 0 ? i : 0),
12132 FY + TILEY * (dy != 0 ? i : 0));
12137 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12139 BlitBitmap(drawto_field, drawto_field,
12140 FX + TILEX * (dx == -1) - softscroll_offset,
12141 FY + TILEY * (dy == -1) - softscroll_offset,
12142 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12143 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12144 FX + TILEX * (dx == 1) - softscroll_offset,
12145 FY + TILEY * (dy == 1) - softscroll_offset);
12151 x = (dx == 1 ? BX1 : BX2);
12152 for (y = BY1; y <= BY2; y++)
12153 DrawScreenField(x, y);
12158 y = (dy == 1 ? BY1 : BY2);
12159 for (x = BX1; x <= BX2; x++)
12160 DrawScreenField(x, y);
12163 redraw_mask |= REDRAW_FIELD;
12166 static boolean canFallDown(struct PlayerInfo *player)
12168 int jx = player->jx, jy = player->jy;
12170 return (IN_LEV_FIELD(jx, jy + 1) &&
12171 (IS_FREE(jx, jy + 1) ||
12172 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12173 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12174 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12177 static boolean canPassField(int x, int y, int move_dir)
12179 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12180 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12181 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12182 int nextx = x + dx;
12183 int nexty = y + dy;
12184 int element = Feld[x][y];
12186 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12187 !CAN_MOVE(element) &&
12188 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12189 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12190 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12193 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12195 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12196 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12197 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12201 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12202 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12203 (IS_DIGGABLE(Feld[newx][newy]) ||
12204 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12205 canPassField(newx, newy, move_dir)));
12208 static void CheckGravityMovement(struct PlayerInfo *player)
12210 #if USE_PLAYER_GRAVITY
12211 if (player->gravity && !player->programmed_action)
12213 if (game.gravity && !player->programmed_action)
12216 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12217 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12218 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12219 int jx = player->jx, jy = player->jy;
12220 boolean player_is_moving_to_valid_field =
12221 (!player_is_snapping &&
12222 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12223 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12224 boolean player_can_fall_down = canFallDown(player);
12226 if (player_can_fall_down &&
12227 !player_is_moving_to_valid_field)
12228 player->programmed_action = MV_DOWN;
12232 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12234 return CheckGravityMovement(player);
12236 #if USE_PLAYER_GRAVITY
12237 if (player->gravity && !player->programmed_action)
12239 if (game.gravity && !player->programmed_action)
12242 int jx = player->jx, jy = player->jy;
12243 boolean field_under_player_is_free =
12244 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12245 boolean player_is_standing_on_valid_field =
12246 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12247 (IS_WALKABLE(Feld[jx][jy]) &&
12248 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12250 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12251 player->programmed_action = MV_DOWN;
12256 MovePlayerOneStep()
12257 -----------------------------------------------------------------------------
12258 dx, dy: direction (non-diagonal) to try to move the player to
12259 real_dx, real_dy: direction as read from input device (can be diagonal)
12262 boolean MovePlayerOneStep(struct PlayerInfo *player,
12263 int dx, int dy, int real_dx, int real_dy)
12265 int jx = player->jx, jy = player->jy;
12266 int new_jx = jx + dx, new_jy = jy + dy;
12267 #if !USE_FIXED_DONT_RUN_INTO
12271 boolean player_can_move = !player->cannot_move;
12273 if (!player->active || (!dx && !dy))
12274 return MP_NO_ACTION;
12276 player->MovDir = (dx < 0 ? MV_LEFT :
12277 dx > 0 ? MV_RIGHT :
12279 dy > 0 ? MV_DOWN : MV_NONE);
12281 if (!IN_LEV_FIELD(new_jx, new_jy))
12282 return MP_NO_ACTION;
12284 if (!player_can_move)
12286 if (player->MovPos == 0)
12288 player->is_moving = FALSE;
12289 player->is_digging = FALSE;
12290 player->is_collecting = FALSE;
12291 player->is_snapping = FALSE;
12292 player->is_pushing = FALSE;
12297 if (!options.network && game.centered_player_nr == -1 &&
12298 !AllPlayersInSight(player, new_jx, new_jy))
12299 return MP_NO_ACTION;
12301 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12302 return MP_NO_ACTION;
12305 #if !USE_FIXED_DONT_RUN_INTO
12306 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12308 /* (moved to DigField()) */
12309 if (player_can_move && DONT_RUN_INTO(element))
12311 if (element == EL_ACID && dx == 0 && dy == 1)
12313 SplashAcid(new_jx, new_jy);
12314 Feld[jx][jy] = EL_PLAYER_1;
12315 InitMovingField(jx, jy, MV_DOWN);
12316 Store[jx][jy] = EL_ACID;
12317 ContinueMoving(jx, jy);
12318 BuryPlayer(player);
12321 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12327 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12328 if (can_move != MP_MOVING)
12331 /* check if DigField() has caused relocation of the player */
12332 if (player->jx != jx || player->jy != jy)
12333 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12335 StorePlayer[jx][jy] = 0;
12336 player->last_jx = jx;
12337 player->last_jy = jy;
12338 player->jx = new_jx;
12339 player->jy = new_jy;
12340 StorePlayer[new_jx][new_jy] = player->element_nr;
12342 if (player->move_delay_value_next != -1)
12344 player->move_delay_value = player->move_delay_value_next;
12345 player->move_delay_value_next = -1;
12349 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12351 player->step_counter++;
12353 PlayerVisit[jx][jy] = FrameCounter;
12355 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12356 player->is_moving = TRUE;
12360 /* should better be called in MovePlayer(), but this breaks some tapes */
12361 ScrollPlayer(player, SCROLL_INIT);
12367 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12369 int jx = player->jx, jy = player->jy;
12370 int old_jx = jx, old_jy = jy;
12371 int moved = MP_NO_ACTION;
12373 if (!player->active)
12378 if (player->MovPos == 0)
12380 player->is_moving = FALSE;
12381 player->is_digging = FALSE;
12382 player->is_collecting = FALSE;
12383 player->is_snapping = FALSE;
12384 player->is_pushing = FALSE;
12390 if (player->move_delay > 0)
12393 player->move_delay = -1; /* set to "uninitialized" value */
12395 /* store if player is automatically moved to next field */
12396 player->is_auto_moving = (player->programmed_action != MV_NONE);
12398 /* remove the last programmed player action */
12399 player->programmed_action = 0;
12401 if (player->MovPos)
12403 /* should only happen if pre-1.2 tape recordings are played */
12404 /* this is only for backward compatibility */
12406 int original_move_delay_value = player->move_delay_value;
12409 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12413 /* scroll remaining steps with finest movement resolution */
12414 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12416 while (player->MovPos)
12418 ScrollPlayer(player, SCROLL_GO_ON);
12419 ScrollScreen(NULL, SCROLL_GO_ON);
12421 AdvanceFrameAndPlayerCounters(player->index_nr);
12427 player->move_delay_value = original_move_delay_value;
12430 player->is_active = FALSE;
12432 if (player->last_move_dir & MV_HORIZONTAL)
12434 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12435 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12439 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12440 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12443 #if USE_FIXED_BORDER_RUNNING_GFX
12444 if (!moved && !player->is_active)
12446 player->is_moving = FALSE;
12447 player->is_digging = FALSE;
12448 player->is_collecting = FALSE;
12449 player->is_snapping = FALSE;
12450 player->is_pushing = FALSE;
12458 if (moved & MP_MOVING && !ScreenMovPos &&
12459 (player->index_nr == game.centered_player_nr ||
12460 game.centered_player_nr == -1))
12462 if (moved & MP_MOVING && !ScreenMovPos &&
12463 (player == local_player || !options.network))
12466 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12467 int offset = game.scroll_delay_value;
12469 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12471 /* actual player has left the screen -- scroll in that direction */
12472 if (jx != old_jx) /* player has moved horizontally */
12473 scroll_x += (jx - old_jx);
12474 else /* player has moved vertically */
12475 scroll_y += (jy - old_jy);
12479 if (jx != old_jx) /* player has moved horizontally */
12481 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12482 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12483 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12485 /* don't scroll over playfield boundaries */
12486 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12487 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12489 /* don't scroll more than one field at a time */
12490 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12492 /* don't scroll against the player's moving direction */
12493 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12494 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12495 scroll_x = old_scroll_x;
12497 else /* player has moved vertically */
12499 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12500 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12501 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12503 /* don't scroll over playfield boundaries */
12504 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12505 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12507 /* don't scroll more than one field at a time */
12508 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12510 /* don't scroll against the player's moving direction */
12511 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12512 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12513 scroll_y = old_scroll_y;
12517 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12520 if (!options.network && game.centered_player_nr == -1 &&
12521 !AllPlayersInVisibleScreen())
12523 scroll_x = old_scroll_x;
12524 scroll_y = old_scroll_y;
12528 if (!options.network && !AllPlayersInVisibleScreen())
12530 scroll_x = old_scroll_x;
12531 scroll_y = old_scroll_y;
12536 ScrollScreen(player, SCROLL_INIT);
12537 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12542 player->StepFrame = 0;
12544 if (moved & MP_MOVING)
12546 if (old_jx != jx && old_jy == jy)
12547 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12548 else if (old_jx == jx && old_jy != jy)
12549 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12551 DrawLevelField(jx, jy); /* for "crumbled sand" */
12553 player->last_move_dir = player->MovDir;
12554 player->is_moving = TRUE;
12555 player->is_snapping = FALSE;
12556 player->is_switching = FALSE;
12557 player->is_dropping = FALSE;
12558 player->is_dropping_pressed = FALSE;
12559 player->drop_pressed_delay = 0;
12562 /* should better be called here than above, but this breaks some tapes */
12563 ScrollPlayer(player, SCROLL_INIT);
12568 CheckGravityMovementWhenNotMoving(player);
12570 player->is_moving = FALSE;
12572 /* at this point, the player is allowed to move, but cannot move right now
12573 (e.g. because of something blocking the way) -- ensure that the player
12574 is also allowed to move in the next frame (in old versions before 3.1.1,
12575 the player was forced to wait again for eight frames before next try) */
12577 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12578 player->move_delay = 0; /* allow direct movement in the next frame */
12581 if (player->move_delay == -1) /* not yet initialized by DigField() */
12582 player->move_delay = player->move_delay_value;
12584 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12586 TestIfPlayerTouchesBadThing(jx, jy);
12587 TestIfPlayerTouchesCustomElement(jx, jy);
12590 if (!player->active)
12591 RemovePlayer(player);
12596 void ScrollPlayer(struct PlayerInfo *player, int mode)
12598 int jx = player->jx, jy = player->jy;
12599 int last_jx = player->last_jx, last_jy = player->last_jy;
12600 int move_stepsize = TILEX / player->move_delay_value;
12602 #if USE_NEW_PLAYER_SPEED
12603 if (!player->active)
12606 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12609 if (!player->active || player->MovPos == 0)
12613 if (mode == SCROLL_INIT)
12615 player->actual_frame_counter = FrameCounter;
12616 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12618 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12619 Feld[last_jx][last_jy] == EL_EMPTY)
12621 int last_field_block_delay = 0; /* start with no blocking at all */
12622 int block_delay_adjustment = player->block_delay_adjustment;
12624 /* if player blocks last field, add delay for exactly one move */
12625 if (player->block_last_field)
12627 last_field_block_delay += player->move_delay_value;
12629 /* when blocking enabled, prevent moving up despite gravity */
12630 #if USE_PLAYER_GRAVITY
12631 if (player->gravity && player->MovDir == MV_UP)
12632 block_delay_adjustment = -1;
12634 if (game.gravity && player->MovDir == MV_UP)
12635 block_delay_adjustment = -1;
12639 /* add block delay adjustment (also possible when not blocking) */
12640 last_field_block_delay += block_delay_adjustment;
12642 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12643 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12646 #if USE_NEW_PLAYER_SPEED
12647 if (player->MovPos != 0) /* player has not yet reached destination */
12653 else if (!FrameReached(&player->actual_frame_counter, 1))
12656 #if USE_NEW_PLAYER_SPEED
12657 if (player->MovPos != 0)
12659 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12660 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12662 /* before DrawPlayer() to draw correct player graphic for this case */
12663 if (player->MovPos == 0)
12664 CheckGravityMovement(player);
12667 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12668 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12670 /* before DrawPlayer() to draw correct player graphic for this case */
12671 if (player->MovPos == 0)
12672 CheckGravityMovement(player);
12675 if (player->MovPos == 0) /* player reached destination field */
12677 if (player->move_delay_reset_counter > 0)
12679 player->move_delay_reset_counter--;
12681 if (player->move_delay_reset_counter == 0)
12683 /* continue with normal speed after quickly moving through gate */
12684 HALVE_PLAYER_SPEED(player);
12686 /* be able to make the next move without delay */
12687 player->move_delay = 0;
12691 player->last_jx = jx;
12692 player->last_jy = jy;
12694 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12695 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12696 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12697 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12698 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12699 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12701 DrawPlayer(player); /* needed here only to cleanup last field */
12702 RemovePlayer(player);
12704 if (local_player->friends_still_needed == 0 ||
12705 IS_SP_ELEMENT(Feld[jx][jy]))
12706 PlayerWins(player);
12709 /* this breaks one level: "machine", level 000 */
12711 int move_direction = player->MovDir;
12712 int enter_side = MV_DIR_OPPOSITE(move_direction);
12713 int leave_side = move_direction;
12714 int old_jx = last_jx;
12715 int old_jy = last_jy;
12716 int old_element = Feld[old_jx][old_jy];
12717 int new_element = Feld[jx][jy];
12719 if (IS_CUSTOM_ELEMENT(old_element))
12720 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12722 player->index_bit, leave_side);
12724 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12725 CE_PLAYER_LEAVES_X,
12726 player->index_bit, leave_side);
12728 if (IS_CUSTOM_ELEMENT(new_element))
12729 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12730 player->index_bit, enter_side);
12732 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12733 CE_PLAYER_ENTERS_X,
12734 player->index_bit, enter_side);
12736 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12737 CE_MOVE_OF_X, move_direction);
12740 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12742 TestIfPlayerTouchesBadThing(jx, jy);
12743 TestIfPlayerTouchesCustomElement(jx, jy);
12745 /* needed because pushed element has not yet reached its destination,
12746 so it would trigger a change event at its previous field location */
12747 if (!player->is_pushing)
12748 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12750 if (!player->active)
12751 RemovePlayer(player);
12754 if (!local_player->LevelSolved && level.use_step_counter)
12764 if (TimeLeft <= 10 && setup.time_limit)
12765 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12768 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12770 DisplayGameControlValues();
12772 DrawGameValue_Time(TimeLeft);
12775 if (!TimeLeft && setup.time_limit)
12776 for (i = 0; i < MAX_PLAYERS; i++)
12777 KillPlayer(&stored_player[i]);
12780 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12782 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12784 DisplayGameControlValues();
12787 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12788 DrawGameValue_Time(TimePlayed);
12792 if (tape.single_step && tape.recording && !tape.pausing &&
12793 !player->programmed_action)
12794 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12798 void ScrollScreen(struct PlayerInfo *player, int mode)
12800 static unsigned long screen_frame_counter = 0;
12802 if (mode == SCROLL_INIT)
12804 /* set scrolling step size according to actual player's moving speed */
12805 ScrollStepSize = TILEX / player->move_delay_value;
12807 screen_frame_counter = FrameCounter;
12808 ScreenMovDir = player->MovDir;
12809 ScreenMovPos = player->MovPos;
12810 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12813 else if (!FrameReached(&screen_frame_counter, 1))
12818 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12819 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12820 redraw_mask |= REDRAW_FIELD;
12823 ScreenMovDir = MV_NONE;
12826 void TestIfPlayerTouchesCustomElement(int x, int y)
12828 static int xy[4][2] =
12835 static int trigger_sides[4][2] =
12837 /* center side border side */
12838 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12839 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12840 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12841 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12843 static int touch_dir[4] =
12845 MV_LEFT | MV_RIGHT,
12850 int center_element = Feld[x][y]; /* should always be non-moving! */
12853 for (i = 0; i < NUM_DIRECTIONS; i++)
12855 int xx = x + xy[i][0];
12856 int yy = y + xy[i][1];
12857 int center_side = trigger_sides[i][0];
12858 int border_side = trigger_sides[i][1];
12859 int border_element;
12861 if (!IN_LEV_FIELD(xx, yy))
12864 if (IS_PLAYER(x, y))
12866 struct PlayerInfo *player = PLAYERINFO(x, y);
12868 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12869 border_element = Feld[xx][yy]; /* may be moving! */
12870 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12871 border_element = Feld[xx][yy];
12872 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12873 border_element = MovingOrBlocked2Element(xx, yy);
12875 continue; /* center and border element do not touch */
12877 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12878 player->index_bit, border_side);
12879 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12880 CE_PLAYER_TOUCHES_X,
12881 player->index_bit, border_side);
12883 else if (IS_PLAYER(xx, yy))
12885 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12887 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12889 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12890 continue; /* center and border element do not touch */
12893 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12894 player->index_bit, center_side);
12895 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12896 CE_PLAYER_TOUCHES_X,
12897 player->index_bit, center_side);
12903 #if USE_ELEMENT_TOUCHING_BUGFIX
12905 void TestIfElementTouchesCustomElement(int x, int y)
12907 static int xy[4][2] =
12914 static int trigger_sides[4][2] =
12916 /* center side border side */
12917 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12918 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12919 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12920 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12922 static int touch_dir[4] =
12924 MV_LEFT | MV_RIGHT,
12929 boolean change_center_element = FALSE;
12930 int center_element = Feld[x][y]; /* should always be non-moving! */
12931 int border_element_old[NUM_DIRECTIONS];
12934 for (i = 0; i < NUM_DIRECTIONS; i++)
12936 int xx = x + xy[i][0];
12937 int yy = y + xy[i][1];
12938 int border_element;
12940 border_element_old[i] = -1;
12942 if (!IN_LEV_FIELD(xx, yy))
12945 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12946 border_element = Feld[xx][yy]; /* may be moving! */
12947 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12948 border_element = Feld[xx][yy];
12949 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12950 border_element = MovingOrBlocked2Element(xx, yy);
12952 continue; /* center and border element do not touch */
12954 border_element_old[i] = border_element;
12957 for (i = 0; i < NUM_DIRECTIONS; i++)
12959 int xx = x + xy[i][0];
12960 int yy = y + xy[i][1];
12961 int center_side = trigger_sides[i][0];
12962 int border_element = border_element_old[i];
12964 if (border_element == -1)
12967 /* check for change of border element */
12968 CheckElementChangeBySide(xx, yy, border_element, center_element,
12969 CE_TOUCHING_X, center_side);
12972 for (i = 0; i < NUM_DIRECTIONS; i++)
12974 int border_side = trigger_sides[i][1];
12975 int border_element = border_element_old[i];
12977 if (border_element == -1)
12980 /* check for change of center element (but change it only once) */
12981 if (!change_center_element)
12982 change_center_element =
12983 CheckElementChangeBySide(x, y, center_element, border_element,
12984 CE_TOUCHING_X, border_side);
12990 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12992 static int xy[4][2] =
12999 static int trigger_sides[4][2] =
13001 /* center side border side */
13002 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13003 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13004 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13005 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13007 static int touch_dir[4] =
13009 MV_LEFT | MV_RIGHT,
13014 boolean change_center_element = FALSE;
13015 int center_element = Feld[x][y]; /* should always be non-moving! */
13018 for (i = 0; i < NUM_DIRECTIONS; i++)
13020 int xx = x + xy[i][0];
13021 int yy = y + xy[i][1];
13022 int center_side = trigger_sides[i][0];
13023 int border_side = trigger_sides[i][1];
13024 int border_element;
13026 if (!IN_LEV_FIELD(xx, yy))
13029 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13030 border_element = Feld[xx][yy]; /* may be moving! */
13031 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13032 border_element = Feld[xx][yy];
13033 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13034 border_element = MovingOrBlocked2Element(xx, yy);
13036 continue; /* center and border element do not touch */
13038 /* check for change of center element (but change it only once) */
13039 if (!change_center_element)
13040 change_center_element =
13041 CheckElementChangeBySide(x, y, center_element, border_element,
13042 CE_TOUCHING_X, border_side);
13044 /* check for change of border element */
13045 CheckElementChangeBySide(xx, yy, border_element, center_element,
13046 CE_TOUCHING_X, center_side);
13052 void TestIfElementHitsCustomElement(int x, int y, int direction)
13054 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13055 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13056 int hitx = x + dx, hity = y + dy;
13057 int hitting_element = Feld[x][y];
13058 int touched_element;
13060 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13063 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13064 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13066 if (IN_LEV_FIELD(hitx, hity))
13068 int opposite_direction = MV_DIR_OPPOSITE(direction);
13069 int hitting_side = direction;
13070 int touched_side = opposite_direction;
13071 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13072 MovDir[hitx][hity] != direction ||
13073 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13079 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13080 CE_HITTING_X, touched_side);
13082 CheckElementChangeBySide(hitx, hity, touched_element,
13083 hitting_element, CE_HIT_BY_X, hitting_side);
13085 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13086 CE_HIT_BY_SOMETHING, opposite_direction);
13090 /* "hitting something" is also true when hitting the playfield border */
13091 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13092 CE_HITTING_SOMETHING, direction);
13096 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13098 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13099 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13100 int hitx = x + dx, hity = y + dy;
13101 int hitting_element = Feld[x][y];
13102 int touched_element;
13104 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13105 !IS_FREE(hitx, hity) &&
13106 (!IS_MOVING(hitx, hity) ||
13107 MovDir[hitx][hity] != direction ||
13108 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13111 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13115 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13119 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13120 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13122 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13123 EP_CAN_SMASH_EVERYTHING, direction);
13125 if (IN_LEV_FIELD(hitx, hity))
13127 int opposite_direction = MV_DIR_OPPOSITE(direction);
13128 int hitting_side = direction;
13129 int touched_side = opposite_direction;
13131 int touched_element = MovingOrBlocked2Element(hitx, hity);
13134 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13135 MovDir[hitx][hity] != direction ||
13136 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13145 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13146 CE_SMASHED_BY_SOMETHING, opposite_direction);
13148 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13149 CE_OTHER_IS_SMASHING, touched_side);
13151 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13152 CE_OTHER_GETS_SMASHED, hitting_side);
13158 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13160 int i, kill_x = -1, kill_y = -1;
13162 int bad_element = -1;
13163 static int test_xy[4][2] =
13170 static int test_dir[4] =
13178 for (i = 0; i < NUM_DIRECTIONS; i++)
13180 int test_x, test_y, test_move_dir, test_element;
13182 test_x = good_x + test_xy[i][0];
13183 test_y = good_y + test_xy[i][1];
13185 if (!IN_LEV_FIELD(test_x, test_y))
13189 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13191 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13193 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13194 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13196 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13197 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13201 bad_element = test_element;
13207 if (kill_x != -1 || kill_y != -1)
13209 if (IS_PLAYER(good_x, good_y))
13211 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13213 if (player->shield_deadly_time_left > 0 &&
13214 !IS_INDESTRUCTIBLE(bad_element))
13215 Bang(kill_x, kill_y);
13216 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13217 KillPlayer(player);
13220 Bang(good_x, good_y);
13224 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13226 int i, kill_x = -1, kill_y = -1;
13227 int bad_element = Feld[bad_x][bad_y];
13228 static int test_xy[4][2] =
13235 static int touch_dir[4] =
13237 MV_LEFT | MV_RIGHT,
13242 static int test_dir[4] =
13250 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13253 for (i = 0; i < NUM_DIRECTIONS; i++)
13255 int test_x, test_y, test_move_dir, test_element;
13257 test_x = bad_x + test_xy[i][0];
13258 test_y = bad_y + test_xy[i][1];
13259 if (!IN_LEV_FIELD(test_x, test_y))
13263 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13265 test_element = Feld[test_x][test_y];
13267 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13268 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13270 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13271 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13273 /* good thing is player or penguin that does not move away */
13274 if (IS_PLAYER(test_x, test_y))
13276 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13278 if (bad_element == EL_ROBOT && player->is_moving)
13279 continue; /* robot does not kill player if he is moving */
13281 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13283 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13284 continue; /* center and border element do not touch */
13291 else if (test_element == EL_PENGUIN)
13300 if (kill_x != -1 || kill_y != -1)
13302 if (IS_PLAYER(kill_x, kill_y))
13304 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13306 if (player->shield_deadly_time_left > 0 &&
13307 !IS_INDESTRUCTIBLE(bad_element))
13308 Bang(bad_x, bad_y);
13309 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13310 KillPlayer(player);
13313 Bang(kill_x, kill_y);
13317 void TestIfPlayerTouchesBadThing(int x, int y)
13319 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13322 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13324 TestIfGoodThingHitsBadThing(x, y, move_dir);
13327 void TestIfBadThingTouchesPlayer(int x, int y)
13329 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13332 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13334 TestIfBadThingHitsGoodThing(x, y, move_dir);
13337 void TestIfFriendTouchesBadThing(int x, int y)
13339 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13342 void TestIfBadThingTouchesFriend(int x, int y)
13344 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13347 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13349 int i, kill_x = bad_x, kill_y = bad_y;
13350 static int xy[4][2] =
13358 for (i = 0; i < NUM_DIRECTIONS; i++)
13362 x = bad_x + xy[i][0];
13363 y = bad_y + xy[i][1];
13364 if (!IN_LEV_FIELD(x, y))
13367 element = Feld[x][y];
13368 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13369 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13377 if (kill_x != bad_x || kill_y != bad_y)
13378 Bang(bad_x, bad_y);
13381 void KillPlayer(struct PlayerInfo *player)
13383 int jx = player->jx, jy = player->jy;
13385 if (!player->active)
13388 /* the following code was introduced to prevent an infinite loop when calling
13390 -> CheckTriggeredElementChangeExt()
13391 -> ExecuteCustomElementAction()
13393 -> (infinitely repeating the above sequence of function calls)
13394 which occurs when killing the player while having a CE with the setting
13395 "kill player X when explosion of <player X>"; the solution using a new
13396 field "player->killed" was chosen for backwards compatibility, although
13397 clever use of the fields "player->active" etc. would probably also work */
13399 if (player->killed)
13403 player->killed = TRUE;
13405 /* remove accessible field at the player's position */
13406 Feld[jx][jy] = EL_EMPTY;
13408 /* deactivate shield (else Bang()/Explode() would not work right) */
13409 player->shield_normal_time_left = 0;
13410 player->shield_deadly_time_left = 0;
13413 BuryPlayer(player);
13416 static void KillPlayerUnlessEnemyProtected(int x, int y)
13418 if (!PLAYER_ENEMY_PROTECTED(x, y))
13419 KillPlayer(PLAYERINFO(x, y));
13422 static void KillPlayerUnlessExplosionProtected(int x, int y)
13424 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13425 KillPlayer(PLAYERINFO(x, y));
13428 void BuryPlayer(struct PlayerInfo *player)
13430 int jx = player->jx, jy = player->jy;
13432 if (!player->active)
13435 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13436 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13438 player->GameOver = TRUE;
13439 RemovePlayer(player);
13442 void RemovePlayer(struct PlayerInfo *player)
13444 int jx = player->jx, jy = player->jy;
13445 int i, found = FALSE;
13447 player->present = FALSE;
13448 player->active = FALSE;
13450 if (!ExplodeField[jx][jy])
13451 StorePlayer[jx][jy] = 0;
13453 if (player->is_moving)
13454 DrawLevelField(player->last_jx, player->last_jy);
13456 for (i = 0; i < MAX_PLAYERS; i++)
13457 if (stored_player[i].active)
13461 AllPlayersGone = TRUE;
13467 #if USE_NEW_SNAP_DELAY
13468 static void setFieldForSnapping(int x, int y, int element, int direction)
13470 struct ElementInfo *ei = &element_info[element];
13471 int direction_bit = MV_DIR_TO_BIT(direction);
13472 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13473 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13474 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13476 Feld[x][y] = EL_ELEMENT_SNAPPING;
13477 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13479 ResetGfxAnimation(x, y);
13481 GfxElement[x][y] = element;
13482 GfxAction[x][y] = action;
13483 GfxDir[x][y] = direction;
13484 GfxFrame[x][y] = -1;
13489 =============================================================================
13490 checkDiagonalPushing()
13491 -----------------------------------------------------------------------------
13492 check if diagonal input device direction results in pushing of object
13493 (by checking if the alternative direction is walkable, diggable, ...)
13494 =============================================================================
13497 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13498 int x, int y, int real_dx, int real_dy)
13500 int jx, jy, dx, dy, xx, yy;
13502 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13505 /* diagonal direction: check alternative direction */
13510 xx = jx + (dx == 0 ? real_dx : 0);
13511 yy = jy + (dy == 0 ? real_dy : 0);
13513 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13517 =============================================================================
13519 -----------------------------------------------------------------------------
13520 x, y: field next to player (non-diagonal) to try to dig to
13521 real_dx, real_dy: direction as read from input device (can be diagonal)
13522 =============================================================================
13525 int DigField(struct PlayerInfo *player,
13526 int oldx, int oldy, int x, int y,
13527 int real_dx, int real_dy, int mode)
13529 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13530 boolean player_was_pushing = player->is_pushing;
13531 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13532 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13533 int jx = oldx, jy = oldy;
13534 int dx = x - jx, dy = y - jy;
13535 int nextx = x + dx, nexty = y + dy;
13536 int move_direction = (dx == -1 ? MV_LEFT :
13537 dx == +1 ? MV_RIGHT :
13539 dy == +1 ? MV_DOWN : MV_NONE);
13540 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13541 int dig_side = MV_DIR_OPPOSITE(move_direction);
13542 int old_element = Feld[jx][jy];
13543 #if USE_FIXED_DONT_RUN_INTO
13544 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13550 if (is_player) /* function can also be called by EL_PENGUIN */
13552 if (player->MovPos == 0)
13554 player->is_digging = FALSE;
13555 player->is_collecting = FALSE;
13558 if (player->MovPos == 0) /* last pushing move finished */
13559 player->is_pushing = FALSE;
13561 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13563 player->is_switching = FALSE;
13564 player->push_delay = -1;
13566 return MP_NO_ACTION;
13570 #if !USE_FIXED_DONT_RUN_INTO
13571 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13572 return MP_NO_ACTION;
13575 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13576 old_element = Back[jx][jy];
13578 /* in case of element dropped at player position, check background */
13579 else if (Back[jx][jy] != EL_EMPTY &&
13580 game.engine_version >= VERSION_IDENT(2,2,0,0))
13581 old_element = Back[jx][jy];
13583 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13584 return MP_NO_ACTION; /* field has no opening in this direction */
13586 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13587 return MP_NO_ACTION; /* field has no opening in this direction */
13589 #if USE_FIXED_DONT_RUN_INTO
13590 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13594 Feld[jx][jy] = player->artwork_element;
13595 InitMovingField(jx, jy, MV_DOWN);
13596 Store[jx][jy] = EL_ACID;
13597 ContinueMoving(jx, jy);
13598 BuryPlayer(player);
13600 return MP_DONT_RUN_INTO;
13604 #if USE_FIXED_DONT_RUN_INTO
13605 if (player_can_move && DONT_RUN_INTO(element))
13607 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13609 return MP_DONT_RUN_INTO;
13613 #if USE_FIXED_DONT_RUN_INTO
13614 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13615 return MP_NO_ACTION;
13618 #if !USE_FIXED_DONT_RUN_INTO
13619 element = Feld[x][y];
13622 collect_count = element_info[element].collect_count_initial;
13624 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13625 return MP_NO_ACTION;
13627 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13628 player_can_move = player_can_move_or_snap;
13630 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13631 game.engine_version >= VERSION_IDENT(2,2,0,0))
13633 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13634 player->index_bit, dig_side);
13635 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13636 player->index_bit, dig_side);
13638 if (element == EL_DC_LANDMINE)
13641 if (Feld[x][y] != element) /* field changed by snapping */
13644 return MP_NO_ACTION;
13647 #if USE_PLAYER_GRAVITY
13648 if (player->gravity && is_player && !player->is_auto_moving &&
13649 canFallDown(player) && move_direction != MV_DOWN &&
13650 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13651 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13653 if (game.gravity && is_player && !player->is_auto_moving &&
13654 canFallDown(player) && move_direction != MV_DOWN &&
13655 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13656 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13659 if (player_can_move &&
13660 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13662 int sound_element = SND_ELEMENT(element);
13663 int sound_action = ACTION_WALKING;
13665 if (IS_RND_GATE(element))
13667 if (!player->key[RND_GATE_NR(element)])
13668 return MP_NO_ACTION;
13670 else if (IS_RND_GATE_GRAY(element))
13672 if (!player->key[RND_GATE_GRAY_NR(element)])
13673 return MP_NO_ACTION;
13675 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13677 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13678 return MP_NO_ACTION;
13680 else if (element == EL_EXIT_OPEN ||
13681 element == EL_EM_EXIT_OPEN ||
13682 element == EL_STEEL_EXIT_OPEN ||
13683 element == EL_EM_STEEL_EXIT_OPEN ||
13684 element == EL_SP_EXIT_OPEN ||
13685 element == EL_SP_EXIT_OPENING)
13687 sound_action = ACTION_PASSING; /* player is passing exit */
13689 else if (element == EL_EMPTY)
13691 sound_action = ACTION_MOVING; /* nothing to walk on */
13694 /* play sound from background or player, whatever is available */
13695 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13696 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13698 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13700 else if (player_can_move &&
13701 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13703 if (!ACCESS_FROM(element, opposite_direction))
13704 return MP_NO_ACTION; /* field not accessible from this direction */
13706 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13707 return MP_NO_ACTION;
13709 if (IS_EM_GATE(element))
13711 if (!player->key[EM_GATE_NR(element)])
13712 return MP_NO_ACTION;
13714 else if (IS_EM_GATE_GRAY(element))
13716 if (!player->key[EM_GATE_GRAY_NR(element)])
13717 return MP_NO_ACTION;
13719 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13721 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13722 return MP_NO_ACTION;
13724 else if (IS_EMC_GATE(element))
13726 if (!player->key[EMC_GATE_NR(element)])
13727 return MP_NO_ACTION;
13729 else if (IS_EMC_GATE_GRAY(element))
13731 if (!player->key[EMC_GATE_GRAY_NR(element)])
13732 return MP_NO_ACTION;
13734 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13736 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13737 return MP_NO_ACTION;
13739 else if (element == EL_DC_GATE_WHITE ||
13740 element == EL_DC_GATE_WHITE_GRAY ||
13741 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13743 if (player->num_white_keys == 0)
13744 return MP_NO_ACTION;
13746 player->num_white_keys--;
13748 else if (IS_SP_PORT(element))
13750 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13751 element == EL_SP_GRAVITY_PORT_RIGHT ||
13752 element == EL_SP_GRAVITY_PORT_UP ||
13753 element == EL_SP_GRAVITY_PORT_DOWN)
13754 #if USE_PLAYER_GRAVITY
13755 player->gravity = !player->gravity;
13757 game.gravity = !game.gravity;
13759 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13760 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13761 element == EL_SP_GRAVITY_ON_PORT_UP ||
13762 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13763 #if USE_PLAYER_GRAVITY
13764 player->gravity = TRUE;
13766 game.gravity = TRUE;
13768 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13769 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13770 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13771 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13772 #if USE_PLAYER_GRAVITY
13773 player->gravity = FALSE;
13775 game.gravity = FALSE;
13779 /* automatically move to the next field with double speed */
13780 player->programmed_action = move_direction;
13782 if (player->move_delay_reset_counter == 0)
13784 player->move_delay_reset_counter = 2; /* two double speed steps */
13786 DOUBLE_PLAYER_SPEED(player);
13789 PlayLevelSoundAction(x, y, ACTION_PASSING);
13791 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13795 if (mode != DF_SNAP)
13797 GfxElement[x][y] = GFX_ELEMENT(element);
13798 player->is_digging = TRUE;
13801 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13803 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13804 player->index_bit, dig_side);
13806 if (mode == DF_SNAP)
13808 #if USE_NEW_SNAP_DELAY
13809 if (level.block_snap_field)
13810 setFieldForSnapping(x, y, element, move_direction);
13812 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13814 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13817 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13818 player->index_bit, dig_side);
13821 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13825 if (is_player && mode != DF_SNAP)
13827 GfxElement[x][y] = element;
13828 player->is_collecting = TRUE;
13831 if (element == EL_SPEED_PILL)
13833 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13835 else if (element == EL_EXTRA_TIME && level.time > 0)
13837 TimeLeft += level.extra_time;
13840 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13842 DisplayGameControlValues();
13844 DrawGameValue_Time(TimeLeft);
13847 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13849 player->shield_normal_time_left += level.shield_normal_time;
13850 if (element == EL_SHIELD_DEADLY)
13851 player->shield_deadly_time_left += level.shield_deadly_time;
13853 else if (element == EL_DYNAMITE ||
13854 element == EL_EM_DYNAMITE ||
13855 element == EL_SP_DISK_RED)
13857 if (player->inventory_size < MAX_INVENTORY_SIZE)
13858 player->inventory_element[player->inventory_size++] = element;
13860 DrawGameDoorValues();
13862 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13864 player->dynabomb_count++;
13865 player->dynabombs_left++;
13867 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13869 player->dynabomb_size++;
13871 else if (element == EL_DYNABOMB_INCREASE_POWER)
13873 player->dynabomb_xl = TRUE;
13875 else if (IS_KEY(element))
13877 player->key[KEY_NR(element)] = TRUE;
13879 DrawGameDoorValues();
13881 else if (element == EL_DC_KEY_WHITE)
13883 player->num_white_keys++;
13885 /* display white keys? */
13886 /* DrawGameDoorValues(); */
13888 else if (IS_ENVELOPE(element))
13890 player->show_envelope = element;
13892 else if (element == EL_EMC_LENSES)
13894 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13896 RedrawAllInvisibleElementsForLenses();
13898 else if (element == EL_EMC_MAGNIFIER)
13900 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13902 RedrawAllInvisibleElementsForMagnifier();
13904 else if (IS_DROPPABLE(element) ||
13905 IS_THROWABLE(element)) /* can be collected and dropped */
13909 if (collect_count == 0)
13910 player->inventory_infinite_element = element;
13912 for (i = 0; i < collect_count; i++)
13913 if (player->inventory_size < MAX_INVENTORY_SIZE)
13914 player->inventory_element[player->inventory_size++] = element;
13916 DrawGameDoorValues();
13918 else if (collect_count > 0)
13920 local_player->gems_still_needed -= collect_count;
13921 if (local_player->gems_still_needed < 0)
13922 local_player->gems_still_needed = 0;
13925 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13927 DisplayGameControlValues();
13929 DrawGameValue_Emeralds(local_player->gems_still_needed);
13933 RaiseScoreElement(element);
13934 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13937 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13938 player->index_bit, dig_side);
13940 if (mode == DF_SNAP)
13942 #if USE_NEW_SNAP_DELAY
13943 if (level.block_snap_field)
13944 setFieldForSnapping(x, y, element, move_direction);
13946 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13948 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13951 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13952 player->index_bit, dig_side);
13955 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13957 if (mode == DF_SNAP && element != EL_BD_ROCK)
13958 return MP_NO_ACTION;
13960 if (CAN_FALL(element) && dy)
13961 return MP_NO_ACTION;
13963 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13964 !(element == EL_SPRING && level.use_spring_bug))
13965 return MP_NO_ACTION;
13967 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13968 ((move_direction & MV_VERTICAL &&
13969 ((element_info[element].move_pattern & MV_LEFT &&
13970 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13971 (element_info[element].move_pattern & MV_RIGHT &&
13972 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13973 (move_direction & MV_HORIZONTAL &&
13974 ((element_info[element].move_pattern & MV_UP &&
13975 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13976 (element_info[element].move_pattern & MV_DOWN &&
13977 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13978 return MP_NO_ACTION;
13980 /* do not push elements already moving away faster than player */
13981 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13982 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13983 return MP_NO_ACTION;
13985 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13987 if (player->push_delay_value == -1 || !player_was_pushing)
13988 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13990 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13992 if (player->push_delay_value == -1)
13993 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13995 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13997 if (!player->is_pushing)
13998 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14001 player->is_pushing = TRUE;
14002 player->is_active = TRUE;
14004 if (!(IN_LEV_FIELD(nextx, nexty) &&
14005 (IS_FREE(nextx, nexty) ||
14006 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14007 IS_SB_ELEMENT(element)))))
14008 return MP_NO_ACTION;
14010 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14011 return MP_NO_ACTION;
14013 if (player->push_delay == -1) /* new pushing; restart delay */
14014 player->push_delay = 0;
14016 if (player->push_delay < player->push_delay_value &&
14017 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14018 element != EL_SPRING && element != EL_BALLOON)
14020 /* make sure that there is no move delay before next try to push */
14021 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14022 player->move_delay = 0;
14024 return MP_NO_ACTION;
14027 if (IS_SB_ELEMENT(element))
14029 if (element == EL_SOKOBAN_FIELD_FULL)
14031 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14032 local_player->sokobanfields_still_needed++;
14035 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14037 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14038 local_player->sokobanfields_still_needed--;
14041 Feld[x][y] = EL_SOKOBAN_OBJECT;
14043 if (Back[x][y] == Back[nextx][nexty])
14044 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14045 else if (Back[x][y] != 0)
14046 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14049 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14052 if (local_player->sokobanfields_still_needed == 0 &&
14053 game.emulation == EMU_SOKOBAN)
14055 PlayerWins(player);
14057 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14061 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14063 InitMovingField(x, y, move_direction);
14064 GfxAction[x][y] = ACTION_PUSHING;
14066 if (mode == DF_SNAP)
14067 ContinueMoving(x, y);
14069 MovPos[x][y] = (dx != 0 ? dx : dy);
14071 Pushed[x][y] = TRUE;
14072 Pushed[nextx][nexty] = TRUE;
14074 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14075 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14077 player->push_delay_value = -1; /* get new value later */
14079 /* check for element change _after_ element has been pushed */
14080 if (game.use_change_when_pushing_bug)
14082 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14083 player->index_bit, dig_side);
14084 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14085 player->index_bit, dig_side);
14088 else if (IS_SWITCHABLE(element))
14090 if (PLAYER_SWITCHING(player, x, y))
14092 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14093 player->index_bit, dig_side);
14098 player->is_switching = TRUE;
14099 player->switch_x = x;
14100 player->switch_y = y;
14102 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14104 if (element == EL_ROBOT_WHEEL)
14106 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14110 game.robot_wheel_active = TRUE;
14112 DrawLevelField(x, y);
14114 else if (element == EL_SP_TERMINAL)
14118 SCAN_PLAYFIELD(xx, yy)
14120 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14122 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14123 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14126 else if (IS_BELT_SWITCH(element))
14128 ToggleBeltSwitch(x, y);
14130 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14131 element == EL_SWITCHGATE_SWITCH_DOWN ||
14132 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14133 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14135 ToggleSwitchgateSwitch(x, y);
14137 else if (element == EL_LIGHT_SWITCH ||
14138 element == EL_LIGHT_SWITCH_ACTIVE)
14140 ToggleLightSwitch(x, y);
14142 else if (element == EL_TIMEGATE_SWITCH ||
14143 element == EL_DC_TIMEGATE_SWITCH)
14145 ActivateTimegateSwitch(x, y);
14147 else if (element == EL_BALLOON_SWITCH_LEFT ||
14148 element == EL_BALLOON_SWITCH_RIGHT ||
14149 element == EL_BALLOON_SWITCH_UP ||
14150 element == EL_BALLOON_SWITCH_DOWN ||
14151 element == EL_BALLOON_SWITCH_NONE ||
14152 element == EL_BALLOON_SWITCH_ANY)
14154 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14155 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14156 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14157 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14158 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14161 else if (element == EL_LAMP)
14163 Feld[x][y] = EL_LAMP_ACTIVE;
14164 local_player->lights_still_needed--;
14166 ResetGfxAnimation(x, y);
14167 DrawLevelField(x, y);
14169 else if (element == EL_TIME_ORB_FULL)
14171 Feld[x][y] = EL_TIME_ORB_EMPTY;
14173 if (level.time > 0 || level.use_time_orb_bug)
14175 TimeLeft += level.time_orb_time;
14178 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14180 DisplayGameControlValues();
14182 DrawGameValue_Time(TimeLeft);
14186 ResetGfxAnimation(x, y);
14187 DrawLevelField(x, y);
14189 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14190 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14194 game.ball_state = !game.ball_state;
14196 SCAN_PLAYFIELD(xx, yy)
14198 int e = Feld[xx][yy];
14200 if (game.ball_state)
14202 if (e == EL_EMC_MAGIC_BALL)
14203 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14204 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14205 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14209 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14210 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14211 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14212 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14217 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14218 player->index_bit, dig_side);
14220 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14221 player->index_bit, dig_side);
14223 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14224 player->index_bit, dig_side);
14230 if (!PLAYER_SWITCHING(player, x, y))
14232 player->is_switching = TRUE;
14233 player->switch_x = x;
14234 player->switch_y = y;
14236 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14237 player->index_bit, dig_side);
14238 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14239 player->index_bit, dig_side);
14241 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14242 player->index_bit, dig_side);
14243 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14244 player->index_bit, dig_side);
14247 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14248 player->index_bit, dig_side);
14249 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14250 player->index_bit, dig_side);
14252 return MP_NO_ACTION;
14255 player->push_delay = -1;
14257 if (is_player) /* function can also be called by EL_PENGUIN */
14259 if (Feld[x][y] != element) /* really digged/collected something */
14261 player->is_collecting = !player->is_digging;
14262 player->is_active = TRUE;
14269 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14271 int jx = player->jx, jy = player->jy;
14272 int x = jx + dx, y = jy + dy;
14273 int snap_direction = (dx == -1 ? MV_LEFT :
14274 dx == +1 ? MV_RIGHT :
14276 dy == +1 ? MV_DOWN : MV_NONE);
14277 boolean can_continue_snapping = (level.continuous_snapping &&
14278 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14280 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14283 if (!player->active || !IN_LEV_FIELD(x, y))
14291 if (player->MovPos == 0)
14292 player->is_pushing = FALSE;
14294 player->is_snapping = FALSE;
14296 if (player->MovPos == 0)
14298 player->is_moving = FALSE;
14299 player->is_digging = FALSE;
14300 player->is_collecting = FALSE;
14306 #if USE_NEW_CONTINUOUS_SNAPPING
14307 /* prevent snapping with already pressed snap key when not allowed */
14308 if (player->is_snapping && !can_continue_snapping)
14311 if (player->is_snapping)
14315 player->MovDir = snap_direction;
14317 if (player->MovPos == 0)
14319 player->is_moving = FALSE;
14320 player->is_digging = FALSE;
14321 player->is_collecting = FALSE;
14324 player->is_dropping = FALSE;
14325 player->is_dropping_pressed = FALSE;
14326 player->drop_pressed_delay = 0;
14328 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14331 player->is_snapping = TRUE;
14332 player->is_active = TRUE;
14334 if (player->MovPos == 0)
14336 player->is_moving = FALSE;
14337 player->is_digging = FALSE;
14338 player->is_collecting = FALSE;
14341 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14342 DrawLevelField(player->last_jx, player->last_jy);
14344 DrawLevelField(x, y);
14349 boolean DropElement(struct PlayerInfo *player)
14351 int old_element, new_element;
14352 int dropx = player->jx, dropy = player->jy;
14353 int drop_direction = player->MovDir;
14354 int drop_side = drop_direction;
14356 int drop_element = get_next_dropped_element(player);
14358 int drop_element = (player->inventory_size > 0 ?
14359 player->inventory_element[player->inventory_size - 1] :
14360 player->inventory_infinite_element != EL_UNDEFINED ?
14361 player->inventory_infinite_element :
14362 player->dynabombs_left > 0 ?
14363 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14367 player->is_dropping_pressed = TRUE;
14369 /* do not drop an element on top of another element; when holding drop key
14370 pressed without moving, dropped element must move away before the next
14371 element can be dropped (this is especially important if the next element
14372 is dynamite, which can be placed on background for historical reasons) */
14373 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14376 if (IS_THROWABLE(drop_element))
14378 dropx += GET_DX_FROM_DIR(drop_direction);
14379 dropy += GET_DY_FROM_DIR(drop_direction);
14381 if (!IN_LEV_FIELD(dropx, dropy))
14385 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14386 new_element = drop_element; /* default: no change when dropping */
14388 /* check if player is active, not moving and ready to drop */
14389 if (!player->active || player->MovPos || player->drop_delay > 0)
14392 /* check if player has anything that can be dropped */
14393 if (new_element == EL_UNDEFINED)
14396 /* check if drop key was pressed long enough for EM style dynamite */
14397 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14400 /* check if anything can be dropped at the current position */
14401 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14404 /* collected custom elements can only be dropped on empty fields */
14405 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14408 if (old_element != EL_EMPTY)
14409 Back[dropx][dropy] = old_element; /* store old element on this field */
14411 ResetGfxAnimation(dropx, dropy);
14412 ResetRandomAnimationValue(dropx, dropy);
14414 if (player->inventory_size > 0 ||
14415 player->inventory_infinite_element != EL_UNDEFINED)
14417 if (player->inventory_size > 0)
14419 player->inventory_size--;
14421 DrawGameDoorValues();
14423 if (new_element == EL_DYNAMITE)
14424 new_element = EL_DYNAMITE_ACTIVE;
14425 else if (new_element == EL_EM_DYNAMITE)
14426 new_element = EL_EM_DYNAMITE_ACTIVE;
14427 else if (new_element == EL_SP_DISK_RED)
14428 new_element = EL_SP_DISK_RED_ACTIVE;
14431 Feld[dropx][dropy] = new_element;
14433 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14434 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14435 el2img(Feld[dropx][dropy]), 0);
14437 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14439 /* needed if previous element just changed to "empty" in the last frame */
14440 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14442 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14443 player->index_bit, drop_side);
14444 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14446 player->index_bit, drop_side);
14448 TestIfElementTouchesCustomElement(dropx, dropy);
14450 else /* player is dropping a dyna bomb */
14452 player->dynabombs_left--;
14454 Feld[dropx][dropy] = new_element;
14456 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14457 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14458 el2img(Feld[dropx][dropy]), 0);
14460 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14463 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14464 InitField_WithBug1(dropx, dropy, FALSE);
14466 new_element = Feld[dropx][dropy]; /* element might have changed */
14468 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14469 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14471 int move_direction, nextx, nexty;
14473 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14474 MovDir[dropx][dropy] = drop_direction;
14476 move_direction = MovDir[dropx][dropy];
14477 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14478 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14480 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14482 #if USE_FIX_IMPACT_COLLISION
14483 /* do not cause impact style collision by dropping elements that can fall */
14484 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14486 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14490 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14491 player->is_dropping = TRUE;
14493 player->drop_pressed_delay = 0;
14494 player->is_dropping_pressed = FALSE;
14496 player->drop_x = dropx;
14497 player->drop_y = dropy;
14502 /* ------------------------------------------------------------------------- */
14503 /* game sound playing functions */
14504 /* ------------------------------------------------------------------------- */
14506 static int *loop_sound_frame = NULL;
14507 static int *loop_sound_volume = NULL;
14509 void InitPlayLevelSound()
14511 int num_sounds = getSoundListSize();
14513 checked_free(loop_sound_frame);
14514 checked_free(loop_sound_volume);
14516 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14517 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14520 static void PlayLevelSound(int x, int y, int nr)
14522 int sx = SCREENX(x), sy = SCREENY(y);
14523 int volume, stereo_position;
14524 int max_distance = 8;
14525 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14527 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14528 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14531 if (!IN_LEV_FIELD(x, y) ||
14532 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14533 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14536 volume = SOUND_MAX_VOLUME;
14538 if (!IN_SCR_FIELD(sx, sy))
14540 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14541 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14543 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14546 stereo_position = (SOUND_MAX_LEFT +
14547 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14548 (SCR_FIELDX + 2 * max_distance));
14550 if (IS_LOOP_SOUND(nr))
14552 /* This assures that quieter loop sounds do not overwrite louder ones,
14553 while restarting sound volume comparison with each new game frame. */
14555 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14558 loop_sound_volume[nr] = volume;
14559 loop_sound_frame[nr] = FrameCounter;
14562 PlaySoundExt(nr, volume, stereo_position, type);
14565 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14567 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14568 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14569 y < LEVELY(BY1) ? LEVELY(BY1) :
14570 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14574 static void PlayLevelSoundAction(int x, int y, int action)
14576 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14579 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14581 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14583 if (sound_effect != SND_UNDEFINED)
14584 PlayLevelSound(x, y, sound_effect);
14587 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14590 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14592 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14593 PlayLevelSound(x, y, sound_effect);
14596 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14598 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14600 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14601 PlayLevelSound(x, y, sound_effect);
14604 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14606 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14608 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14609 StopSound(sound_effect);
14612 static void PlayLevelMusic()
14614 if (levelset.music[level_nr] != MUS_UNDEFINED)
14615 PlayMusic(levelset.music[level_nr]); /* from config file */
14617 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14620 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14622 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14623 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14624 int x = xx - 1 - offset;
14625 int y = yy - 1 - offset;
14630 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14634 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14638 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14642 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14646 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14650 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14654 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14657 case SAMPLE_android_clone:
14658 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14661 case SAMPLE_android_move:
14662 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14665 case SAMPLE_spring:
14666 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14670 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14674 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14677 case SAMPLE_eater_eat:
14678 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14682 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14685 case SAMPLE_collect:
14686 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14689 case SAMPLE_diamond:
14690 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14693 case SAMPLE_squash:
14694 /* !!! CHECK THIS !!! */
14696 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14698 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14702 case SAMPLE_wonderfall:
14703 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14707 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14711 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14715 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14719 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14723 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14727 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14730 case SAMPLE_wonder:
14731 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14735 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14738 case SAMPLE_exit_open:
14739 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14742 case SAMPLE_exit_leave:
14743 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14746 case SAMPLE_dynamite:
14747 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14751 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14755 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14759 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14763 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14767 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14771 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14775 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14781 void ChangeTime(int value)
14783 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14787 /* EMC game engine uses value from time counter of RND game engine */
14788 level.native_em_level->lev->time = *time;
14790 DrawGameValue_Time(*time);
14793 void RaiseScore(int value)
14795 /* EMC game engine and RND game engine have separate score counters */
14796 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14797 &level.native_em_level->lev->score : &local_player->score);
14801 DrawGameValue_Score(*score);
14805 void RaiseScore(int value)
14807 local_player->score += value;
14810 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14812 DisplayGameControlValues();
14814 DrawGameValue_Score(local_player->score);
14818 void RaiseScoreElement(int element)
14823 case EL_BD_DIAMOND:
14824 case EL_EMERALD_YELLOW:
14825 case EL_EMERALD_RED:
14826 case EL_EMERALD_PURPLE:
14827 case EL_SP_INFOTRON:
14828 RaiseScore(level.score[SC_EMERALD]);
14831 RaiseScore(level.score[SC_DIAMOND]);
14834 RaiseScore(level.score[SC_CRYSTAL]);
14837 RaiseScore(level.score[SC_PEARL]);
14840 case EL_BD_BUTTERFLY:
14841 case EL_SP_ELECTRON:
14842 RaiseScore(level.score[SC_BUG]);
14845 case EL_BD_FIREFLY:
14846 case EL_SP_SNIKSNAK:
14847 RaiseScore(level.score[SC_SPACESHIP]);
14850 case EL_DARK_YAMYAM:
14851 RaiseScore(level.score[SC_YAMYAM]);
14854 RaiseScore(level.score[SC_ROBOT]);
14857 RaiseScore(level.score[SC_PACMAN]);
14860 RaiseScore(level.score[SC_NUT]);
14863 case EL_EM_DYNAMITE:
14864 case EL_SP_DISK_RED:
14865 case EL_DYNABOMB_INCREASE_NUMBER:
14866 case EL_DYNABOMB_INCREASE_SIZE:
14867 case EL_DYNABOMB_INCREASE_POWER:
14868 RaiseScore(level.score[SC_DYNAMITE]);
14870 case EL_SHIELD_NORMAL:
14871 case EL_SHIELD_DEADLY:
14872 RaiseScore(level.score[SC_SHIELD]);
14874 case EL_EXTRA_TIME:
14875 RaiseScore(level.extra_time_score);
14889 case EL_DC_KEY_WHITE:
14890 RaiseScore(level.score[SC_KEY]);
14893 RaiseScore(element_info[element].collect_score);
14898 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14900 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14902 #if defined(NETWORK_AVALIABLE)
14903 if (options.network)
14904 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14913 FadeSkipNextFadeIn();
14915 fading = fading_none;
14919 OpenDoor(DOOR_CLOSE_1);
14922 game_status = GAME_MODE_MAIN;
14925 DrawAndFadeInMainMenu(REDRAW_FIELD);
14933 FadeOut(REDRAW_FIELD);
14936 game_status = GAME_MODE_MAIN;
14938 DrawAndFadeInMainMenu(REDRAW_FIELD);
14942 else /* continue playing the game */
14944 if (tape.playing && tape.deactivate_display)
14945 TapeDeactivateDisplayOff(TRUE);
14947 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14949 if (tape.playing && tape.deactivate_display)
14950 TapeDeactivateDisplayOn();
14954 void RequestQuitGame(boolean ask_if_really_quit)
14956 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14957 boolean skip_request = AllPlayersGone || quick_quit;
14959 RequestQuitGameExt(skip_request, quick_quit,
14960 "Do you really want to quit the game ?");
14964 /* ------------------------------------------------------------------------- */
14965 /* random generator functions */
14966 /* ------------------------------------------------------------------------- */
14968 unsigned int InitEngineRandom_RND(long seed)
14970 game.num_random_calls = 0;
14973 unsigned int rnd_seed = InitEngineRandom(seed);
14975 printf("::: START RND: %d\n", rnd_seed);
14980 return InitEngineRandom(seed);
14986 unsigned int RND(int max)
14990 game.num_random_calls++;
14992 return GetEngineRandom(max);
14999 /* ------------------------------------------------------------------------- */
15000 /* game engine snapshot handling functions */
15001 /* ------------------------------------------------------------------------- */
15003 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15005 struct EngineSnapshotInfo
15007 /* runtime values for custom element collect score */
15008 int collect_score[NUM_CUSTOM_ELEMENTS];
15010 /* runtime values for group element choice position */
15011 int choice_pos[NUM_GROUP_ELEMENTS];
15013 /* runtime values for belt position animations */
15014 int belt_graphic[4 * NUM_BELT_PARTS];
15015 int belt_anim_mode[4 * NUM_BELT_PARTS];
15018 struct EngineSnapshotNodeInfo
15025 static struct EngineSnapshotInfo engine_snapshot_rnd;
15026 static ListNode *engine_snapshot_list = NULL;
15027 static char *snapshot_level_identifier = NULL;
15028 static int snapshot_level_nr = -1;
15030 void FreeEngineSnapshot()
15032 while (engine_snapshot_list != NULL)
15033 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15036 setString(&snapshot_level_identifier, NULL);
15037 snapshot_level_nr = -1;
15040 static void SaveEngineSnapshotValues_RND()
15042 static int belt_base_active_element[4] =
15044 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15045 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15046 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15047 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15051 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15053 int element = EL_CUSTOM_START + i;
15055 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15058 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15060 int element = EL_GROUP_START + i;
15062 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15065 for (i = 0; i < 4; i++)
15067 for (j = 0; j < NUM_BELT_PARTS; j++)
15069 int element = belt_base_active_element[i] + j;
15070 int graphic = el2img(element);
15071 int anim_mode = graphic_info[graphic].anim_mode;
15073 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15074 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15079 static void LoadEngineSnapshotValues_RND()
15081 unsigned long num_random_calls = game.num_random_calls;
15084 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15086 int element = EL_CUSTOM_START + i;
15088 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15091 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15093 int element = EL_GROUP_START + i;
15095 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15098 for (i = 0; i < 4; i++)
15100 for (j = 0; j < NUM_BELT_PARTS; j++)
15102 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15103 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15105 graphic_info[graphic].anim_mode = anim_mode;
15109 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15111 InitRND(tape.random_seed);
15112 for (i = 0; i < num_random_calls; i++)
15116 if (game.num_random_calls != num_random_calls)
15118 Error(ERR_INFO, "number of random calls out of sync");
15119 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15120 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15121 Error(ERR_EXIT, "this should not happen -- please debug");
15125 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15127 struct EngineSnapshotNodeInfo *bi =
15128 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15130 bi->buffer_orig = buffer;
15131 bi->buffer_copy = checked_malloc(size);
15134 memcpy(bi->buffer_copy, buffer, size);
15136 addNodeToList(&engine_snapshot_list, NULL, bi);
15139 void SaveEngineSnapshot()
15141 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15143 if (level_editor_test_game) /* do not save snapshots from editor */
15146 /* copy some special values to a structure better suited for the snapshot */
15148 SaveEngineSnapshotValues_RND();
15149 SaveEngineSnapshotValues_EM();
15151 /* save values stored in special snapshot structure */
15153 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15154 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15156 /* save further RND engine values */
15158 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15159 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15160 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15162 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15163 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15164 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15165 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15167 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15168 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15169 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15170 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15171 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15173 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15174 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15175 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15177 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15179 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15181 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15182 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15184 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15185 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15186 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15187 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15188 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15189 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15190 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15191 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15192 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15193 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15194 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15195 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15196 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15197 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15198 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15199 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15200 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15201 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15203 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15204 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15206 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15207 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15208 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15210 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15211 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15213 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15214 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15215 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15216 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15217 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15219 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15220 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15222 /* save level identification information */
15224 setString(&snapshot_level_identifier, leveldir_current->identifier);
15225 snapshot_level_nr = level_nr;
15228 ListNode *node = engine_snapshot_list;
15231 while (node != NULL)
15233 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15238 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15242 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15244 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15247 void LoadEngineSnapshot()
15249 ListNode *node = engine_snapshot_list;
15251 if (engine_snapshot_list == NULL)
15254 while (node != NULL)
15256 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15261 /* restore special values from snapshot structure */
15263 LoadEngineSnapshotValues_RND();
15264 LoadEngineSnapshotValues_EM();
15267 boolean CheckEngineSnapshot()
15269 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15270 snapshot_level_nr == level_nr);
15274 /* ---------- new game button stuff ---------------------------------------- */
15276 /* graphic position values for game buttons */
15277 #define GAME_BUTTON_XSIZE 30
15278 #define GAME_BUTTON_YSIZE 30
15279 #define GAME_BUTTON_XPOS 5
15280 #define GAME_BUTTON_YPOS 215
15281 #define SOUND_BUTTON_XPOS 5
15282 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15284 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15285 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15286 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15287 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15288 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15289 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15297 } gamebutton_info[NUM_GAME_BUTTONS] =
15301 &game.button.stop.x, &game.button.stop.y,
15302 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15307 &game.button.pause.x, &game.button.pause.y,
15308 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15309 GAME_CTRL_ID_PAUSE,
15313 &game.button.play.x, &game.button.play.y,
15314 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15319 &game.button.sound_music.x, &game.button.sound_music.y,
15320 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15321 SOUND_CTRL_ID_MUSIC,
15322 "background music on/off"
15325 &game.button.sound_loops.x, &game.button.sound_loops.y,
15326 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15327 SOUND_CTRL_ID_LOOPS,
15328 "sound loops on/off"
15331 &game.button.sound_simple.x,&game.button.sound_simple.y,
15332 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15333 SOUND_CTRL_ID_SIMPLE,
15334 "normal sounds on/off"
15338 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15343 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15344 GAME_CTRL_ID_PAUSE,
15348 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15353 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15354 SOUND_CTRL_ID_MUSIC,
15355 "background music on/off"
15358 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15359 SOUND_CTRL_ID_LOOPS,
15360 "sound loops on/off"
15363 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15364 SOUND_CTRL_ID_SIMPLE,
15365 "normal sounds on/off"
15370 void CreateGameButtons()
15374 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15376 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15377 struct GadgetInfo *gi;
15380 unsigned long event_mask;
15382 int gd_xoffset, gd_yoffset;
15383 int gd_x1, gd_x2, gd_y1, gd_y2;
15386 x = DX + *gamebutton_info[i].x;
15387 y = DY + *gamebutton_info[i].y;
15388 gd_xoffset = gamebutton_info[i].gd_x;
15389 gd_yoffset = gamebutton_info[i].gd_y;
15390 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15391 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15393 if (id == GAME_CTRL_ID_STOP ||
15394 id == GAME_CTRL_ID_PAUSE ||
15395 id == GAME_CTRL_ID_PLAY)
15397 button_type = GD_TYPE_NORMAL_BUTTON;
15399 event_mask = GD_EVENT_RELEASED;
15400 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15401 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15405 button_type = GD_TYPE_CHECK_BUTTON;
15407 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15408 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15409 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15410 event_mask = GD_EVENT_PRESSED;
15411 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15412 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15415 gi = CreateGadget(GDI_CUSTOM_ID, id,
15416 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15421 GDI_X, DX + gd_xoffset,
15422 GDI_Y, DY + gd_yoffset,
15424 GDI_WIDTH, GAME_BUTTON_XSIZE,
15425 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15426 GDI_TYPE, button_type,
15427 GDI_STATE, GD_BUTTON_UNPRESSED,
15428 GDI_CHECKED, checked,
15429 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15430 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15431 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15432 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15433 GDI_EVENT_MASK, event_mask,
15434 GDI_CALLBACK_ACTION, HandleGameButtons,
15438 Error(ERR_EXIT, "cannot create gadget");
15440 game_gadget[id] = gi;
15444 void FreeGameButtons()
15448 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15449 FreeGadget(game_gadget[i]);
15452 static void MapGameButtons()
15456 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15457 MapGadget(game_gadget[i]);
15460 void UnmapGameButtons()
15464 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15465 UnmapGadget(game_gadget[i]);
15468 static void HandleGameButtons(struct GadgetInfo *gi)
15470 int id = gi->custom_id;
15472 if (game_status != GAME_MODE_PLAYING)
15477 case GAME_CTRL_ID_STOP:
15481 RequestQuitGame(TRUE);
15484 case GAME_CTRL_ID_PAUSE:
15485 if (options.network)
15487 #if defined(NETWORK_AVALIABLE)
15489 SendToServer_ContinuePlaying();
15491 SendToServer_PausePlaying();
15495 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15498 case GAME_CTRL_ID_PLAY:
15501 #if defined(NETWORK_AVALIABLE)
15502 if (options.network)
15503 SendToServer_ContinuePlaying();
15507 tape.pausing = FALSE;
15508 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15513 case SOUND_CTRL_ID_MUSIC:
15514 if (setup.sound_music)
15516 setup.sound_music = FALSE;
15519 else if (audio.music_available)
15521 setup.sound = setup.sound_music = TRUE;
15523 SetAudioMode(setup.sound);
15529 case SOUND_CTRL_ID_LOOPS:
15530 if (setup.sound_loops)
15531 setup.sound_loops = FALSE;
15532 else if (audio.loops_available)
15534 setup.sound = setup.sound_loops = TRUE;
15535 SetAudioMode(setup.sound);
15539 case SOUND_CTRL_ID_SIMPLE:
15540 if (setup.sound_simple)
15541 setup.sound_simple = FALSE;
15542 else if (audio.sound_available)
15544 setup.sound = setup.sound_simple = TRUE;
15545 SetAudioMode(setup.sound);