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_CONTROL_LEVEL_NUMBER 0
135 #define GAME_CONTROL_GEMS 1
136 #define GAME_CONTROL_INVENTORY_COUNT 2
137 #define GAME_CONTROL_INVENTORY_FIRST_1 3
138 #define GAME_CONTROL_INVENTORY_FIRST_2 4
139 #define GAME_CONTROL_INVENTORY_FIRST_3 5
140 #define GAME_CONTROL_INVENTORY_FIRST_4 6
141 #define GAME_CONTROL_INVENTORY_FIRST_5 7
142 #define GAME_CONTROL_INVENTORY_FIRST_6 8
143 #define GAME_CONTROL_INVENTORY_FIRST_7 9
144 #define GAME_CONTROL_INVENTORY_FIRST_8 10
145 #define GAME_CONTROL_INVENTORY_LAST_1 11
146 #define GAME_CONTROL_INVENTORY_LAST_2 12
147 #define GAME_CONTROL_INVENTORY_LAST_3 13
148 #define GAME_CONTROL_INVENTORY_LAST_4 14
149 #define GAME_CONTROL_INVENTORY_LAST_5 15
150 #define GAME_CONTROL_INVENTORY_LAST_6 16
151 #define GAME_CONTROL_INVENTORY_LAST_7 17
152 #define GAME_CONTROL_INVENTORY_LAST_8 18
153 #define GAME_CONTROL_KEY_1 19
154 #define GAME_CONTROL_KEY_2 20
155 #define GAME_CONTROL_KEY_3 21
156 #define GAME_CONTROL_KEY_4 22
157 #define GAME_CONTROL_KEY_5 23
158 #define GAME_CONTROL_KEY_6 24
159 #define GAME_CONTROL_KEY_7 25
160 #define GAME_CONTROL_KEY_8 26
161 #define GAME_CONTROL_KEY_WHITE 27
162 #define GAME_CONTROL_KEY_WHITE_COUNT 28
163 #define GAME_CONTROL_SCORE 29
164 #define GAME_CONTROL_TIME 30
165 #define GAME_CONTROL_TIME_HH 31
166 #define GAME_CONTROL_TIME_MM 32
167 #define GAME_CONTROL_TIME_SS 33
168 #define GAME_CONTROL_SHIELD_NORMAL 34
169 #define GAME_CONTROL_SHIELD_NORMAL_TIME 35
170 #define GAME_CONTROL_SHIELD_DEADLY 36
171 #define GAME_CONTROL_SHIELD_DEADLY_TIME 37
172 #define GAME_CONTROL_EXIT 38
173 #define GAME_CONTROL_EMC_MAGIC_BALL 39
174 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH 40
175 #define GAME_CONTROL_LIGHT_SWITCH 41
176 #define GAME_CONTROL_LIGHT_SWITCH_TIME 42
177 #define GAME_CONTROL_TIMEGATE_SWITCH 43
178 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_CONTROL_SWITCHGATE_SWITCH 45
180 #define GAME_CONTROL_EMC_LENSES 46
181 #define GAME_CONTROL_EMC_LENSES_TIME 47
182 #define GAME_CONTROL_EMC_MAGNIFIER 48
183 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 49
184 #define GAME_CONTROL_BALLOON_SWITCH 50
185 #define GAME_CONTROL_DYNABOMB_NUMBER 51
186 #define GAME_CONTROL_DYNABOMB_SIZE 52
187 #define GAME_CONTROL_DYNABOMB_POWER 53
188 #define GAME_CONTROL_PENGUINS 54
189 #define GAME_CONTROL_SOKOBAN_OBJECTS 55
190 #define GAME_CONTROL_SOKOBAN_FIELDS 56
191 #define GAME_CONTROL_ROBOT_WHEEL 57
192 #define GAME_CONTROL_CONVEYOR_BELT_1 58
193 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 59
194 #define GAME_CONTROL_CONVEYOR_BELT_2 60
195 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 61
196 #define GAME_CONTROL_CONVEYOR_BELT_3 62
197 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 63
198 #define GAME_CONTROL_CONVEYOR_BELT_4 64
199 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 65
200 #define GAME_CONTROL_MAGIC_WALL 66
201 #define GAME_CONTROL_MAGIC_WALL_TIME 67
202 #define GAME_CONTROL_GRAVITY_STATE 68
203 #define GAME_CONTROL_PLAYER_NAME 69
204 #define GAME_CONTROL_LEVEL_NAME 70
205 #define GAME_CONTROL_LEVEL_AUTHOR 71
207 #define NUM_GAME_CONTROLS 72
209 int game_control_value[NUM_GAME_CONTROLS];
210 int last_game_control_value[NUM_GAME_CONTROLS];
212 struct GameControlInfo
216 struct TextPosInfo *pos;
220 static struct GameControlInfo game_controls[] =
223 GAME_CONTROL_LEVEL_NUMBER,
224 &game.panel.level_number,
233 GAME_CONTROL_INVENTORY_COUNT,
234 &game.panel.inventory_count,
238 GAME_CONTROL_INVENTORY_FIRST_1,
239 &game.panel.inventory_first_1,
243 GAME_CONTROL_INVENTORY_FIRST_2,
244 &game.panel.inventory_first_2,
248 GAME_CONTROL_INVENTORY_FIRST_3,
249 &game.panel.inventory_first_3,
253 GAME_CONTROL_INVENTORY_FIRST_4,
254 &game.panel.inventory_first_4,
258 GAME_CONTROL_INVENTORY_FIRST_5,
259 &game.panel.inventory_first_5,
263 GAME_CONTROL_INVENTORY_FIRST_6,
264 &game.panel.inventory_first_6,
268 GAME_CONTROL_INVENTORY_FIRST_7,
269 &game.panel.inventory_first_7,
273 GAME_CONTROL_INVENTORY_FIRST_8,
274 &game.panel.inventory_first_8,
278 GAME_CONTROL_INVENTORY_LAST_1,
279 &game.panel.inventory_last_1,
283 GAME_CONTROL_INVENTORY_LAST_2,
284 &game.panel.inventory_last_2,
288 GAME_CONTROL_INVENTORY_LAST_3,
289 &game.panel.inventory_last_3,
293 GAME_CONTROL_INVENTORY_LAST_4,
294 &game.panel.inventory_last_4,
298 GAME_CONTROL_INVENTORY_LAST_5,
299 &game.panel.inventory_last_5,
303 GAME_CONTROL_INVENTORY_LAST_6,
304 &game.panel.inventory_last_6,
308 GAME_CONTROL_INVENTORY_LAST_7,
309 &game.panel.inventory_last_7,
313 GAME_CONTROL_INVENTORY_LAST_8,
314 &game.panel.inventory_last_8,
358 GAME_CONTROL_KEY_WHITE,
359 &game.panel.key_white,
363 GAME_CONTROL_KEY_WHITE_COUNT,
364 &game.panel.key_white_count,
378 GAME_CONTROL_TIME_HH,
383 GAME_CONTROL_TIME_MM,
388 GAME_CONTROL_TIME_SS,
393 GAME_CONTROL_SHIELD_NORMAL,
394 &game.panel.shield_normal,
398 GAME_CONTROL_SHIELD_NORMAL_TIME,
399 &game.panel.shield_normal_time,
403 GAME_CONTROL_SHIELD_DEADLY,
404 &game.panel.shield_deadly,
408 GAME_CONTROL_SHIELD_DEADLY_TIME,
409 &game.panel.shield_deadly_time,
418 GAME_CONTROL_EMC_MAGIC_BALL,
419 &game.panel.emc_magic_ball,
423 GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
424 &game.panel.emc_magic_ball_switch,
428 GAME_CONTROL_LIGHT_SWITCH,
429 &game.panel.light_switch,
433 GAME_CONTROL_LIGHT_SWITCH_TIME,
434 &game.panel.light_switch_time,
438 GAME_CONTROL_TIMEGATE_SWITCH,
439 &game.panel.timegate_switch,
443 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
444 &game.panel.timegate_switch_time,
448 GAME_CONTROL_SWITCHGATE_SWITCH,
449 &game.panel.switchgate_switch,
453 GAME_CONTROL_EMC_LENSES,
454 &game.panel.emc_lenses,
458 GAME_CONTROL_EMC_LENSES_TIME,
459 &game.panel.emc_lenses_time,
463 GAME_CONTROL_EMC_MAGNIFIER,
464 &game.panel.emc_magnifier,
468 GAME_CONTROL_EMC_MAGNIFIER_TIME,
469 &game.panel.emc_magnifier_time,
473 GAME_CONTROL_BALLOON_SWITCH,
474 &game.panel.balloon_switch,
478 GAME_CONTROL_DYNABOMB_NUMBER,
479 &game.panel.dynabomb_number,
483 GAME_CONTROL_DYNABOMB_SIZE,
484 &game.panel.dynabomb_size,
488 GAME_CONTROL_DYNABOMB_POWER,
489 &game.panel.dynabomb_power,
493 GAME_CONTROL_PENGUINS,
494 &game.panel.penguins,
498 GAME_CONTROL_SOKOBAN_OBJECTS,
499 &game.panel.sokoban_objects,
503 GAME_CONTROL_SOKOBAN_FIELDS,
504 &game.panel.sokoban_fields,
508 GAME_CONTROL_ROBOT_WHEEL,
509 &game.panel.robot_wheel,
513 GAME_CONTROL_CONVEYOR_BELT_1,
514 &game.panel.conveyor_belt_1,
518 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
519 &game.panel.conveyor_belt_1_switch,
523 GAME_CONTROL_CONVEYOR_BELT_2,
524 &game.panel.conveyor_belt_2,
528 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
529 &game.panel.conveyor_belt_2_switch,
533 GAME_CONTROL_CONVEYOR_BELT_3,
534 &game.panel.conveyor_belt_3,
538 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
539 &game.panel.conveyor_belt_3_switch,
543 GAME_CONTROL_CONVEYOR_BELT_4,
544 &game.panel.conveyor_belt_4,
548 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
549 &game.panel.conveyor_belt_4_switch,
553 GAME_CONTROL_MAGIC_WALL,
554 &game.panel.magic_wall,
558 GAME_CONTROL_MAGIC_WALL_TIME,
559 &game.panel.magic_wall_time,
563 GAME_CONTROL_GRAVITY_STATE,
564 &game.panel.gravity_state,
568 GAME_CONTROL_PLAYER_NAME,
569 &game.panel.player_name,
573 GAME_CONTROL_LEVEL_NAME,
574 &game.panel.level_name,
578 GAME_CONTROL_LEVEL_AUTHOR,
579 &game.panel.level_author,
592 /* values for delayed check of falling and moving elements and for collision */
593 #define CHECK_DELAY_MOVING 3
594 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
595 #define CHECK_DELAY_COLLISION 2
596 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
598 /* values for initial player move delay (initial delay counter value) */
599 #define INITIAL_MOVE_DELAY_OFF -1
600 #define INITIAL_MOVE_DELAY_ON 0
602 /* values for player movement speed (which is in fact a delay value) */
603 #define MOVE_DELAY_MIN_SPEED 32
604 #define MOVE_DELAY_NORMAL_SPEED 8
605 #define MOVE_DELAY_HIGH_SPEED 4
606 #define MOVE_DELAY_MAX_SPEED 1
608 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
609 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
611 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
612 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
614 /* values for other actions */
615 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
616 #define MOVE_STEPSIZE_MIN (1)
617 #define MOVE_STEPSIZE_MAX (TILEX)
619 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
620 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
622 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
624 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
625 RND(element_info[e].push_delay_random))
626 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
627 RND(element_info[e].drop_delay_random))
628 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
629 RND(element_info[e].move_delay_random))
630 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
631 (element_info[e].move_delay_random))
632 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
633 RND(element_info[e].ce_value_random_initial))
634 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
635 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
636 RND((c)->delay_random * (c)->delay_frames))
637 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
638 RND((c)->delay_random))
641 #define GET_VALID_RUNTIME_ELEMENT(e) \
642 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
644 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
645 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
646 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
647 (be) + (e) - EL_SELF)
649 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
650 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
651 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
652 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
653 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
654 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
655 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
656 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
657 RESOLVED_REFERENCE_ELEMENT(be, e) : \
660 #define CAN_GROW_INTO(e) \
661 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
663 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
664 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
667 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
668 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
669 (CAN_MOVE_INTO_ACID(e) && \
670 Feld[x][y] == EL_ACID) || \
673 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
674 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
675 (CAN_MOVE_INTO_ACID(e) && \
676 Feld[x][y] == EL_ACID) || \
679 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
680 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
682 (CAN_MOVE_INTO_ACID(e) && \
683 Feld[x][y] == EL_ACID) || \
684 (DONT_COLLIDE_WITH(e) && \
686 !PLAYER_ENEMY_PROTECTED(x, y))))
688 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
689 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
691 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
692 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
694 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
695 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
697 #define ANDROID_CAN_CLONE_FIELD(x, y) \
698 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
699 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
701 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
702 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
704 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
705 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
707 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
708 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
710 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
711 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
713 #define PIG_CAN_ENTER_FIELD(e, x, y) \
714 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
716 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
717 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
718 Feld[x][y] == EL_EM_EXIT_OPEN || \
719 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
720 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
721 IS_FOOD_PENGUIN(Feld[x][y])))
722 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
723 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
725 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
726 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
728 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
729 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
731 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
732 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
733 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
735 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
737 #define CE_ENTER_FIELD_COND(e, x, y) \
738 (!IS_PLAYER(x, y) && \
739 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
741 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
742 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
744 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
745 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
747 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
748 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
749 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
750 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
752 /* game button identifiers */
753 #define GAME_CTRL_ID_STOP 0
754 #define GAME_CTRL_ID_PAUSE 1
755 #define GAME_CTRL_ID_PLAY 2
756 #define SOUND_CTRL_ID_MUSIC 3
757 #define SOUND_CTRL_ID_LOOPS 4
758 #define SOUND_CTRL_ID_SIMPLE 5
760 #define NUM_GAME_BUTTONS 6
763 /* forward declaration for internal use */
765 static void CreateField(int, int, int);
767 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
768 static void AdvanceFrameAndPlayerCounters(int);
770 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
771 static boolean MovePlayer(struct PlayerInfo *, int, int);
772 static void ScrollPlayer(struct PlayerInfo *, int);
773 static void ScrollScreen(struct PlayerInfo *, int);
775 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
777 static void InitBeltMovement(void);
778 static void CloseAllOpenTimegates(void);
779 static void CheckGravityMovement(struct PlayerInfo *);
780 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
781 static void KillPlayerUnlessEnemyProtected(int, int);
782 static void KillPlayerUnlessExplosionProtected(int, int);
784 static void TestIfPlayerTouchesCustomElement(int, int);
785 static void TestIfElementTouchesCustomElement(int, int);
786 static void TestIfElementHitsCustomElement(int, int, int);
788 static void TestIfElementSmashesCustomElement(int, int, int);
791 static void HandleElementChange(int, int, int);
792 static void ExecuteCustomElementAction(int, int, int, int);
793 static boolean ChangeElement(int, int, int, int);
795 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
796 #define CheckTriggeredElementChange(x, y, e, ev) \
797 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
798 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
799 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
800 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
801 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
802 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
803 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
805 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
806 #define CheckElementChange(x, y, e, te, ev) \
807 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
808 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
809 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
810 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
811 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
813 static void PlayLevelSound(int, int, int);
814 static void PlayLevelSoundNearest(int, int, int);
815 static void PlayLevelSoundAction(int, int, int);
816 static void PlayLevelSoundElementAction(int, int, int, int);
817 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
818 static void PlayLevelSoundActionIfLoop(int, int, int);
819 static void StopLevelSoundActionIfLoop(int, int, int);
820 static void PlayLevelMusic();
822 static void MapGameButtons();
823 static void HandleGameButtons(struct GadgetInfo *);
825 int AmoebeNachbarNr(int, int);
826 void AmoebeUmwandeln(int, int);
827 void ContinueMoving(int, int);
829 void InitMovDir(int, int);
830 void InitAmoebaNr(int, int);
831 int NewHiScore(void);
833 void TestIfGoodThingHitsBadThing(int, int, int);
834 void TestIfBadThingHitsGoodThing(int, int, int);
835 void TestIfPlayerTouchesBadThing(int, int);
836 void TestIfPlayerRunsIntoBadThing(int, int, int);
837 void TestIfBadThingTouchesPlayer(int, int);
838 void TestIfBadThingRunsIntoPlayer(int, int, int);
839 void TestIfFriendTouchesBadThing(int, int);
840 void TestIfBadThingTouchesFriend(int, int);
841 void TestIfBadThingTouchesOtherBadThing(int, int);
843 void KillPlayer(struct PlayerInfo *);
844 void BuryPlayer(struct PlayerInfo *);
845 void RemovePlayer(struct PlayerInfo *);
847 boolean SnapField(struct PlayerInfo *, int, int);
848 boolean DropElement(struct PlayerInfo *);
850 static int getInvisibleActiveFromInvisibleElement(int);
851 static int getInvisibleFromInvisibleActiveElement(int);
853 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
855 /* for detection of endless loops, caused by custom element programming */
856 /* (using maximal playfield width x 10 is just a rough approximation) */
857 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
859 #define RECURSION_LOOP_DETECTION_START(e, rc) \
861 if (recursion_loop_detected) \
864 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
866 recursion_loop_detected = TRUE; \
867 recursion_loop_element = (e); \
870 recursion_loop_depth++; \
873 #define RECURSION_LOOP_DETECTION_END() \
875 recursion_loop_depth--; \
878 static int recursion_loop_depth;
879 static boolean recursion_loop_detected;
880 static boolean recursion_loop_element;
883 /* ------------------------------------------------------------------------- */
884 /* definition of elements that automatically change to other elements after */
885 /* a specified time, eventually calling a function when changing */
886 /* ------------------------------------------------------------------------- */
888 /* forward declaration for changer functions */
889 static void InitBuggyBase(int, int);
890 static void WarnBuggyBase(int, int);
892 static void InitTrap(int, int);
893 static void ActivateTrap(int, int);
894 static void ChangeActiveTrap(int, int);
896 static void InitRobotWheel(int, int);
897 static void RunRobotWheel(int, int);
898 static void StopRobotWheel(int, int);
900 static void InitTimegateWheel(int, int);
901 static void RunTimegateWheel(int, int);
903 static void InitMagicBallDelay(int, int);
904 static void ActivateMagicBall(int, int);
906 struct ChangingElementInfo
911 void (*pre_change_function)(int x, int y);
912 void (*change_function)(int x, int y);
913 void (*post_change_function)(int x, int y);
916 static struct ChangingElementInfo change_delay_list[] =
951 EL_STEEL_EXIT_OPENING,
959 EL_STEEL_EXIT_CLOSING,
960 EL_STEEL_EXIT_CLOSED,
987 EL_EM_STEEL_EXIT_OPENING,
988 EL_EM_STEEL_EXIT_OPEN,
995 EL_EM_STEEL_EXIT_CLOSING,
999 EL_EM_STEEL_EXIT_CLOSED,
1023 EL_SWITCHGATE_OPENING,
1031 EL_SWITCHGATE_CLOSING,
1032 EL_SWITCHGATE_CLOSED,
1039 EL_TIMEGATE_OPENING,
1047 EL_TIMEGATE_CLOSING,
1056 EL_ACID_SPLASH_LEFT,
1064 EL_ACID_SPLASH_RIGHT,
1073 EL_SP_BUGGY_BASE_ACTIVATING,
1080 EL_SP_BUGGY_BASE_ACTIVATING,
1081 EL_SP_BUGGY_BASE_ACTIVE,
1088 EL_SP_BUGGY_BASE_ACTIVE,
1112 EL_ROBOT_WHEEL_ACTIVE,
1120 EL_TIMEGATE_SWITCH_ACTIVE,
1128 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1129 EL_DC_TIMEGATE_SWITCH,
1136 EL_EMC_MAGIC_BALL_ACTIVE,
1137 EL_EMC_MAGIC_BALL_ACTIVE,
1144 EL_EMC_SPRING_BUMPER_ACTIVE,
1145 EL_EMC_SPRING_BUMPER,
1152 EL_DIAGONAL_SHRINKING,
1160 EL_DIAGONAL_GROWING,
1181 int push_delay_fixed, push_delay_random;
1185 { EL_SPRING, 0, 0 },
1186 { EL_BALLOON, 0, 0 },
1188 { EL_SOKOBAN_OBJECT, 2, 0 },
1189 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1190 { EL_SATELLITE, 2, 0 },
1191 { EL_SP_DISK_YELLOW, 2, 0 },
1193 { EL_UNDEFINED, 0, 0 },
1201 move_stepsize_list[] =
1203 { EL_AMOEBA_DROP, 2 },
1204 { EL_AMOEBA_DROPPING, 2 },
1205 { EL_QUICKSAND_FILLING, 1 },
1206 { EL_QUICKSAND_EMPTYING, 1 },
1207 { EL_QUICKSAND_FAST_FILLING, 2 },
1208 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1209 { EL_MAGIC_WALL_FILLING, 2 },
1210 { EL_MAGIC_WALL_EMPTYING, 2 },
1211 { EL_BD_MAGIC_WALL_FILLING, 2 },
1212 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1213 { EL_DC_MAGIC_WALL_FILLING, 2 },
1214 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1216 { EL_UNDEFINED, 0 },
1224 collect_count_list[] =
1227 { EL_BD_DIAMOND, 1 },
1228 { EL_EMERALD_YELLOW, 1 },
1229 { EL_EMERALD_RED, 1 },
1230 { EL_EMERALD_PURPLE, 1 },
1232 { EL_SP_INFOTRON, 1 },
1236 { EL_UNDEFINED, 0 },
1244 access_direction_list[] =
1246 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1247 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1248 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1249 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1250 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1251 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1252 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1253 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1254 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1255 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1256 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1258 { EL_SP_PORT_LEFT, MV_RIGHT },
1259 { EL_SP_PORT_RIGHT, MV_LEFT },
1260 { EL_SP_PORT_UP, MV_DOWN },
1261 { EL_SP_PORT_DOWN, MV_UP },
1262 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1263 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1264 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1265 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1266 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1267 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1268 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1269 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1270 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1271 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1272 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1273 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1274 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1275 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1276 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1278 { EL_UNDEFINED, MV_NONE }
1281 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1283 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1284 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1285 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1286 IS_JUST_CHANGING(x, y))
1288 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1290 /* static variables for playfield scan mode (scanning forward or backward) */
1291 static int playfield_scan_start_x = 0;
1292 static int playfield_scan_start_y = 0;
1293 static int playfield_scan_delta_x = 1;
1294 static int playfield_scan_delta_y = 1;
1296 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1297 (y) >= 0 && (y) <= lev_fieldy - 1; \
1298 (y) += playfield_scan_delta_y) \
1299 for ((x) = playfield_scan_start_x; \
1300 (x) >= 0 && (x) <= lev_fieldx - 1; \
1301 (x) += playfield_scan_delta_x)
1304 void DEBUG_SetMaximumDynamite()
1308 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1309 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1310 local_player->inventory_element[local_player->inventory_size++] =
1315 static void InitPlayfieldScanModeVars()
1317 if (game.use_reverse_scan_direction)
1319 playfield_scan_start_x = lev_fieldx - 1;
1320 playfield_scan_start_y = lev_fieldy - 1;
1322 playfield_scan_delta_x = -1;
1323 playfield_scan_delta_y = -1;
1327 playfield_scan_start_x = 0;
1328 playfield_scan_start_y = 0;
1330 playfield_scan_delta_x = 1;
1331 playfield_scan_delta_y = 1;
1335 static void InitPlayfieldScanMode(int mode)
1337 game.use_reverse_scan_direction =
1338 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1340 InitPlayfieldScanModeVars();
1343 static int get_move_delay_from_stepsize(int move_stepsize)
1346 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1348 /* make sure that stepsize value is always a power of 2 */
1349 move_stepsize = (1 << log_2(move_stepsize));
1351 return TILEX / move_stepsize;
1354 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1357 int player_nr = player->index_nr;
1358 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1359 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1361 /* do no immediately change move delay -- the player might just be moving */
1362 player->move_delay_value_next = move_delay;
1364 /* information if player can move must be set separately */
1365 player->cannot_move = cannot_move;
1369 player->move_delay = game.initial_move_delay[player_nr];
1370 player->move_delay_value = game.initial_move_delay_value[player_nr];
1372 player->move_delay_value_next = -1;
1374 player->move_delay_reset_counter = 0;
1378 void GetPlayerConfig()
1380 GameFrameDelay = setup.game_frame_delay;
1382 if (!audio.sound_available)
1383 setup.sound_simple = FALSE;
1385 if (!audio.loops_available)
1386 setup.sound_loops = FALSE;
1388 if (!audio.music_available)
1389 setup.sound_music = FALSE;
1391 if (!video.fullscreen_available)
1392 setup.fullscreen = FALSE;
1394 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1396 SetAudioMode(setup.sound);
1400 int GetElementFromGroupElement(int element)
1402 if (IS_GROUP_ELEMENT(element))
1404 struct ElementGroupInfo *group = element_info[element].group;
1405 int last_anim_random_frame = gfx.anim_random_frame;
1408 if (group->choice_mode == ANIM_RANDOM)
1409 gfx.anim_random_frame = RND(group->num_elements_resolved);
1411 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1412 group->choice_mode, 0,
1415 if (group->choice_mode == ANIM_RANDOM)
1416 gfx.anim_random_frame = last_anim_random_frame;
1418 group->choice_pos++;
1420 element = group->element_resolved[element_pos];
1426 static void InitPlayerField(int x, int y, int element, boolean init_game)
1428 if (element == EL_SP_MURPHY)
1432 if (stored_player[0].present)
1434 Feld[x][y] = EL_SP_MURPHY_CLONE;
1440 stored_player[0].use_murphy = TRUE;
1442 if (!level.use_artwork_element[0])
1443 stored_player[0].artwork_element = EL_SP_MURPHY;
1446 Feld[x][y] = EL_PLAYER_1;
1452 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1453 int jx = player->jx, jy = player->jy;
1455 player->present = TRUE;
1457 player->block_last_field = (element == EL_SP_MURPHY ?
1458 level.sp_block_last_field :
1459 level.block_last_field);
1461 /* ---------- initialize player's last field block delay --------------- */
1463 /* always start with reliable default value (no adjustment needed) */
1464 player->block_delay_adjustment = 0;
1466 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1467 if (player->block_last_field && element == EL_SP_MURPHY)
1468 player->block_delay_adjustment = 1;
1470 /* special case 2: in game engines before 3.1.1, blocking was different */
1471 if (game.use_block_last_field_bug)
1472 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1474 if (!options.network || player->connected)
1476 player->active = TRUE;
1478 /* remove potentially duplicate players */
1479 if (StorePlayer[jx][jy] == Feld[x][y])
1480 StorePlayer[jx][jy] = 0;
1482 StorePlayer[x][y] = Feld[x][y];
1486 printf("Player %d activated.\n", player->element_nr);
1487 printf("[Local player is %d and currently %s.]\n",
1488 local_player->element_nr,
1489 local_player->active ? "active" : "not active");
1493 Feld[x][y] = EL_EMPTY;
1495 player->jx = player->last_jx = x;
1496 player->jy = player->last_jy = y;
1500 static void InitField(int x, int y, boolean init_game)
1502 int element = Feld[x][y];
1511 InitPlayerField(x, y, element, init_game);
1514 case EL_SOKOBAN_FIELD_PLAYER:
1515 element = Feld[x][y] = EL_PLAYER_1;
1516 InitField(x, y, init_game);
1518 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1519 InitField(x, y, init_game);
1522 case EL_SOKOBAN_FIELD_EMPTY:
1523 local_player->sokobanfields_still_needed++;
1527 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1528 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1529 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1530 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1531 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1532 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1533 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1534 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1535 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1536 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1545 case EL_SPACESHIP_RIGHT:
1546 case EL_SPACESHIP_UP:
1547 case EL_SPACESHIP_LEFT:
1548 case EL_SPACESHIP_DOWN:
1549 case EL_BD_BUTTERFLY:
1550 case EL_BD_BUTTERFLY_RIGHT:
1551 case EL_BD_BUTTERFLY_UP:
1552 case EL_BD_BUTTERFLY_LEFT:
1553 case EL_BD_BUTTERFLY_DOWN:
1555 case EL_BD_FIREFLY_RIGHT:
1556 case EL_BD_FIREFLY_UP:
1557 case EL_BD_FIREFLY_LEFT:
1558 case EL_BD_FIREFLY_DOWN:
1559 case EL_PACMAN_RIGHT:
1561 case EL_PACMAN_LEFT:
1562 case EL_PACMAN_DOWN:
1564 case EL_YAMYAM_LEFT:
1565 case EL_YAMYAM_RIGHT:
1567 case EL_YAMYAM_DOWN:
1568 case EL_DARK_YAMYAM:
1571 case EL_SP_SNIKSNAK:
1572 case EL_SP_ELECTRON:
1581 case EL_AMOEBA_FULL:
1586 case EL_AMOEBA_DROP:
1587 if (y == lev_fieldy - 1)
1589 Feld[x][y] = EL_AMOEBA_GROWING;
1590 Store[x][y] = EL_AMOEBA_WET;
1594 case EL_DYNAMITE_ACTIVE:
1595 case EL_SP_DISK_RED_ACTIVE:
1596 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1597 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1598 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1599 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1600 MovDelay[x][y] = 96;
1603 case EL_EM_DYNAMITE_ACTIVE:
1604 MovDelay[x][y] = 32;
1608 local_player->lights_still_needed++;
1612 local_player->friends_still_needed++;
1617 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1620 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1621 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1622 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1623 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1624 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1625 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1626 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1627 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1628 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1629 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1630 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1631 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1634 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1635 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1636 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1638 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1640 game.belt_dir[belt_nr] = belt_dir;
1641 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1643 else /* more than one switch -- set it like the first switch */
1645 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1650 #if !USE_BOTH_SWITCHGATE_SWITCHES
1651 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1653 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1656 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1658 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1662 case EL_LIGHT_SWITCH_ACTIVE:
1664 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1667 case EL_INVISIBLE_STEELWALL:
1668 case EL_INVISIBLE_WALL:
1669 case EL_INVISIBLE_SAND:
1670 if (game.light_time_left > 0 ||
1671 game.lenses_time_left > 0)
1672 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1675 case EL_EMC_MAGIC_BALL:
1676 if (game.ball_state)
1677 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1680 case EL_EMC_MAGIC_BALL_SWITCH:
1681 if (game.ball_state)
1682 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1686 if (IS_CUSTOM_ELEMENT(element))
1688 if (CAN_MOVE(element))
1691 #if USE_NEW_CUSTOM_VALUE
1692 if (!element_info[element].use_last_ce_value || init_game)
1693 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1696 else if (IS_GROUP_ELEMENT(element))
1698 Feld[x][y] = GetElementFromGroupElement(element);
1700 InitField(x, y, init_game);
1707 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1710 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1712 InitField(x, y, init_game);
1714 /* not needed to call InitMovDir() -- already done by InitField()! */
1715 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1716 CAN_MOVE(Feld[x][y]))
1720 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1722 int old_element = Feld[x][y];
1724 InitField(x, y, init_game);
1726 /* not needed to call InitMovDir() -- already done by InitField()! */
1727 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1728 CAN_MOVE(old_element) &&
1729 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1732 /* this case is in fact a combination of not less than three bugs:
1733 first, it calls InitMovDir() for elements that can move, although this is
1734 already done by InitField(); then, it checks the element that was at this
1735 field _before_ the call to InitField() (which can change it); lastly, it
1736 was not called for "mole with direction" elements, which were treated as
1737 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1743 static int get_key_element_from_nr(int key_nr)
1745 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1746 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1747 EL_EM_KEY_1 : EL_KEY_1);
1749 return key_base_element + key_nr;
1752 static int get_next_drop_element(struct PlayerInfo *player)
1754 return (player->inventory_size > 0 ?
1755 player->inventory_element[player->inventory_size - 1] :
1756 player->inventory_infinite_element != EL_UNDEFINED ?
1757 player->inventory_infinite_element :
1758 player->dynabombs_left > 0 ?
1759 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1763 static int get_drop_element_from_pos(struct PlayerInfo *player, int pos)
1765 /* pos >= 0: get element from bottom of the stack;
1766 pos < 0: get element from top of the stack */
1770 int min_inventory_size = -pos;
1771 int inventory_pos = player->inventory_size - min_inventory_size;
1772 int min_dynabombs_left = min_inventory_size - player->inventory_size;
1774 return (player->inventory_size >= min_inventory_size ?
1775 player->inventory_element[inventory_pos] :
1776 player->inventory_infinite_element != EL_UNDEFINED ?
1777 player->inventory_infinite_element :
1778 player->dynabombs_left >= min_dynabombs_left ?
1779 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1784 int min_dynabombs_left = pos + 1;
1785 int min_inventory_size = pos + 1 - player->dynabombs_left;
1786 int inventory_pos = pos - player->dynabombs_left;
1788 return (player->inventory_infinite_element != EL_UNDEFINED ?
1789 player->inventory_infinite_element :
1790 player->dynabombs_left >= min_dynabombs_left ?
1791 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1792 player->inventory_size >= min_inventory_size ?
1793 player->inventory_element[inventory_pos] :
1798 void InitGameControlValues()
1802 for (i = 0; game_controls[i].nr != -1; i++)
1804 int nr = game_controls[i].nr;
1805 int type = game_controls[i].type;
1806 struct TextPosInfo *pos = game_controls[i].pos;
1808 /* force update of game controls after initialization */
1809 game_control_value[nr] = last_game_control_value[nr] = -1;
1811 /* determine panel value width for later calculation of alignment */
1812 if (type == TYPE_INTEGER || type == TYPE_STRING)
1814 pos->width = pos->size * getFontWidth(pos->font);
1815 pos->height = getFontHeight(pos->font);
1817 else if (type == TYPE_ELEMENT)
1819 pos->width = pos->size;
1820 pos->height = pos->size;
1825 void UpdateGameControlValues()
1828 int time = (level.time == 0 ? TimePlayed : TimeLeft);
1829 int score = (local_player->LevelSolved ? local_player->score_final :
1830 local_player->score);
1831 int exit_closed = (local_player->gems_still_needed > 0 ||
1832 local_player->sokobanfields_still_needed > 0 ||
1833 local_player->lights_still_needed > 0);
1835 game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1836 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1838 game_control_value[GAME_CONTROL_INVENTORY_COUNT] = 0;
1839 for (i = 0; i < MAX_NUM_KEYS; i++)
1840 game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY;
1841 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY;
1842 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1844 if (game.centered_player_nr == -1)
1846 for (i = 0; i < MAX_PLAYERS; i++)
1848 for (k = 0; k < MAX_NUM_KEYS; k++)
1849 if (stored_player[i].key[k])
1850 game_control_value[GAME_CONTROL_KEY_1 + k] =
1851 get_key_element_from_nr(k);
1853 game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1854 stored_player[i].inventory_size;
1856 if (stored_player[i].num_white_keys > 0)
1857 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1859 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1860 stored_player[i].num_white_keys;
1865 int player_nr = game.centered_player_nr;
1867 for (k = 0; k < MAX_NUM_KEYS; k++)
1868 if (stored_player[player_nr].key[k])
1869 game_control_value[GAME_CONTROL_KEY_1 + k] =
1870 get_key_element_from_nr(k);
1872 game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1873 stored_player[player_nr].inventory_size;
1875 if (stored_player[player_nr].num_white_keys > 0)
1876 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1878 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1879 stored_player[player_nr].num_white_keys;
1882 for (i = 0; i < 8; i++)
1884 game_control_value[GAME_CONTROL_INVENTORY_FIRST_1 + i] =
1885 get_drop_element_from_pos(local_player, i);
1886 game_control_value[GAME_CONTROL_INVENTORY_LAST_1 + i] =
1887 get_drop_element_from_pos(local_player, -i - 1);
1890 game_control_value[GAME_CONTROL_SCORE] = score;
1892 game_control_value[GAME_CONTROL_TIME] = time;
1894 game_control_value[GAME_CONTROL_TIME_HH] = time / 3600;
1895 game_control_value[GAME_CONTROL_TIME_MM] = (time / 60) % 60;
1896 game_control_value[GAME_CONTROL_TIME_SS] = time % 60;
1898 game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1899 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1901 game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1902 local_player->shield_normal_time_left;
1903 game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1904 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1906 game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1907 local_player->shield_deadly_time_left;
1909 game_control_value[GAME_CONTROL_EXIT] =
1910 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
1913 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] =
1914 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
1915 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] =
1916 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
1917 EL_EMC_MAGIC_BALL_SWITCH);
1919 game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1920 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1921 game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1923 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1924 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1925 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1926 game.timegate_time_left;
1929 game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] =
1930 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
1932 game_control_value[GAME_CONTROL_EMC_LENSES] =
1933 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1934 game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1936 game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1937 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1938 game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1940 game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1941 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
1942 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1943 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
1944 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
1945 EL_BALLOON_SWITCH_NONE);
1947 game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1948 local_player->dynabomb_count;
1949 game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1950 local_player->dynabomb_size;
1951 game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1952 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1954 game_control_value[GAME_CONTROL_PENGUINS] =
1955 local_player->friends_still_needed;
1957 game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1958 local_player->sokobanfields_still_needed;
1959 game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1960 local_player->sokobanfields_still_needed;
1963 game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1966 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1967 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1968 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1969 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1970 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1971 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1972 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1973 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1976 game_control_value[GAME_CONTROL_MAGIC_WALL] =
1977 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
1978 game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] = game.magic_wall_time_left;
1980 #if USE_PLAYER_GRAVITY
1981 game_control_value[GAME_CONTROL_GRAVITY_STATE] = local_player->gravity;
1983 game_control_value[GAME_CONTROL_GRAVITY_STATE] = game.gravity;
1986 game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1987 game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1988 game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1991 void DisplayGameControlValues()
1995 game_status = GAME_MODE_PSEUDO_PANEL;
1997 for (i = 0; game_controls[i].nr != -1; i++)
1999 int nr = game_controls[i].nr;
2000 int type = game_controls[i].type;
2001 struct TextPosInfo *pos = game_controls[i].pos;
2002 int value = game_control_value[nr];
2003 int last_value = last_game_control_value[nr];
2004 int size = pos->size;
2005 int font = pos->font;
2007 if (value == last_value)
2010 last_game_control_value[nr] = value;
2013 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2016 if (PANEL_DEACTIVATED(pos))
2019 if (type == TYPE_INTEGER)
2021 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2023 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2025 if (use_dynamic_size) /* use dynamic number of digits */
2027 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2028 int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2029 int size2 = size1 + 1;
2030 int font1 = pos->font;
2031 int font2 = pos->font_alt;
2033 size = (value < value_change ? size1 : size2);
2034 font = (value < value_change ? font1 : font2);
2036 /* clear background if value just changed its size (dynamic digits) */
2037 if ((last_value < value_change) != (value < value_change))
2039 int width1 = size1 * getFontWidth(font1);
2040 int width2 = size2 * getFontWidth(font2);
2041 int max_width = MAX(width1, width2);
2042 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2044 pos->width = max_width;
2046 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2047 max_width, max_height);
2051 pos->width = size * getFontWidth(font);
2054 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2056 else if (type == TYPE_ELEMENT)
2058 int dst_x = PANEL_XPOS(pos);
2059 int dst_y = PANEL_YPOS(pos);
2061 if (value == EL_UNDEFINED || value == EL_EMPTY)
2063 int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2064 int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2066 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2067 size, size, dst_x, dst_y);
2071 int graphic = el2panelimg(value);
2073 DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2076 else if (type == TYPE_STRING)
2078 boolean active = (value != 0);
2079 char *state_normal = "off";
2080 char *state_active = "on";
2081 char *state = (active ? state_active : state_normal);
2082 char *s = (nr == GAME_CONTROL_GRAVITY_STATE ? state :
2083 nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
2084 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
2085 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2087 if (nr == GAME_CONTROL_GRAVITY_STATE)
2089 int font1 = pos->font; /* (used for normal state) */
2090 int font2 = pos->font_alt; /* (used for active state) */
2091 int size1 = strlen(state_normal);
2092 int size2 = strlen(state_active);
2093 int width1 = size1 * getFontWidth(font1);
2094 int width2 = size2 * getFontWidth(font2);
2095 int max_width = MAX(width1, width2);
2096 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2098 pos->width = max_width;
2100 /* clear background for values that may have changed its size */
2101 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2102 max_width, max_height);
2104 font = (active ? font2 : font1);
2109 char *s_cut = getStringCopyN(s, size);
2111 size = strlen(s_cut); /* string size may be smaller than "chars" */
2112 pos->width = size * getFontWidth(font);
2114 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2120 redraw_mask |= REDRAW_DOOR_1;
2123 game_status = GAME_MODE_PLAYING;
2126 void DrawGameValue_Emeralds(int value)
2128 struct TextPosInfo *pos = &game.panel.gems;
2130 int font_nr = pos->font;
2132 int font_nr = FONT_TEXT_2;
2134 int font_width = getFontWidth(font_nr);
2135 int chars = pos->size;
2138 return; /* !!! USE NEW STUFF !!! */
2141 if (PANEL_DEACTIVATED(pos))
2144 pos->width = chars * font_width;
2146 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2149 void DrawGameValue_Dynamite(int value)
2151 struct TextPosInfo *pos = &game.panel.inventory_count;
2153 int font_nr = pos->font;
2155 int font_nr = FONT_TEXT_2;
2157 int font_width = getFontWidth(font_nr);
2158 int chars = pos->size;
2161 return; /* !!! USE NEW STUFF !!! */
2164 if (PANEL_DEACTIVATED(pos))
2167 pos->width = chars * font_width;
2169 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2172 void DrawGameValue_Score(int value)
2174 struct TextPosInfo *pos = &game.panel.score;
2176 int font_nr = pos->font;
2178 int font_nr = FONT_TEXT_2;
2180 int font_width = getFontWidth(font_nr);
2181 int chars = pos->size;
2184 return; /* !!! USE NEW STUFF !!! */
2187 if (PANEL_DEACTIVATED(pos))
2190 pos->width = chars * font_width;
2192 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2195 void DrawGameValue_Time(int value)
2197 struct TextPosInfo *pos = &game.panel.time;
2198 static int last_value = -1;
2201 int chars = pos->size;
2203 int font1_nr = pos->font;
2204 int font2_nr = pos->font_alt;
2206 int font1_nr = FONT_TEXT_2;
2207 int font2_nr = FONT_TEXT_1;
2209 int font_nr = font1_nr;
2210 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2213 return; /* !!! USE NEW STUFF !!! */
2216 if (PANEL_DEACTIVATED(pos))
2219 if (use_dynamic_chars) /* use dynamic number of chars */
2221 chars = (value < 1000 ? chars1 : chars2);
2222 font_nr = (value < 1000 ? font1_nr : font2_nr);
2225 /* clear background if value just changed its size (dynamic chars only) */
2226 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2228 int width1 = chars1 * getFontWidth(font1_nr);
2229 int width2 = chars2 * getFontWidth(font2_nr);
2230 int max_width = MAX(width1, width2);
2231 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2233 pos->width = max_width;
2235 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2236 max_width, max_height);
2239 pos->width = chars * getFontWidth(font_nr);
2241 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2246 void DrawGameValue_Level(int value)
2248 struct TextPosInfo *pos = &game.panel.level_number;
2251 int chars = pos->size;
2253 int font1_nr = pos->font;
2254 int font2_nr = pos->font_alt;
2256 int font1_nr = FONT_TEXT_2;
2257 int font2_nr = FONT_TEXT_1;
2259 int font_nr = font1_nr;
2260 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2263 return; /* !!! USE NEW STUFF !!! */
2266 if (PANEL_DEACTIVATED(pos))
2269 if (use_dynamic_chars) /* use dynamic number of chars */
2271 chars = (level_nr < 100 ? chars1 : chars2);
2272 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2275 pos->width = chars * getFontWidth(font_nr);
2277 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2280 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2283 struct TextPosInfo *pos = &game.panel.keys;
2286 int base_key_graphic = EL_KEY_1;
2291 return; /* !!! USE NEW STUFF !!! */
2295 if (PANEL_DEACTIVATED(pos))
2300 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2301 base_key_graphic = EL_EM_KEY_1;
2305 pos->width = 4 * MINI_TILEX;
2309 for (i = 0; i < MAX_NUM_KEYS; i++)
2311 /* currently only 4 of 8 possible keys are displayed */
2312 for (i = 0; i < STD_NUM_KEYS; i++)
2316 struct TextPosInfo *pos = &game.panel.key[i];
2318 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2319 int src_y = DOOR_GFX_PAGEY1 + 123;
2321 int dst_x = PANEL_XPOS(pos);
2322 int dst_y = PANEL_YPOS(pos);
2324 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2325 int dst_y = PANEL_YPOS(pos);
2329 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2330 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2332 int graphic = el2edimg(element);
2336 if (PANEL_DEACTIVATED(pos))
2341 /* masked blit with tiles from half-size scaled bitmap does not work yet
2342 (no mask bitmap created for these sizes after loading and scaling) --
2343 solution: load without creating mask, scale, then create final mask */
2345 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2346 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2351 int graphic = el2edimg(base_key_graphic + i);
2356 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2358 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2359 dst_x - src_x, dst_y - src_y);
2360 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2366 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2368 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2369 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2372 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2374 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2375 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2383 void DrawGameValue_Emeralds(int value)
2385 int font_nr = FONT_TEXT_2;
2386 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2388 if (PANEL_DEACTIVATED(game.panel.gems))
2391 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2394 void DrawGameValue_Dynamite(int value)
2396 int font_nr = FONT_TEXT_2;
2397 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2399 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2402 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2405 void DrawGameValue_Score(int value)
2407 int font_nr = FONT_TEXT_2;
2408 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2410 if (PANEL_DEACTIVATED(game.panel.score))
2413 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2416 void DrawGameValue_Time(int value)
2418 int font1_nr = FONT_TEXT_2;
2420 int font2_nr = FONT_TEXT_1;
2422 int font2_nr = FONT_LEVEL_NUMBER;
2424 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2425 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2427 if (PANEL_DEACTIVATED(game.panel.time))
2430 /* clear background if value just changed its size */
2431 if (value == 999 || value == 1000)
2432 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2435 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2437 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2440 void DrawGameValue_Level(int value)
2442 int font1_nr = FONT_TEXT_2;
2444 int font2_nr = FONT_TEXT_1;
2446 int font2_nr = FONT_LEVEL_NUMBER;
2449 if (PANEL_DEACTIVATED(game.panel.level))
2453 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2455 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2458 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2460 int base_key_graphic = EL_KEY_1;
2463 if (PANEL_DEACTIVATED(game.panel.keys))
2466 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2467 base_key_graphic = EL_EM_KEY_1;
2469 /* currently only 4 of 8 possible keys are displayed */
2470 for (i = 0; i < STD_NUM_KEYS; i++)
2472 int x = XX_KEYS + i * MINI_TILEX;
2476 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2478 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2479 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2485 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2488 int key[MAX_NUM_KEYS];
2491 /* prevent EM engine from updating time/score values parallel to GameWon() */
2492 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2493 local_player->LevelSolved)
2496 for (i = 0; i < MAX_NUM_KEYS; i++)
2497 key[i] = key_bits & (1 << i);
2499 DrawGameValue_Level(level_nr);
2501 DrawGameValue_Emeralds(emeralds);
2502 DrawGameValue_Dynamite(dynamite);
2503 DrawGameValue_Score(score);
2504 DrawGameValue_Time(time);
2506 DrawGameValue_Keys(key);
2509 void DrawGameDoorValues()
2511 UpdateGameControlValues();
2512 DisplayGameControlValues();
2515 void DrawGameDoorValues_OLD()
2517 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2518 int dynamite_value = 0;
2519 int score_value = (local_player->LevelSolved ? local_player->score_final :
2520 local_player->score);
2521 int gems_value = local_player->gems_still_needed;
2525 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2527 DrawGameDoorValues_EM();
2532 if (game.centered_player_nr == -1)
2534 for (i = 0; i < MAX_PLAYERS; i++)
2536 for (j = 0; j < MAX_NUM_KEYS; j++)
2537 if (stored_player[i].key[j])
2538 key_bits |= (1 << j);
2540 dynamite_value += stored_player[i].inventory_size;
2545 int player_nr = game.centered_player_nr;
2547 for (i = 0; i < MAX_NUM_KEYS; i++)
2548 if (stored_player[player_nr].key[i])
2549 key_bits |= (1 << i);
2551 dynamite_value = stored_player[player_nr].inventory_size;
2554 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2560 =============================================================================
2562 -----------------------------------------------------------------------------
2563 initialize game engine due to level / tape version number
2564 =============================================================================
2567 static void InitGameEngine()
2569 int i, j, k, l, x, y;
2571 /* set game engine from tape file when re-playing, else from level file */
2572 game.engine_version = (tape.playing ? tape.engine_version :
2573 level.game_version);
2575 /* ---------------------------------------------------------------------- */
2576 /* set flags for bugs and changes according to active game engine version */
2577 /* ---------------------------------------------------------------------- */
2580 Summary of bugfix/change:
2581 Fixed handling for custom elements that change when pushed by the player.
2583 Fixed/changed in version:
2587 Before 3.1.0, custom elements that "change when pushing" changed directly
2588 after the player started pushing them (until then handled in "DigField()").
2589 Since 3.1.0, these custom elements are not changed until the "pushing"
2590 move of the element is finished (now handled in "ContinueMoving()").
2592 Affected levels/tapes:
2593 The first condition is generally needed for all levels/tapes before version
2594 3.1.0, which might use the old behaviour before it was changed; known tapes
2595 that are affected are some tapes from the level set "Walpurgis Gardens" by
2597 The second condition is an exception from the above case and is needed for
2598 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2599 above (including some development versions of 3.1.0), but before it was
2600 known that this change would break tapes like the above and was fixed in
2601 3.1.1, so that the changed behaviour was active although the engine version
2602 while recording maybe was before 3.1.0. There is at least one tape that is
2603 affected by this exception, which is the tape for the one-level set "Bug
2604 Machine" by Juergen Bonhagen.
2607 game.use_change_when_pushing_bug =
2608 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2610 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2611 tape.game_version < VERSION_IDENT(3,1,1,0)));
2614 Summary of bugfix/change:
2615 Fixed handling for blocking the field the player leaves when moving.
2617 Fixed/changed in version:
2621 Before 3.1.1, when "block last field when moving" was enabled, the field
2622 the player is leaving when moving was blocked for the time of the move,
2623 and was directly unblocked afterwards. This resulted in the last field
2624 being blocked for exactly one less than the number of frames of one player
2625 move. Additionally, even when blocking was disabled, the last field was
2626 blocked for exactly one frame.
2627 Since 3.1.1, due to changes in player movement handling, the last field
2628 is not blocked at all when blocking is disabled. When blocking is enabled,
2629 the last field is blocked for exactly the number of frames of one player
2630 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2631 last field is blocked for exactly one more than the number of frames of
2634 Affected levels/tapes:
2635 (!!! yet to be determined -- probably many !!!)
2638 game.use_block_last_field_bug =
2639 (game.engine_version < VERSION_IDENT(3,1,1,0));
2642 Summary of bugfix/change:
2643 Changed behaviour of CE changes with multiple changes per single frame.
2645 Fixed/changed in version:
2649 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2650 This resulted in race conditions where CEs seem to behave strange in some
2651 situations (where triggered CE changes were just skipped because there was
2652 already a CE change on that tile in the playfield in that engine frame).
2653 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2654 (The number of changes per frame must be limited in any case, because else
2655 it is easily possible to define CE changes that would result in an infinite
2656 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2657 should be set large enough so that it would only be reached in cases where
2658 the corresponding CE change conditions run into a loop. Therefore, it seems
2659 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2660 maximal number of change pages for custom elements.)
2662 Affected levels/tapes:
2666 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2667 game.max_num_changes_per_frame = 1;
2669 game.max_num_changes_per_frame =
2670 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2673 /* ---------------------------------------------------------------------- */
2675 /* default scan direction: scan playfield from top/left to bottom/right */
2676 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2678 /* dynamically adjust element properties according to game engine version */
2679 InitElementPropertiesEngine(game.engine_version);
2682 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2683 printf(" tape version == %06d [%s] [file: %06d]\n",
2684 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2686 printf(" => game.engine_version == %06d\n", game.engine_version);
2689 /* ---------- initialize player's initial move delay --------------------- */
2691 /* dynamically adjust player properties according to level information */
2692 for (i = 0; i < MAX_PLAYERS; i++)
2693 game.initial_move_delay_value[i] =
2694 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2696 /* dynamically adjust player properties according to game engine version */
2697 for (i = 0; i < MAX_PLAYERS; i++)
2698 game.initial_move_delay[i] =
2699 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2700 game.initial_move_delay_value[i] : 0);
2702 /* ---------- initialize player's initial push delay --------------------- */
2704 /* dynamically adjust player properties according to game engine version */
2705 game.initial_push_delay_value =
2706 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2708 /* ---------- initialize changing elements ------------------------------- */
2710 /* initialize changing elements information */
2711 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2713 struct ElementInfo *ei = &element_info[i];
2715 /* this pointer might have been changed in the level editor */
2716 ei->change = &ei->change_page[0];
2718 if (!IS_CUSTOM_ELEMENT(i))
2720 ei->change->target_element = EL_EMPTY_SPACE;
2721 ei->change->delay_fixed = 0;
2722 ei->change->delay_random = 0;
2723 ei->change->delay_frames = 1;
2726 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2728 ei->has_change_event[j] = FALSE;
2730 ei->event_page_nr[j] = 0;
2731 ei->event_page[j] = &ei->change_page[0];
2735 /* add changing elements from pre-defined list */
2736 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2738 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2739 struct ElementInfo *ei = &element_info[ch_delay->element];
2741 ei->change->target_element = ch_delay->target_element;
2742 ei->change->delay_fixed = ch_delay->change_delay;
2744 ei->change->pre_change_function = ch_delay->pre_change_function;
2745 ei->change->change_function = ch_delay->change_function;
2746 ei->change->post_change_function = ch_delay->post_change_function;
2748 ei->change->can_change = TRUE;
2749 ei->change->can_change_or_has_action = TRUE;
2751 ei->has_change_event[CE_DELAY] = TRUE;
2753 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2754 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2757 /* ---------- initialize internal run-time variables ------------- */
2759 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2761 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2763 for (j = 0; j < ei->num_change_pages; j++)
2765 ei->change_page[j].can_change_or_has_action =
2766 (ei->change_page[j].can_change |
2767 ei->change_page[j].has_action);
2771 /* add change events from custom element configuration */
2772 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2774 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2776 for (j = 0; j < ei->num_change_pages; j++)
2778 if (!ei->change_page[j].can_change_or_has_action)
2781 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2783 /* only add event page for the first page found with this event */
2784 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2786 ei->has_change_event[k] = TRUE;
2788 ei->event_page_nr[k] = j;
2789 ei->event_page[k] = &ei->change_page[j];
2795 /* ---------- initialize run-time trigger player and element ------------- */
2797 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801 for (j = 0; j < ei->num_change_pages; j++)
2803 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2804 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2805 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2806 ei->change_page[j].actual_trigger_ce_value = 0;
2807 ei->change_page[j].actual_trigger_ce_score = 0;
2811 /* ---------- initialize trigger events ---------------------------------- */
2813 /* initialize trigger events information */
2814 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2815 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2816 trigger_events[i][j] = FALSE;
2818 /* add trigger events from element change event properties */
2819 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2821 struct ElementInfo *ei = &element_info[i];
2823 for (j = 0; j < ei->num_change_pages; j++)
2825 if (!ei->change_page[j].can_change_or_has_action)
2828 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2830 int trigger_element = ei->change_page[j].trigger_element;
2832 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2834 if (ei->change_page[j].has_event[k])
2836 if (IS_GROUP_ELEMENT(trigger_element))
2838 struct ElementGroupInfo *group =
2839 element_info[trigger_element].group;
2841 for (l = 0; l < group->num_elements_resolved; l++)
2842 trigger_events[group->element_resolved[l]][k] = TRUE;
2844 else if (trigger_element == EL_ANY_ELEMENT)
2845 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2846 trigger_events[l][k] = TRUE;
2848 trigger_events[trigger_element][k] = TRUE;
2855 /* ---------- initialize push delay -------------------------------------- */
2857 /* initialize push delay values to default */
2858 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2860 if (!IS_CUSTOM_ELEMENT(i))
2862 /* set default push delay values (corrected since version 3.0.7-1) */
2863 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2865 element_info[i].push_delay_fixed = 2;
2866 element_info[i].push_delay_random = 8;
2870 element_info[i].push_delay_fixed = 8;
2871 element_info[i].push_delay_random = 8;
2876 /* set push delay value for certain elements from pre-defined list */
2877 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2879 int e = push_delay_list[i].element;
2881 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2882 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2885 /* set push delay value for Supaplex elements for newer engine versions */
2886 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2888 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2890 if (IS_SP_ELEMENT(i))
2892 /* set SP push delay to just enough to push under a falling zonk */
2893 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2895 element_info[i].push_delay_fixed = delay;
2896 element_info[i].push_delay_random = 0;
2901 /* ---------- initialize move stepsize ----------------------------------- */
2903 /* initialize move stepsize values to default */
2904 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905 if (!IS_CUSTOM_ELEMENT(i))
2906 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2908 /* set move stepsize value for certain elements from pre-defined list */
2909 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2911 int e = move_stepsize_list[i].element;
2913 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2916 /* ---------- initialize collect score ----------------------------------- */
2918 /* initialize collect score values for custom elements from initial value */
2919 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2920 if (IS_CUSTOM_ELEMENT(i))
2921 element_info[i].collect_score = element_info[i].collect_score_initial;
2923 /* ---------- initialize collect count ----------------------------------- */
2925 /* initialize collect count values for non-custom elements */
2926 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2927 if (!IS_CUSTOM_ELEMENT(i))
2928 element_info[i].collect_count_initial = 0;
2930 /* add collect count values for all elements from pre-defined list */
2931 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2932 element_info[collect_count_list[i].element].collect_count_initial =
2933 collect_count_list[i].count;
2935 /* ---------- initialize access direction -------------------------------- */
2937 /* initialize access direction values to default (access from every side) */
2938 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2939 if (!IS_CUSTOM_ELEMENT(i))
2940 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2942 /* set access direction value for certain elements from pre-defined list */
2943 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2944 element_info[access_direction_list[i].element].access_direction =
2945 access_direction_list[i].direction;
2947 /* ---------- initialize explosion content ------------------------------- */
2948 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2950 if (IS_CUSTOM_ELEMENT(i))
2953 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2955 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2957 element_info[i].content.e[x][y] =
2958 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2959 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2960 i == EL_PLAYER_3 ? EL_EMERALD :
2961 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2962 i == EL_MOLE ? EL_EMERALD_RED :
2963 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2964 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2965 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2966 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2967 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2968 i == EL_WALL_EMERALD ? EL_EMERALD :
2969 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2970 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2971 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2972 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2973 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2974 i == EL_WALL_PEARL ? EL_PEARL :
2975 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2980 /* ---------- initialize recursion detection ------------------------------ */
2981 recursion_loop_depth = 0;
2982 recursion_loop_detected = FALSE;
2983 recursion_loop_element = EL_UNDEFINED;
2985 /* ---------- initialize graphics engine ---------------------------------- */
2986 game.scroll_delay_value =
2987 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2988 setup.scroll_delay ? setup.scroll_delay_value : 0);
2989 game.scroll_delay_value =
2990 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2993 int get_num_special_action(int element, int action_first, int action_last)
2995 int num_special_action = 0;
2998 for (i = action_first; i <= action_last; i++)
3000 boolean found = FALSE;
3002 for (j = 0; j < NUM_DIRECTIONS; j++)
3003 if (el_act_dir2img(element, i, j) !=
3004 el_act_dir2img(element, ACTION_DEFAULT, j))
3008 num_special_action++;
3013 return num_special_action;
3018 =============================================================================
3020 -----------------------------------------------------------------------------
3021 initialize and start new game
3022 =============================================================================
3027 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3028 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3029 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3031 boolean do_fading = (game_status == GAME_MODE_MAIN);
3035 game_status = GAME_MODE_PLAYING;
3038 InitGameControlValues();
3040 /* don't play tapes over network */
3041 network_playing = (options.network && !tape.playing);
3043 for (i = 0; i < MAX_PLAYERS; i++)
3045 struct PlayerInfo *player = &stored_player[i];
3047 player->index_nr = i;
3048 player->index_bit = (1 << i);
3049 player->element_nr = EL_PLAYER_1 + i;
3051 player->present = FALSE;
3052 player->active = FALSE;
3053 player->killed = FALSE;
3056 player->effective_action = 0;
3057 player->programmed_action = 0;
3060 player->score_final = 0;
3062 player->gems_still_needed = level.gems_needed;
3063 player->sokobanfields_still_needed = 0;
3064 player->lights_still_needed = 0;
3065 player->friends_still_needed = 0;
3067 for (j = 0; j < MAX_NUM_KEYS; j++)
3068 player->key[j] = FALSE;
3070 player->num_white_keys = 0;
3072 player->dynabomb_count = 0;
3073 player->dynabomb_size = 1;
3074 player->dynabombs_left = 0;
3075 player->dynabomb_xl = FALSE;
3077 player->MovDir = MV_NONE;
3080 player->GfxDir = MV_NONE;
3081 player->GfxAction = ACTION_DEFAULT;
3083 player->StepFrame = 0;
3085 player->use_murphy = FALSE;
3086 player->artwork_element =
3087 (level.use_artwork_element[i] ? level.artwork_element[i] :
3088 player->element_nr);
3090 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3091 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3093 player->gravity = level.initial_player_gravity[i];
3095 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3097 player->actual_frame_counter = 0;
3099 player->step_counter = 0;
3101 player->last_move_dir = MV_NONE;
3103 player->is_active = FALSE;
3105 player->is_waiting = FALSE;
3106 player->is_moving = FALSE;
3107 player->is_auto_moving = FALSE;
3108 player->is_digging = FALSE;
3109 player->is_snapping = FALSE;
3110 player->is_collecting = FALSE;
3111 player->is_pushing = FALSE;
3112 player->is_switching = FALSE;
3113 player->is_dropping = FALSE;
3114 player->is_dropping_pressed = FALSE;
3116 player->is_bored = FALSE;
3117 player->is_sleeping = FALSE;
3119 player->frame_counter_bored = -1;
3120 player->frame_counter_sleeping = -1;
3122 player->anim_delay_counter = 0;
3123 player->post_delay_counter = 0;
3125 player->dir_waiting = MV_NONE;
3126 player->action_waiting = ACTION_DEFAULT;
3127 player->last_action_waiting = ACTION_DEFAULT;
3128 player->special_action_bored = ACTION_DEFAULT;
3129 player->special_action_sleeping = ACTION_DEFAULT;
3131 player->switch_x = -1;
3132 player->switch_y = -1;
3134 player->drop_x = -1;
3135 player->drop_y = -1;
3137 player->show_envelope = 0;
3139 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3141 player->push_delay = -1; /* initialized when pushing starts */
3142 player->push_delay_value = game.initial_push_delay_value;
3144 player->drop_delay = 0;
3145 player->drop_pressed_delay = 0;
3147 player->last_jx = -1;
3148 player->last_jy = -1;
3152 player->shield_normal_time_left = 0;
3153 player->shield_deadly_time_left = 0;
3155 player->inventory_infinite_element = EL_UNDEFINED;
3156 player->inventory_size = 0;
3158 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3159 SnapField(player, 0, 0);
3161 player->LevelSolved = FALSE;
3162 player->GameOver = FALSE;
3164 player->LevelSolved_GameWon = FALSE;
3165 player->LevelSolved_GameEnd = FALSE;
3166 player->LevelSolved_PanelOff = FALSE;
3167 player->LevelSolved_SaveTape = FALSE;
3168 player->LevelSolved_SaveScore = FALSE;
3171 network_player_action_received = FALSE;
3173 #if defined(NETWORK_AVALIABLE)
3174 /* initial null action */
3175 if (network_playing)
3176 SendToServer_MovePlayer(MV_NONE);
3185 TimeLeft = level.time;
3188 ScreenMovDir = MV_NONE;
3192 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3194 AllPlayersGone = FALSE;
3196 game.yamyam_content_nr = 0;
3197 game.magic_wall_active = FALSE;
3198 game.magic_wall_time_left = 0;
3199 game.light_time_left = 0;
3200 game.timegate_time_left = 0;
3201 game.switchgate_pos = 0;
3202 game.wind_direction = level.wind_direction_initial;
3204 #if !USE_PLAYER_GRAVITY
3205 game.gravity = FALSE;
3206 game.explosions_delayed = TRUE;
3209 game.lenses_time_left = 0;
3210 game.magnify_time_left = 0;
3212 game.ball_state = level.ball_state_initial;
3213 game.ball_content_nr = 0;
3215 game.envelope_active = FALSE;
3217 /* set focus to local player for network games, else to all players */
3218 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3219 game.centered_player_nr_next = game.centered_player_nr;
3220 game.set_centered_player = FALSE;
3222 if (network_playing && tape.recording)
3224 /* store client dependent player focus when recording network games */
3225 tape.centered_player_nr_next = game.centered_player_nr_next;
3226 tape.set_centered_player = TRUE;
3229 for (i = 0; i < NUM_BELTS; i++)
3231 game.belt_dir[i] = MV_NONE;
3232 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3235 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3236 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3238 SCAN_PLAYFIELD(x, y)
3240 Feld[x][y] = level.field[x][y];
3241 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3242 ChangeDelay[x][y] = 0;
3243 ChangePage[x][y] = -1;
3244 #if USE_NEW_CUSTOM_VALUE
3245 CustomValue[x][y] = 0; /* initialized in InitField() */
3247 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3249 WasJustMoving[x][y] = 0;
3250 WasJustFalling[x][y] = 0;
3251 CheckCollision[x][y] = 0;
3252 CheckImpact[x][y] = 0;
3254 Pushed[x][y] = FALSE;
3256 ChangeCount[x][y] = 0;
3257 ChangeEvent[x][y] = -1;
3259 ExplodePhase[x][y] = 0;
3260 ExplodeDelay[x][y] = 0;
3261 ExplodeField[x][y] = EX_TYPE_NONE;
3263 RunnerVisit[x][y] = 0;
3264 PlayerVisit[x][y] = 0;
3267 GfxRandom[x][y] = INIT_GFX_RANDOM();
3268 GfxElement[x][y] = EL_UNDEFINED;
3269 GfxAction[x][y] = ACTION_DEFAULT;
3270 GfxDir[x][y] = MV_NONE;
3273 SCAN_PLAYFIELD(x, y)
3275 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3277 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3279 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3282 InitField(x, y, TRUE);
3287 for (i = 0; i < MAX_PLAYERS; i++)
3289 struct PlayerInfo *player = &stored_player[i];
3291 /* set number of special actions for bored and sleeping animation */
3292 player->num_special_action_bored =
3293 get_num_special_action(player->artwork_element,
3294 ACTION_BORING_1, ACTION_BORING_LAST);
3295 player->num_special_action_sleeping =
3296 get_num_special_action(player->artwork_element,
3297 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3300 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3301 emulate_sb ? EMU_SOKOBAN :
3302 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3304 #if USE_NEW_ALL_SLIPPERY
3305 /* initialize type of slippery elements */
3306 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3308 if (!IS_CUSTOM_ELEMENT(i))
3310 /* default: elements slip down either to the left or right randomly */
3311 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3313 /* SP style elements prefer to slip down on the left side */
3314 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3315 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3317 /* BD style elements prefer to slip down on the left side */
3318 if (game.emulation == EMU_BOULDERDASH)
3319 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3324 /* initialize explosion and ignition delay */
3325 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3327 if (!IS_CUSTOM_ELEMENT(i))
3330 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3331 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3332 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3333 int last_phase = (num_phase + 1) * delay;
3334 int half_phase = (num_phase / 2) * delay;
3336 element_info[i].explosion_delay = last_phase - 1;
3337 element_info[i].ignition_delay = half_phase;
3339 if (i == EL_BLACK_ORB)
3340 element_info[i].ignition_delay = 1;
3344 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3345 element_info[i].explosion_delay = 1;
3347 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3348 element_info[i].ignition_delay = 1;
3352 /* correct non-moving belts to start moving left */
3353 for (i = 0; i < NUM_BELTS; i++)
3354 if (game.belt_dir[i] == MV_NONE)
3355 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3357 /* check if any connected player was not found in playfield */
3358 for (i = 0; i < MAX_PLAYERS; i++)
3360 struct PlayerInfo *player = &stored_player[i];
3362 if (player->connected && !player->present)
3364 for (j = 0; j < MAX_PLAYERS; j++)
3366 struct PlayerInfo *some_player = &stored_player[j];
3367 int jx = some_player->jx, jy = some_player->jy;
3369 /* assign first free player found that is present in the playfield */
3370 if (some_player->present && !some_player->connected)
3372 player->present = TRUE;
3373 player->active = TRUE;
3375 some_player->present = FALSE;
3376 some_player->active = FALSE;
3378 player->artwork_element = some_player->artwork_element;
3380 player->block_last_field = some_player->block_last_field;
3381 player->block_delay_adjustment = some_player->block_delay_adjustment;
3383 StorePlayer[jx][jy] = player->element_nr;
3384 player->jx = player->last_jx = jx;
3385 player->jy = player->last_jy = jy;
3395 /* when playing a tape, eliminate all players who do not participate */
3397 for (i = 0; i < MAX_PLAYERS; i++)
3399 if (stored_player[i].active && !tape.player_participates[i])
3401 struct PlayerInfo *player = &stored_player[i];
3402 int jx = player->jx, jy = player->jy;
3404 player->active = FALSE;
3405 StorePlayer[jx][jy] = 0;
3406 Feld[jx][jy] = EL_EMPTY;
3410 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3412 /* when in single player mode, eliminate all but the first active player */
3414 for (i = 0; i < MAX_PLAYERS; i++)
3416 if (stored_player[i].active)
3418 for (j = i + 1; j < MAX_PLAYERS; j++)
3420 if (stored_player[j].active)
3422 struct PlayerInfo *player = &stored_player[j];
3423 int jx = player->jx, jy = player->jy;
3425 player->active = FALSE;
3426 player->present = FALSE;
3428 StorePlayer[jx][jy] = 0;
3429 Feld[jx][jy] = EL_EMPTY;
3436 /* when recording the game, store which players take part in the game */
3439 for (i = 0; i < MAX_PLAYERS; i++)
3440 if (stored_player[i].active)
3441 tape.player_participates[i] = TRUE;
3446 for (i = 0; i < MAX_PLAYERS; i++)
3448 struct PlayerInfo *player = &stored_player[i];
3450 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3455 if (local_player == player)
3456 printf("Player %d is local player.\n", i+1);
3460 if (BorderElement == EL_EMPTY)
3463 SBX_Right = lev_fieldx - SCR_FIELDX;
3465 SBY_Lower = lev_fieldy - SCR_FIELDY;
3470 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3472 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3475 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3476 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3478 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3479 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3481 /* if local player not found, look for custom element that might create
3482 the player (make some assumptions about the right custom element) */
3483 if (!local_player->present)
3485 int start_x = 0, start_y = 0;
3486 int found_rating = 0;
3487 int found_element = EL_UNDEFINED;
3488 int player_nr = local_player->index_nr;
3490 SCAN_PLAYFIELD(x, y)
3492 int element = Feld[x][y];
3497 if (level.use_start_element[player_nr] &&
3498 level.start_element[player_nr] == element &&
3505 found_element = element;
3508 if (!IS_CUSTOM_ELEMENT(element))
3511 if (CAN_CHANGE(element))
3513 for (i = 0; i < element_info[element].num_change_pages; i++)
3515 /* check for player created from custom element as single target */
3516 content = element_info[element].change_page[i].target_element;
3517 is_player = ELEM_IS_PLAYER(content);
3519 if (is_player && (found_rating < 3 ||
3520 (found_rating == 3 && element < found_element)))
3526 found_element = element;
3531 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3533 /* check for player created from custom element as explosion content */
3534 content = element_info[element].content.e[xx][yy];
3535 is_player = ELEM_IS_PLAYER(content);
3537 if (is_player && (found_rating < 2 ||
3538 (found_rating == 2 && element < found_element)))
3540 start_x = x + xx - 1;
3541 start_y = y + yy - 1;
3544 found_element = element;
3547 if (!CAN_CHANGE(element))
3550 for (i = 0; i < element_info[element].num_change_pages; i++)
3552 /* check for player created from custom element as extended target */
3554 element_info[element].change_page[i].target_content.e[xx][yy];
3556 is_player = ELEM_IS_PLAYER(content);
3558 if (is_player && (found_rating < 1 ||
3559 (found_rating == 1 && element < found_element)))
3561 start_x = x + xx - 1;
3562 start_y = y + yy - 1;
3565 found_element = element;
3571 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3572 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3575 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3576 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3581 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3582 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3583 local_player->jx - MIDPOSX);
3585 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3586 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3587 local_player->jy - MIDPOSY);
3590 /* do not use PLAYING mask for fading out from main screen */
3591 game_status = GAME_MODE_MAIN;
3595 if (!game.restart_level)
3596 CloseDoor(DOOR_CLOSE_1);
3599 if (level_editor_test_game)
3600 FadeSkipNextFadeIn();
3602 FadeSetEnterScreen();
3604 if (level_editor_test_game)
3605 fading = fading_none;
3607 fading = menu.destination;
3611 FadeOut(REDRAW_FIELD);
3614 FadeOut(REDRAW_FIELD);
3617 game_status = GAME_MODE_PLAYING;
3619 /* !!! FIX THIS (START) !!! */
3620 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3622 InitGameEngine_EM();
3624 /* blit playfield from scroll buffer to normal back buffer for fading in */
3625 BlitScreenToBitmap_EM(backbuffer);
3632 /* after drawing the level, correct some elements */
3633 if (game.timegate_time_left == 0)
3634 CloseAllOpenTimegates();
3636 /* blit playfield from scroll buffer to normal back buffer for fading in */
3637 if (setup.soft_scrolling)
3638 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3640 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3642 /* !!! FIX THIS (END) !!! */
3645 FadeIn(REDRAW_FIELD);
3648 FadeIn(REDRAW_FIELD);
3653 if (!game.restart_level)
3655 /* copy default game door content to main double buffer */
3656 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3657 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3660 SetPanelBackground();
3661 SetDrawBackgroundMask(REDRAW_DOOR_1);
3663 DrawGameDoorValues();
3665 if (!game.restart_level)
3669 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3670 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3671 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3675 /* copy actual game door content to door double buffer for OpenDoor() */
3676 BlitBitmap(drawto, bitmap_db_door,
3677 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3679 OpenDoor(DOOR_OPEN_ALL);
3681 PlaySound(SND_GAME_STARTING);
3683 if (setup.sound_music)
3686 KeyboardAutoRepeatOffUnlessAutoplay();
3690 for (i = 0; i < MAX_PLAYERS; i++)
3691 printf("Player %d %sactive.\n",
3692 i + 1, (stored_player[i].active ? "" : "not "));
3703 game.restart_level = FALSE;
3706 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3708 /* this is used for non-R'n'D game engines to update certain engine values */
3710 /* needed to determine if sounds are played within the visible screen area */
3711 scroll_x = actual_scroll_x;
3712 scroll_y = actual_scroll_y;
3715 void InitMovDir(int x, int y)
3717 int i, element = Feld[x][y];
3718 static int xy[4][2] =
3725 static int direction[3][4] =
3727 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3728 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3729 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3738 Feld[x][y] = EL_BUG;
3739 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3742 case EL_SPACESHIP_RIGHT:
3743 case EL_SPACESHIP_UP:
3744 case EL_SPACESHIP_LEFT:
3745 case EL_SPACESHIP_DOWN:
3746 Feld[x][y] = EL_SPACESHIP;
3747 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3750 case EL_BD_BUTTERFLY_RIGHT:
3751 case EL_BD_BUTTERFLY_UP:
3752 case EL_BD_BUTTERFLY_LEFT:
3753 case EL_BD_BUTTERFLY_DOWN:
3754 Feld[x][y] = EL_BD_BUTTERFLY;
3755 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3758 case EL_BD_FIREFLY_RIGHT:
3759 case EL_BD_FIREFLY_UP:
3760 case EL_BD_FIREFLY_LEFT:
3761 case EL_BD_FIREFLY_DOWN:
3762 Feld[x][y] = EL_BD_FIREFLY;
3763 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3766 case EL_PACMAN_RIGHT:
3768 case EL_PACMAN_LEFT:
3769 case EL_PACMAN_DOWN:
3770 Feld[x][y] = EL_PACMAN;
3771 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3774 case EL_YAMYAM_LEFT:
3775 case EL_YAMYAM_RIGHT:
3777 case EL_YAMYAM_DOWN:
3778 Feld[x][y] = EL_YAMYAM;
3779 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3782 case EL_SP_SNIKSNAK:
3783 MovDir[x][y] = MV_UP;
3786 case EL_SP_ELECTRON:
3787 MovDir[x][y] = MV_LEFT;
3794 Feld[x][y] = EL_MOLE;
3795 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3799 if (IS_CUSTOM_ELEMENT(element))
3801 struct ElementInfo *ei = &element_info[element];
3802 int move_direction_initial = ei->move_direction_initial;
3803 int move_pattern = ei->move_pattern;
3805 if (move_direction_initial == MV_START_PREVIOUS)
3807 if (MovDir[x][y] != MV_NONE)
3810 move_direction_initial = MV_START_AUTOMATIC;
3813 if (move_direction_initial == MV_START_RANDOM)
3814 MovDir[x][y] = 1 << RND(4);
3815 else if (move_direction_initial & MV_ANY_DIRECTION)
3816 MovDir[x][y] = move_direction_initial;
3817 else if (move_pattern == MV_ALL_DIRECTIONS ||
3818 move_pattern == MV_TURNING_LEFT ||
3819 move_pattern == MV_TURNING_RIGHT ||
3820 move_pattern == MV_TURNING_LEFT_RIGHT ||
3821 move_pattern == MV_TURNING_RIGHT_LEFT ||
3822 move_pattern == MV_TURNING_RANDOM)
3823 MovDir[x][y] = 1 << RND(4);
3824 else if (move_pattern == MV_HORIZONTAL)
3825 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3826 else if (move_pattern == MV_VERTICAL)
3827 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3828 else if (move_pattern & MV_ANY_DIRECTION)
3829 MovDir[x][y] = element_info[element].move_pattern;
3830 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3831 move_pattern == MV_ALONG_RIGHT_SIDE)
3833 /* use random direction as default start direction */
3834 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3835 MovDir[x][y] = 1 << RND(4);
3837 for (i = 0; i < NUM_DIRECTIONS; i++)
3839 int x1 = x + xy[i][0];
3840 int y1 = y + xy[i][1];
3842 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3844 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3845 MovDir[x][y] = direction[0][i];
3847 MovDir[x][y] = direction[1][i];
3856 MovDir[x][y] = 1 << RND(4);
3858 if (element != EL_BUG &&
3859 element != EL_SPACESHIP &&
3860 element != EL_BD_BUTTERFLY &&
3861 element != EL_BD_FIREFLY)
3864 for (i = 0; i < NUM_DIRECTIONS; i++)
3866 int x1 = x + xy[i][0];
3867 int y1 = y + xy[i][1];
3869 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3871 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3873 MovDir[x][y] = direction[0][i];
3876 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3877 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3879 MovDir[x][y] = direction[1][i];
3888 GfxDir[x][y] = MovDir[x][y];
3891 void InitAmoebaNr(int x, int y)
3894 int group_nr = AmoebeNachbarNr(x, y);
3898 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3900 if (AmoebaCnt[i] == 0)
3908 AmoebaNr[x][y] = group_nr;
3909 AmoebaCnt[group_nr]++;
3910 AmoebaCnt2[group_nr]++;
3913 static void PlayerWins(struct PlayerInfo *player)
3915 player->LevelSolved = TRUE;
3916 player->GameOver = TRUE;
3918 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3919 level.native_em_level->lev->score : player->score);
3924 static int time, time_final;
3925 static int score, score_final;
3926 static int game_over_delay_1 = 0;
3927 static int game_over_delay_2 = 0;
3928 int game_over_delay_value_1 = 50;
3929 int game_over_delay_value_2 = 50;
3931 if (!local_player->LevelSolved_GameWon)
3935 /* do not start end game actions before the player stops moving (to exit) */
3936 if (local_player->MovPos)
3939 local_player->LevelSolved_GameWon = TRUE;
3940 local_player->LevelSolved_SaveTape = tape.recording;
3941 local_player->LevelSolved_SaveScore = !tape.playing;
3943 if (tape.auto_play) /* tape might already be stopped here */
3944 tape.auto_play_level_solved = TRUE;
3950 game_over_delay_1 = game_over_delay_value_1;
3951 game_over_delay_2 = game_over_delay_value_2;
3953 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3954 score = score_final = local_player->score_final;
3959 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3961 else if (level.time == 0 && TimePlayed < 999)
3964 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3967 local_player->score_final = score_final;
3969 if (level_editor_test_game)
3972 score = score_final;
3975 game_control_value[GAME_CONTROL_TIME] = time;
3976 game_control_value[GAME_CONTROL_SCORE] = score;
3978 DisplayGameControlValues();
3980 DrawGameValue_Time(time);
3981 DrawGameValue_Score(score);
3985 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3987 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3989 /* close exit door after last player */
3990 if ((AllPlayersGone &&
3991 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3992 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3993 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3994 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3995 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3997 int element = Feld[ExitX][ExitY];
4000 if (element == EL_EM_EXIT_OPEN ||
4001 element == EL_EM_STEEL_EXIT_OPEN)
4008 Feld[ExitX][ExitY] =
4009 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4010 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4011 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4012 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4013 EL_EM_STEEL_EXIT_CLOSING);
4015 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4019 /* player disappears */
4020 DrawLevelField(ExitX, ExitY);
4023 for (i = 0; i < MAX_PLAYERS; i++)
4025 struct PlayerInfo *player = &stored_player[i];
4027 if (player->present)
4029 RemovePlayer(player);
4031 /* player disappears */
4032 DrawLevelField(player->jx, player->jy);
4037 PlaySound(SND_GAME_WINNING);
4040 if (game_over_delay_1 > 0)
4042 game_over_delay_1--;
4047 if (time != time_final)
4049 int time_to_go = ABS(time_final - time);
4050 int time_count_dir = (time < time_final ? +1 : -1);
4051 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4053 time += time_count_steps * time_count_dir;
4054 score += time_count_steps * level.score[SC_TIME_BONUS];
4057 game_control_value[GAME_CONTROL_TIME] = time;
4058 game_control_value[GAME_CONTROL_SCORE] = score;
4060 DisplayGameControlValues();
4062 DrawGameValue_Time(time);
4063 DrawGameValue_Score(score);
4066 if (time == time_final)
4067 StopSound(SND_GAME_LEVELTIME_BONUS);
4068 else if (setup.sound_loops)
4069 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4071 PlaySound(SND_GAME_LEVELTIME_BONUS);
4076 local_player->LevelSolved_PanelOff = TRUE;
4078 if (game_over_delay_2 > 0)
4080 game_over_delay_2--;
4093 boolean raise_level = FALSE;
4095 local_player->LevelSolved_GameEnd = TRUE;
4097 CloseDoor(DOOR_CLOSE_1);
4099 if (local_player->LevelSolved_SaveTape)
4106 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4108 SaveTape(tape.level_nr); /* ask to save tape */
4112 if (level_editor_test_game)
4114 game_status = GAME_MODE_MAIN;
4117 DrawAndFadeInMainMenu(REDRAW_FIELD);
4125 if (!local_player->LevelSolved_SaveScore)
4128 FadeOut(REDRAW_FIELD);
4131 game_status = GAME_MODE_MAIN;
4133 DrawAndFadeInMainMenu(REDRAW_FIELD);
4138 if (level_nr == leveldir_current->handicap_level)
4140 leveldir_current->handicap_level++;
4141 SaveLevelSetup_SeriesInfo();
4144 if (level_nr < leveldir_current->last_level)
4145 raise_level = TRUE; /* advance to next level */
4147 if ((hi_pos = NewHiScore()) >= 0)
4149 game_status = GAME_MODE_SCORES;
4151 DrawHallOfFame(hi_pos);
4162 FadeOut(REDRAW_FIELD);
4165 game_status = GAME_MODE_MAIN;
4173 DrawAndFadeInMainMenu(REDRAW_FIELD);
4182 LoadScore(level_nr);
4184 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4185 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4188 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4190 if (local_player->score_final > highscore[k].Score)
4192 /* player has made it to the hall of fame */
4194 if (k < MAX_SCORE_ENTRIES - 1)
4196 int m = MAX_SCORE_ENTRIES - 1;
4199 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4200 if (strEqual(setup.player_name, highscore[l].Name))
4202 if (m == k) /* player's new highscore overwrites his old one */
4206 for (l = m; l > k; l--)
4208 strcpy(highscore[l].Name, highscore[l - 1].Name);
4209 highscore[l].Score = highscore[l - 1].Score;
4216 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4217 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4218 highscore[k].Score = local_player->score_final;
4224 else if (!strncmp(setup.player_name, highscore[k].Name,
4225 MAX_PLAYER_NAME_LEN))
4226 break; /* player already there with a higher score */
4232 SaveScore(level_nr);
4237 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4239 int element = Feld[x][y];
4240 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4241 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4242 int horiz_move = (dx != 0);
4243 int sign = (horiz_move ? dx : dy);
4244 int step = sign * element_info[element].move_stepsize;
4246 /* special values for move stepsize for spring and things on conveyor belt */
4249 if (CAN_FALL(element) &&
4250 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4251 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4252 else if (element == EL_SPRING)
4253 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4259 inline static int getElementMoveStepsize(int x, int y)
4261 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4264 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4266 if (player->GfxAction != action || player->GfxDir != dir)
4269 printf("Player frame reset! (%d => %d, %d => %d)\n",
4270 player->GfxAction, action, player->GfxDir, dir);
4273 player->GfxAction = action;
4274 player->GfxDir = dir;
4276 player->StepFrame = 0;
4280 #if USE_GFX_RESET_GFX_ANIMATION
4281 static void ResetGfxFrame(int x, int y, boolean redraw)
4283 int element = Feld[x][y];
4284 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4285 int last_gfx_frame = GfxFrame[x][y];
4287 if (graphic_info[graphic].anim_global_sync)
4288 GfxFrame[x][y] = FrameCounter;
4289 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4290 GfxFrame[x][y] = CustomValue[x][y];
4291 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4292 GfxFrame[x][y] = element_info[element].collect_score;
4293 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4294 GfxFrame[x][y] = ChangeDelay[x][y];
4296 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4297 DrawLevelGraphicAnimation(x, y, graphic);
4301 static void ResetGfxAnimation(int x, int y)
4303 GfxAction[x][y] = ACTION_DEFAULT;
4304 GfxDir[x][y] = MovDir[x][y];
4307 #if USE_GFX_RESET_GFX_ANIMATION
4308 ResetGfxFrame(x, y, FALSE);
4312 static void ResetRandomAnimationValue(int x, int y)
4314 GfxRandom[x][y] = INIT_GFX_RANDOM();
4317 void InitMovingField(int x, int y, int direction)
4319 int element = Feld[x][y];
4320 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4321 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4324 boolean is_moving_before, is_moving_after;
4326 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4329 /* check if element was/is moving or being moved before/after mode change */
4332 is_moving_before = (WasJustMoving[x][y] != 0);
4334 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4335 is_moving_before = WasJustMoving[x][y];
4338 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4340 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4342 /* reset animation only for moving elements which change direction of moving
4343 or which just started or stopped moving
4344 (else CEs with property "can move" / "not moving" are reset each frame) */
4345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4347 if (is_moving_before != is_moving_after ||
4348 direction != MovDir[x][y])
4349 ResetGfxAnimation(x, y);
4351 if ((is_moving_before || is_moving_after) && !continues_moving)
4352 ResetGfxAnimation(x, y);
4355 if (!continues_moving)
4356 ResetGfxAnimation(x, y);
4359 MovDir[x][y] = direction;
4360 GfxDir[x][y] = direction;
4362 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4363 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4364 direction == MV_DOWN && CAN_FALL(element) ?
4365 ACTION_FALLING : ACTION_MOVING);
4367 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4368 ACTION_FALLING : ACTION_MOVING);
4371 /* this is needed for CEs with property "can move" / "not moving" */
4373 if (is_moving_after)
4375 if (Feld[newx][newy] == EL_EMPTY)
4376 Feld[newx][newy] = EL_BLOCKED;
4378 MovDir[newx][newy] = MovDir[x][y];
4380 #if USE_NEW_CUSTOM_VALUE
4381 CustomValue[newx][newy] = CustomValue[x][y];
4384 GfxFrame[newx][newy] = GfxFrame[x][y];
4385 GfxRandom[newx][newy] = GfxRandom[x][y];
4386 GfxAction[newx][newy] = GfxAction[x][y];
4387 GfxDir[newx][newy] = GfxDir[x][y];
4391 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4393 int direction = MovDir[x][y];
4394 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4395 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4401 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4403 int oldx = x, oldy = y;
4404 int direction = MovDir[x][y];
4406 if (direction == MV_LEFT)
4408 else if (direction == MV_RIGHT)
4410 else if (direction == MV_UP)
4412 else if (direction == MV_DOWN)
4415 *comes_from_x = oldx;
4416 *comes_from_y = oldy;
4419 int MovingOrBlocked2Element(int x, int y)
4421 int element = Feld[x][y];
4423 if (element == EL_BLOCKED)
4427 Blocked2Moving(x, y, &oldx, &oldy);
4428 return Feld[oldx][oldy];
4434 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4436 /* like MovingOrBlocked2Element(), but if element is moving
4437 and (x,y) is the field the moving element is just leaving,
4438 return EL_BLOCKED instead of the element value */
4439 int element = Feld[x][y];
4441 if (IS_MOVING(x, y))
4443 if (element == EL_BLOCKED)
4447 Blocked2Moving(x, y, &oldx, &oldy);
4448 return Feld[oldx][oldy];
4457 static void RemoveField(int x, int y)
4459 Feld[x][y] = EL_EMPTY;
4465 #if USE_NEW_CUSTOM_VALUE
4466 CustomValue[x][y] = 0;
4470 ChangeDelay[x][y] = 0;
4471 ChangePage[x][y] = -1;
4472 Pushed[x][y] = FALSE;
4475 ExplodeField[x][y] = EX_TYPE_NONE;
4478 GfxElement[x][y] = EL_UNDEFINED;
4479 GfxAction[x][y] = ACTION_DEFAULT;
4480 GfxDir[x][y] = MV_NONE;
4483 void RemoveMovingField(int x, int y)
4485 int oldx = x, oldy = y, newx = x, newy = y;
4486 int element = Feld[x][y];
4487 int next_element = EL_UNDEFINED;
4489 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4492 if (IS_MOVING(x, y))
4494 Moving2Blocked(x, y, &newx, &newy);
4496 if (Feld[newx][newy] != EL_BLOCKED)
4498 /* element is moving, but target field is not free (blocked), but
4499 already occupied by something different (example: acid pool);
4500 in this case, only remove the moving field, but not the target */
4502 RemoveField(oldx, oldy);
4504 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4506 DrawLevelField(oldx, oldy);
4511 else if (element == EL_BLOCKED)
4513 Blocked2Moving(x, y, &oldx, &oldy);
4514 if (!IS_MOVING(oldx, oldy))
4518 if (element == EL_BLOCKED &&
4519 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4520 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4521 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4522 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4523 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4524 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4525 next_element = get_next_element(Feld[oldx][oldy]);
4527 RemoveField(oldx, oldy);
4528 RemoveField(newx, newy);
4530 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4532 if (next_element != EL_UNDEFINED)
4533 Feld[oldx][oldy] = next_element;
4535 DrawLevelField(oldx, oldy);
4536 DrawLevelField(newx, newy);
4539 void DrawDynamite(int x, int y)
4541 int sx = SCREENX(x), sy = SCREENY(y);
4542 int graphic = el2img(Feld[x][y]);
4545 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4548 if (IS_WALKABLE_INSIDE(Back[x][y]))
4552 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4553 else if (Store[x][y])
4554 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4556 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4558 if (Back[x][y] || Store[x][y])
4559 DrawGraphicThruMask(sx, sy, graphic, frame);
4561 DrawGraphic(sx, sy, graphic, frame);
4564 void CheckDynamite(int x, int y)
4566 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4570 if (MovDelay[x][y] != 0)
4573 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4579 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4584 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4586 boolean num_checked_players = 0;
4589 for (i = 0; i < MAX_PLAYERS; i++)
4591 if (stored_player[i].active)
4593 int sx = stored_player[i].jx;
4594 int sy = stored_player[i].jy;
4596 if (num_checked_players == 0)
4603 *sx1 = MIN(*sx1, sx);
4604 *sy1 = MIN(*sy1, sy);
4605 *sx2 = MAX(*sx2, sx);
4606 *sy2 = MAX(*sy2, sy);
4609 num_checked_players++;
4614 static boolean checkIfAllPlayersFitToScreen_RND()
4616 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4618 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4620 return (sx2 - sx1 < SCR_FIELDX &&
4621 sy2 - sy1 < SCR_FIELDY);
4624 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4626 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4628 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4630 *sx = (sx1 + sx2) / 2;
4631 *sy = (sy1 + sy2) / 2;
4634 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4635 boolean center_screen, boolean quick_relocation)
4637 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4638 boolean no_delay = (tape.warp_forward);
4639 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4640 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4642 if (quick_relocation)
4644 int offset = game.scroll_delay_value;
4646 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4648 if (!level.shifted_relocation || center_screen)
4650 /* quick relocation (without scrolling), with centering of screen */
4652 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4653 x > SBX_Right + MIDPOSX ? SBX_Right :
4656 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4657 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4662 /* quick relocation (without scrolling), but do not center screen */
4664 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4665 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4668 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4669 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4672 int offset_x = x + (scroll_x - center_scroll_x);
4673 int offset_y = y + (scroll_y - center_scroll_y);
4675 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4676 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4677 offset_x - MIDPOSX);
4679 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4680 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4681 offset_y - MIDPOSY);
4686 /* quick relocation (without scrolling), inside visible screen area */
4688 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4689 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4690 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4692 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4693 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4694 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4696 /* don't scroll over playfield boundaries */
4697 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4698 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4700 /* don't scroll over playfield boundaries */
4701 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4702 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4705 RedrawPlayfield(TRUE, 0,0,0,0);
4710 int scroll_xx, scroll_yy;
4712 if (!level.shifted_relocation || center_screen)
4714 /* visible relocation (with scrolling), with centering of screen */
4716 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4717 x > SBX_Right + MIDPOSX ? SBX_Right :
4720 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4721 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4726 /* visible relocation (with scrolling), but do not center screen */
4728 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4729 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4732 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4733 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4736 int offset_x = x + (scroll_x - center_scroll_x);
4737 int offset_y = y + (scroll_y - center_scroll_y);
4739 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4740 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4741 offset_x - MIDPOSX);
4743 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4744 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4745 offset_y - MIDPOSY);
4750 /* visible relocation (with scrolling), with centering of screen */
4752 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4753 x > SBX_Right + MIDPOSX ? SBX_Right :
4756 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4757 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4761 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4763 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4766 int fx = FX, fy = FY;
4768 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4769 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4771 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4777 fx += dx * TILEX / 2;
4778 fy += dy * TILEY / 2;
4780 ScrollLevel(dx, dy);
4783 /* scroll in two steps of half tile size to make things smoother */
4784 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4786 Delay(wait_delay_value);
4788 /* scroll second step to align at full tile size */
4790 Delay(wait_delay_value);
4795 Delay(wait_delay_value);
4799 void RelocatePlayer(int jx, int jy, int el_player_raw)
4801 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4802 int player_nr = GET_PLAYER_NR(el_player);
4803 struct PlayerInfo *player = &stored_player[player_nr];
4804 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4805 boolean no_delay = (tape.warp_forward);
4806 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4807 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4808 int old_jx = player->jx;
4809 int old_jy = player->jy;
4810 int old_element = Feld[old_jx][old_jy];
4811 int element = Feld[jx][jy];
4812 boolean player_relocated = (old_jx != jx || old_jy != jy);
4814 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4815 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4816 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4817 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4818 int leave_side_horiz = move_dir_horiz;
4819 int leave_side_vert = move_dir_vert;
4820 int enter_side = enter_side_horiz | enter_side_vert;
4821 int leave_side = leave_side_horiz | leave_side_vert;
4823 if (player->GameOver) /* do not reanimate dead player */
4826 if (!player_relocated) /* no need to relocate the player */
4829 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4831 RemoveField(jx, jy); /* temporarily remove newly placed player */
4832 DrawLevelField(jx, jy);
4835 if (player->present)
4837 while (player->MovPos)
4839 ScrollPlayer(player, SCROLL_GO_ON);
4840 ScrollScreen(NULL, SCROLL_GO_ON);
4842 AdvanceFrameAndPlayerCounters(player->index_nr);
4847 Delay(wait_delay_value);
4850 DrawPlayer(player); /* needed here only to cleanup last field */
4851 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4853 player->is_moving = FALSE;
4856 if (IS_CUSTOM_ELEMENT(old_element))
4857 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4859 player->index_bit, leave_side);
4861 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4863 player->index_bit, leave_side);
4865 Feld[jx][jy] = el_player;
4866 InitPlayerField(jx, jy, el_player, TRUE);
4868 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4870 Feld[jx][jy] = element;
4871 InitField(jx, jy, FALSE);
4874 /* only visually relocate centered player */
4875 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4876 FALSE, level.instant_relocation);
4878 TestIfPlayerTouchesBadThing(jx, jy);
4879 TestIfPlayerTouchesCustomElement(jx, jy);
4881 if (IS_CUSTOM_ELEMENT(element))
4882 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4883 player->index_bit, enter_side);
4885 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4886 player->index_bit, enter_side);
4889 void Explode(int ex, int ey, int phase, int mode)
4895 /* !!! eliminate this variable !!! */
4896 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4898 if (game.explosions_delayed)
4900 ExplodeField[ex][ey] = mode;
4904 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4906 int center_element = Feld[ex][ey];
4907 int artwork_element, explosion_element; /* set these values later */
4910 /* --- This is only really needed (and now handled) in "Impact()". --- */
4911 /* do not explode moving elements that left the explode field in time */
4912 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4913 center_element == EL_EMPTY &&
4914 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4919 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4920 if (mode == EX_TYPE_NORMAL ||
4921 mode == EX_TYPE_CENTER ||
4922 mode == EX_TYPE_CROSS)
4923 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4926 /* remove things displayed in background while burning dynamite */
4927 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4930 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4932 /* put moving element to center field (and let it explode there) */
4933 center_element = MovingOrBlocked2Element(ex, ey);
4934 RemoveMovingField(ex, ey);
4935 Feld[ex][ey] = center_element;
4938 /* now "center_element" is finally determined -- set related values now */
4939 artwork_element = center_element; /* for custom player artwork */
4940 explosion_element = center_element; /* for custom player artwork */
4942 if (IS_PLAYER(ex, ey))
4944 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4946 artwork_element = stored_player[player_nr].artwork_element;
4948 if (level.use_explosion_element[player_nr])
4950 explosion_element = level.explosion_element[player_nr];
4951 artwork_element = explosion_element;
4956 if (mode == EX_TYPE_NORMAL ||
4957 mode == EX_TYPE_CENTER ||
4958 mode == EX_TYPE_CROSS)
4959 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4962 last_phase = element_info[explosion_element].explosion_delay + 1;
4964 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4966 int xx = x - ex + 1;
4967 int yy = y - ey + 1;
4970 if (!IN_LEV_FIELD(x, y) ||
4971 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4972 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4975 element = Feld[x][y];
4977 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4979 element = MovingOrBlocked2Element(x, y);
4981 if (!IS_EXPLOSION_PROOF(element))
4982 RemoveMovingField(x, y);
4985 /* indestructible elements can only explode in center (but not flames) */
4986 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4987 mode == EX_TYPE_BORDER)) ||
4988 element == EL_FLAMES)
4991 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4992 behaviour, for example when touching a yamyam that explodes to rocks
4993 with active deadly shield, a rock is created under the player !!! */
4994 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4996 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4997 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4998 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5000 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5003 if (IS_ACTIVE_BOMB(element))
5005 /* re-activate things under the bomb like gate or penguin */
5006 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5013 /* save walkable background elements while explosion on same tile */
5014 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5015 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5016 Back[x][y] = element;
5018 /* ignite explodable elements reached by other explosion */
5019 if (element == EL_EXPLOSION)
5020 element = Store2[x][y];
5022 if (AmoebaNr[x][y] &&
5023 (element == EL_AMOEBA_FULL ||
5024 element == EL_BD_AMOEBA ||
5025 element == EL_AMOEBA_GROWING))
5027 AmoebaCnt[AmoebaNr[x][y]]--;
5028 AmoebaCnt2[AmoebaNr[x][y]]--;
5033 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5035 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5037 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5039 if (PLAYERINFO(ex, ey)->use_murphy)
5040 Store[x][y] = EL_EMPTY;
5043 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5044 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5045 else if (ELEM_IS_PLAYER(center_element))
5046 Store[x][y] = EL_EMPTY;
5047 else if (center_element == EL_YAMYAM)
5048 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5049 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5050 Store[x][y] = element_info[center_element].content.e[xx][yy];
5052 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5053 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5054 otherwise) -- FIX THIS !!! */
5055 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5056 Store[x][y] = element_info[element].content.e[1][1];
5058 else if (!CAN_EXPLODE(element))
5059 Store[x][y] = element_info[element].content.e[1][1];
5062 Store[x][y] = EL_EMPTY;
5064 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5065 center_element == EL_AMOEBA_TO_DIAMOND)
5066 Store2[x][y] = element;
5068 Feld[x][y] = EL_EXPLOSION;
5069 GfxElement[x][y] = artwork_element;
5071 ExplodePhase[x][y] = 1;
5072 ExplodeDelay[x][y] = last_phase;
5077 if (center_element == EL_YAMYAM)
5078 game.yamyam_content_nr =
5079 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5091 GfxFrame[x][y] = 0; /* restart explosion animation */
5093 last_phase = ExplodeDelay[x][y];
5095 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5099 /* activate this even in non-DEBUG version until cause for crash in
5100 getGraphicAnimationFrame() (see below) is found and eliminated */
5106 /* this can happen if the player leaves an explosion just in time */
5107 if (GfxElement[x][y] == EL_UNDEFINED)
5108 GfxElement[x][y] = EL_EMPTY;
5110 if (GfxElement[x][y] == EL_UNDEFINED)
5113 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5114 printf("Explode(): This should never happen!\n");
5117 GfxElement[x][y] = EL_EMPTY;
5123 border_element = Store2[x][y];
5124 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5125 border_element = StorePlayer[x][y];
5127 if (phase == element_info[border_element].ignition_delay ||
5128 phase == last_phase)
5130 boolean border_explosion = FALSE;
5132 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5133 !PLAYER_EXPLOSION_PROTECTED(x, y))
5135 KillPlayerUnlessExplosionProtected(x, y);
5136 border_explosion = TRUE;
5138 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5140 Feld[x][y] = Store2[x][y];
5143 border_explosion = TRUE;
5145 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5147 AmoebeUmwandeln(x, y);
5149 border_explosion = TRUE;
5152 /* if an element just explodes due to another explosion (chain-reaction),
5153 do not immediately end the new explosion when it was the last frame of
5154 the explosion (as it would be done in the following "if"-statement!) */
5155 if (border_explosion && phase == last_phase)
5159 if (phase == last_phase)
5163 element = Feld[x][y] = Store[x][y];
5164 Store[x][y] = Store2[x][y] = 0;
5165 GfxElement[x][y] = EL_UNDEFINED;
5167 /* player can escape from explosions and might therefore be still alive */
5168 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5169 element <= EL_PLAYER_IS_EXPLODING_4)
5171 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5172 int explosion_element = EL_PLAYER_1 + player_nr;
5173 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5174 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5176 if (level.use_explosion_element[player_nr])
5177 explosion_element = level.explosion_element[player_nr];
5179 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5180 element_info[explosion_element].content.e[xx][yy]);
5183 /* restore probably existing indestructible background element */
5184 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5185 element = Feld[x][y] = Back[x][y];
5188 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5189 GfxDir[x][y] = MV_NONE;
5190 ChangeDelay[x][y] = 0;
5191 ChangePage[x][y] = -1;
5193 #if USE_NEW_CUSTOM_VALUE
5194 CustomValue[x][y] = 0;
5197 InitField_WithBug2(x, y, FALSE);
5199 DrawLevelField(x, y);
5201 TestIfElementTouchesCustomElement(x, y);
5203 if (GFX_CRUMBLED(element))
5204 DrawLevelFieldCrumbledSandNeighbours(x, y);
5206 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5207 StorePlayer[x][y] = 0;
5209 if (ELEM_IS_PLAYER(element))
5210 RelocatePlayer(x, y, element);
5212 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5214 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5215 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5218 DrawLevelFieldCrumbledSand(x, y);
5220 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5222 DrawLevelElement(x, y, Back[x][y]);
5223 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5225 else if (IS_WALKABLE_UNDER(Back[x][y]))
5227 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5228 DrawLevelElementThruMask(x, y, Back[x][y]);
5230 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5231 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5235 void DynaExplode(int ex, int ey)
5238 int dynabomb_element = Feld[ex][ey];
5239 int dynabomb_size = 1;
5240 boolean dynabomb_xl = FALSE;
5241 struct PlayerInfo *player;
5242 static int xy[4][2] =
5250 if (IS_ACTIVE_BOMB(dynabomb_element))
5252 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5253 dynabomb_size = player->dynabomb_size;
5254 dynabomb_xl = player->dynabomb_xl;
5255 player->dynabombs_left++;
5258 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5260 for (i = 0; i < NUM_DIRECTIONS; i++)
5262 for (j = 1; j <= dynabomb_size; j++)
5264 int x = ex + j * xy[i][0];
5265 int y = ey + j * xy[i][1];
5268 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5271 element = Feld[x][y];
5273 /* do not restart explosions of fields with active bombs */
5274 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5277 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5279 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5280 !IS_DIGGABLE(element) && !dynabomb_xl)
5286 void Bang(int x, int y)
5288 int element = MovingOrBlocked2Element(x, y);
5289 int explosion_type = EX_TYPE_NORMAL;
5291 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5293 struct PlayerInfo *player = PLAYERINFO(x, y);
5295 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5296 player->element_nr);
5298 if (level.use_explosion_element[player->index_nr])
5300 int explosion_element = level.explosion_element[player->index_nr];
5302 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5303 explosion_type = EX_TYPE_CROSS;
5304 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5305 explosion_type = EX_TYPE_CENTER;
5313 case EL_BD_BUTTERFLY:
5316 case EL_DARK_YAMYAM:
5320 RaiseScoreElement(element);
5323 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5324 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5325 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5326 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5327 case EL_DYNABOMB_INCREASE_NUMBER:
5328 case EL_DYNABOMB_INCREASE_SIZE:
5329 case EL_DYNABOMB_INCREASE_POWER:
5330 explosion_type = EX_TYPE_DYNA;
5333 case EL_DC_LANDMINE:
5335 case EL_EM_EXIT_OPEN:
5336 case EL_EM_STEEL_EXIT_OPEN:
5338 explosion_type = EX_TYPE_CENTER;
5343 case EL_LAMP_ACTIVE:
5344 case EL_AMOEBA_TO_DIAMOND:
5345 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5346 explosion_type = EX_TYPE_CENTER;
5350 if (element_info[element].explosion_type == EXPLODES_CROSS)
5351 explosion_type = EX_TYPE_CROSS;
5352 else if (element_info[element].explosion_type == EXPLODES_1X1)
5353 explosion_type = EX_TYPE_CENTER;
5357 if (explosion_type == EX_TYPE_DYNA)
5360 Explode(x, y, EX_PHASE_START, explosion_type);
5362 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5365 void SplashAcid(int x, int y)
5367 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5368 (!IN_LEV_FIELD(x - 1, y - 2) ||
5369 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5370 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5372 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5373 (!IN_LEV_FIELD(x + 1, y - 2) ||
5374 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5375 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5377 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5380 static void InitBeltMovement()
5382 static int belt_base_element[4] =
5384 EL_CONVEYOR_BELT_1_LEFT,
5385 EL_CONVEYOR_BELT_2_LEFT,
5386 EL_CONVEYOR_BELT_3_LEFT,
5387 EL_CONVEYOR_BELT_4_LEFT
5389 static int belt_base_active_element[4] =
5391 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5392 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5393 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5394 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5399 /* set frame order for belt animation graphic according to belt direction */
5400 for (i = 0; i < NUM_BELTS; i++)
5404 for (j = 0; j < NUM_BELT_PARTS; j++)
5406 int element = belt_base_active_element[belt_nr] + j;
5407 int graphic = el2img(element);
5409 if (game.belt_dir[i] == MV_LEFT)
5410 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5412 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5416 SCAN_PLAYFIELD(x, y)
5418 int element = Feld[x][y];
5420 for (i = 0; i < NUM_BELTS; i++)
5422 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5424 int e_belt_nr = getBeltNrFromBeltElement(element);
5427 if (e_belt_nr == belt_nr)
5429 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5431 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5438 static void ToggleBeltSwitch(int x, int y)
5440 static int belt_base_element[4] =
5442 EL_CONVEYOR_BELT_1_LEFT,
5443 EL_CONVEYOR_BELT_2_LEFT,
5444 EL_CONVEYOR_BELT_3_LEFT,
5445 EL_CONVEYOR_BELT_4_LEFT
5447 static int belt_base_active_element[4] =
5449 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5450 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5451 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5452 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5454 static int belt_base_switch_element[4] =
5456 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5457 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5458 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5459 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5461 static int belt_move_dir[4] =
5469 int element = Feld[x][y];
5470 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5471 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5472 int belt_dir = belt_move_dir[belt_dir_nr];
5475 if (!IS_BELT_SWITCH(element))
5478 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5479 game.belt_dir[belt_nr] = belt_dir;
5481 if (belt_dir_nr == 3)
5484 /* set frame order for belt animation graphic according to belt direction */
5485 for (i = 0; i < NUM_BELT_PARTS; i++)
5487 int element = belt_base_active_element[belt_nr] + i;
5488 int graphic = el2img(element);
5490 if (belt_dir == MV_LEFT)
5491 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5493 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5496 SCAN_PLAYFIELD(xx, yy)
5498 int element = Feld[xx][yy];
5500 if (IS_BELT_SWITCH(element))
5502 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5504 if (e_belt_nr == belt_nr)
5506 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5507 DrawLevelField(xx, yy);
5510 else if (IS_BELT(element) && belt_dir != MV_NONE)
5512 int e_belt_nr = getBeltNrFromBeltElement(element);
5514 if (e_belt_nr == belt_nr)
5516 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5518 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5519 DrawLevelField(xx, yy);
5522 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5524 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5526 if (e_belt_nr == belt_nr)
5528 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5530 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5531 DrawLevelField(xx, yy);
5537 static void ToggleSwitchgateSwitch(int x, int y)
5541 game.switchgate_pos = !game.switchgate_pos;
5543 SCAN_PLAYFIELD(xx, yy)
5545 int element = Feld[xx][yy];
5547 #if !USE_BOTH_SWITCHGATE_SWITCHES
5548 if (element == EL_SWITCHGATE_SWITCH_UP ||
5549 element == EL_SWITCHGATE_SWITCH_DOWN)
5551 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5552 DrawLevelField(xx, yy);
5554 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5555 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5557 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5558 DrawLevelField(xx, yy);
5561 if (element == EL_SWITCHGATE_SWITCH_UP)
5563 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5564 DrawLevelField(xx, yy);
5566 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5568 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5569 DrawLevelField(xx, yy);
5571 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5573 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5574 DrawLevelField(xx, yy);
5576 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5578 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5579 DrawLevelField(xx, yy);
5582 else if (element == EL_SWITCHGATE_OPEN ||
5583 element == EL_SWITCHGATE_OPENING)
5585 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5587 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5589 else if (element == EL_SWITCHGATE_CLOSED ||
5590 element == EL_SWITCHGATE_CLOSING)
5592 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5594 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5599 static int getInvisibleActiveFromInvisibleElement(int element)
5601 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5602 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5603 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5607 static int getInvisibleFromInvisibleActiveElement(int element)
5609 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5610 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5611 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5615 static void RedrawAllLightSwitchesAndInvisibleElements()
5619 SCAN_PLAYFIELD(x, y)
5621 int element = Feld[x][y];
5623 if (element == EL_LIGHT_SWITCH &&
5624 game.light_time_left > 0)
5626 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5627 DrawLevelField(x, y);
5629 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5630 game.light_time_left == 0)
5632 Feld[x][y] = EL_LIGHT_SWITCH;
5633 DrawLevelField(x, y);
5635 else if (element == EL_EMC_DRIPPER &&
5636 game.light_time_left > 0)
5638 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5639 DrawLevelField(x, y);
5641 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5642 game.light_time_left == 0)
5644 Feld[x][y] = EL_EMC_DRIPPER;
5645 DrawLevelField(x, y);
5647 else if (element == EL_INVISIBLE_STEELWALL ||
5648 element == EL_INVISIBLE_WALL ||
5649 element == EL_INVISIBLE_SAND)
5651 if (game.light_time_left > 0)
5652 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5654 DrawLevelField(x, y);
5656 /* uncrumble neighbour fields, if needed */
5657 if (element == EL_INVISIBLE_SAND)
5658 DrawLevelFieldCrumbledSandNeighbours(x, y);
5660 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5661 element == EL_INVISIBLE_WALL_ACTIVE ||
5662 element == EL_INVISIBLE_SAND_ACTIVE)
5664 if (game.light_time_left == 0)
5665 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5667 DrawLevelField(x, y);
5669 /* re-crumble neighbour fields, if needed */
5670 if (element == EL_INVISIBLE_SAND)
5671 DrawLevelFieldCrumbledSandNeighbours(x, y);
5676 static void RedrawAllInvisibleElementsForLenses()
5680 SCAN_PLAYFIELD(x, y)
5682 int element = Feld[x][y];
5684 if (element == EL_EMC_DRIPPER &&
5685 game.lenses_time_left > 0)
5687 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5688 DrawLevelField(x, y);
5690 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5691 game.lenses_time_left == 0)
5693 Feld[x][y] = EL_EMC_DRIPPER;
5694 DrawLevelField(x, y);
5696 else if (element == EL_INVISIBLE_STEELWALL ||
5697 element == EL_INVISIBLE_WALL ||
5698 element == EL_INVISIBLE_SAND)
5700 if (game.lenses_time_left > 0)
5701 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5703 DrawLevelField(x, y);
5705 /* uncrumble neighbour fields, if needed */
5706 if (element == EL_INVISIBLE_SAND)
5707 DrawLevelFieldCrumbledSandNeighbours(x, y);
5709 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5710 element == EL_INVISIBLE_WALL_ACTIVE ||
5711 element == EL_INVISIBLE_SAND_ACTIVE)
5713 if (game.lenses_time_left == 0)
5714 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5716 DrawLevelField(x, y);
5718 /* re-crumble neighbour fields, if needed */
5719 if (element == EL_INVISIBLE_SAND)
5720 DrawLevelFieldCrumbledSandNeighbours(x, y);
5725 static void RedrawAllInvisibleElementsForMagnifier()
5729 SCAN_PLAYFIELD(x, y)
5731 int element = Feld[x][y];
5733 if (element == EL_EMC_FAKE_GRASS &&
5734 game.magnify_time_left > 0)
5736 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5737 DrawLevelField(x, y);
5739 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5740 game.magnify_time_left == 0)
5742 Feld[x][y] = EL_EMC_FAKE_GRASS;
5743 DrawLevelField(x, y);
5745 else if (IS_GATE_GRAY(element) &&
5746 game.magnify_time_left > 0)
5748 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5749 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5750 IS_EM_GATE_GRAY(element) ?
5751 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5752 IS_EMC_GATE_GRAY(element) ?
5753 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5755 DrawLevelField(x, y);
5757 else if (IS_GATE_GRAY_ACTIVE(element) &&
5758 game.magnify_time_left == 0)
5760 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5761 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5762 IS_EM_GATE_GRAY_ACTIVE(element) ?
5763 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5764 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5765 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5767 DrawLevelField(x, y);
5772 static void ToggleLightSwitch(int x, int y)
5774 int element = Feld[x][y];
5776 game.light_time_left =
5777 (element == EL_LIGHT_SWITCH ?
5778 level.time_light * FRAMES_PER_SECOND : 0);
5780 RedrawAllLightSwitchesAndInvisibleElements();
5783 static void ActivateTimegateSwitch(int x, int y)
5787 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5789 SCAN_PLAYFIELD(xx, yy)
5791 int element = Feld[xx][yy];
5793 if (element == EL_TIMEGATE_CLOSED ||
5794 element == EL_TIMEGATE_CLOSING)
5796 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5797 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5801 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5803 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5804 DrawLevelField(xx, yy);
5811 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5812 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5814 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5818 void Impact(int x, int y)
5820 boolean last_line = (y == lev_fieldy - 1);
5821 boolean object_hit = FALSE;
5822 boolean impact = (last_line || object_hit);
5823 int element = Feld[x][y];
5824 int smashed = EL_STEELWALL;
5826 if (!last_line) /* check if element below was hit */
5828 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5831 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5832 MovDir[x][y + 1] != MV_DOWN ||
5833 MovPos[x][y + 1] <= TILEY / 2));
5835 /* do not smash moving elements that left the smashed field in time */
5836 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5837 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5840 #if USE_QUICKSAND_IMPACT_BUGFIX
5841 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5843 RemoveMovingField(x, y + 1);
5844 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5845 Feld[x][y + 2] = EL_ROCK;
5846 DrawLevelField(x, y + 2);
5851 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5853 RemoveMovingField(x, y + 1);
5854 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5855 Feld[x][y + 2] = EL_ROCK;
5856 DrawLevelField(x, y + 2);
5863 smashed = MovingOrBlocked2Element(x, y + 1);
5865 impact = (last_line || object_hit);
5868 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5870 SplashAcid(x, y + 1);
5874 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5875 /* only reset graphic animation if graphic really changes after impact */
5877 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5879 ResetGfxAnimation(x, y);
5880 DrawLevelField(x, y);
5883 if (impact && CAN_EXPLODE_IMPACT(element))
5888 else if (impact && element == EL_PEARL &&
5889 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5891 ResetGfxAnimation(x, y);
5893 Feld[x][y] = EL_PEARL_BREAKING;
5894 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5897 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5899 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5904 if (impact && element == EL_AMOEBA_DROP)
5906 if (object_hit && IS_PLAYER(x, y + 1))
5907 KillPlayerUnlessEnemyProtected(x, y + 1);
5908 else if (object_hit && smashed == EL_PENGUIN)
5912 Feld[x][y] = EL_AMOEBA_GROWING;
5913 Store[x][y] = EL_AMOEBA_WET;
5915 ResetRandomAnimationValue(x, y);
5920 if (object_hit) /* check which object was hit */
5922 if ((CAN_PASS_MAGIC_WALL(element) &&
5923 (smashed == EL_MAGIC_WALL ||
5924 smashed == EL_BD_MAGIC_WALL)) ||
5925 (CAN_PASS_DC_MAGIC_WALL(element) &&
5926 smashed == EL_DC_MAGIC_WALL))
5929 int activated_magic_wall =
5930 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5931 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5932 EL_DC_MAGIC_WALL_ACTIVE);
5934 /* activate magic wall / mill */
5935 SCAN_PLAYFIELD(xx, yy)
5937 if (Feld[xx][yy] == smashed)
5938 Feld[xx][yy] = activated_magic_wall;
5941 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5942 game.magic_wall_active = TRUE;
5944 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5945 SND_MAGIC_WALL_ACTIVATING :
5946 smashed == EL_BD_MAGIC_WALL ?
5947 SND_BD_MAGIC_WALL_ACTIVATING :
5948 SND_DC_MAGIC_WALL_ACTIVATING));
5951 if (IS_PLAYER(x, y + 1))
5953 if (CAN_SMASH_PLAYER(element))
5955 KillPlayerUnlessEnemyProtected(x, y + 1);
5959 else if (smashed == EL_PENGUIN)
5961 if (CAN_SMASH_PLAYER(element))
5967 else if (element == EL_BD_DIAMOND)
5969 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5975 else if (((element == EL_SP_INFOTRON ||
5976 element == EL_SP_ZONK) &&
5977 (smashed == EL_SP_SNIKSNAK ||
5978 smashed == EL_SP_ELECTRON ||
5979 smashed == EL_SP_DISK_ORANGE)) ||
5980 (element == EL_SP_INFOTRON &&
5981 smashed == EL_SP_DISK_YELLOW))
5986 else if (CAN_SMASH_EVERYTHING(element))
5988 if (IS_CLASSIC_ENEMY(smashed) ||
5989 CAN_EXPLODE_SMASHED(smashed))
5994 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5996 if (smashed == EL_LAMP ||
5997 smashed == EL_LAMP_ACTIVE)
6002 else if (smashed == EL_NUT)
6004 Feld[x][y + 1] = EL_NUT_BREAKING;
6005 PlayLevelSound(x, y, SND_NUT_BREAKING);
6006 RaiseScoreElement(EL_NUT);
6009 else if (smashed == EL_PEARL)
6011 ResetGfxAnimation(x, y);
6013 Feld[x][y + 1] = EL_PEARL_BREAKING;
6014 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6017 else if (smashed == EL_DIAMOND)
6019 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6020 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6023 else if (IS_BELT_SWITCH(smashed))
6025 ToggleBeltSwitch(x, y + 1);
6027 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6028 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6029 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6030 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6032 ToggleSwitchgateSwitch(x, y + 1);
6034 else if (smashed == EL_LIGHT_SWITCH ||
6035 smashed == EL_LIGHT_SWITCH_ACTIVE)
6037 ToggleLightSwitch(x, y + 1);
6042 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6045 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6047 CheckElementChangeBySide(x, y + 1, smashed, element,
6048 CE_SWITCHED, CH_SIDE_TOP);
6049 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6055 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6060 /* play sound of magic wall / mill */
6062 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6063 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6064 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6066 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6067 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6068 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6069 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6070 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6071 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6076 /* play sound of object that hits the ground */
6077 if (last_line || object_hit)
6078 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6081 inline static void TurnRoundExt(int x, int y)
6093 { 0, 0 }, { 0, 0 }, { 0, 0 },
6098 int left, right, back;
6102 { MV_DOWN, MV_UP, MV_RIGHT },
6103 { MV_UP, MV_DOWN, MV_LEFT },
6105 { MV_LEFT, MV_RIGHT, MV_DOWN },
6109 { MV_RIGHT, MV_LEFT, MV_UP }
6112 int element = Feld[x][y];
6113 int move_pattern = element_info[element].move_pattern;
6115 int old_move_dir = MovDir[x][y];
6116 int left_dir = turn[old_move_dir].left;
6117 int right_dir = turn[old_move_dir].right;
6118 int back_dir = turn[old_move_dir].back;
6120 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6121 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6122 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6123 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6125 int left_x = x + left_dx, left_y = y + left_dy;
6126 int right_x = x + right_dx, right_y = y + right_dy;
6127 int move_x = x + move_dx, move_y = y + move_dy;
6131 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6133 TestIfBadThingTouchesOtherBadThing(x, y);
6135 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6136 MovDir[x][y] = right_dir;
6137 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6138 MovDir[x][y] = left_dir;
6140 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6142 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6145 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6147 TestIfBadThingTouchesOtherBadThing(x, y);
6149 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6150 MovDir[x][y] = left_dir;
6151 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6152 MovDir[x][y] = right_dir;
6154 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6156 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6159 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6161 TestIfBadThingTouchesOtherBadThing(x, y);
6163 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6164 MovDir[x][y] = left_dir;
6165 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6166 MovDir[x][y] = right_dir;
6168 if (MovDir[x][y] != old_move_dir)
6171 else if (element == EL_YAMYAM)
6173 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6174 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6176 if (can_turn_left && can_turn_right)
6177 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6178 else if (can_turn_left)
6179 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6180 else if (can_turn_right)
6181 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6183 MovDir[x][y] = back_dir;
6185 MovDelay[x][y] = 16 + 16 * RND(3);
6187 else if (element == EL_DARK_YAMYAM)
6189 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6191 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6194 if (can_turn_left && can_turn_right)
6195 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6196 else if (can_turn_left)
6197 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6198 else if (can_turn_right)
6199 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6201 MovDir[x][y] = back_dir;
6203 MovDelay[x][y] = 16 + 16 * RND(3);
6205 else if (element == EL_PACMAN)
6207 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6208 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6210 if (can_turn_left && can_turn_right)
6211 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6212 else if (can_turn_left)
6213 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6214 else if (can_turn_right)
6215 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6217 MovDir[x][y] = back_dir;
6219 MovDelay[x][y] = 6 + RND(40);
6221 else if (element == EL_PIG)
6223 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6224 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6225 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6226 boolean should_turn_left, should_turn_right, should_move_on;
6228 int rnd = RND(rnd_value);
6230 should_turn_left = (can_turn_left &&
6232 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6233 y + back_dy + left_dy)));
6234 should_turn_right = (can_turn_right &&
6236 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6237 y + back_dy + right_dy)));
6238 should_move_on = (can_move_on &&
6241 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6242 y + move_dy + left_dy) ||
6243 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6244 y + move_dy + right_dy)));
6246 if (should_turn_left || should_turn_right || should_move_on)
6248 if (should_turn_left && should_turn_right && should_move_on)
6249 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6250 rnd < 2 * rnd_value / 3 ? right_dir :
6252 else if (should_turn_left && should_turn_right)
6253 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6254 else if (should_turn_left && should_move_on)
6255 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6256 else if (should_turn_right && should_move_on)
6257 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6258 else if (should_turn_left)
6259 MovDir[x][y] = left_dir;
6260 else if (should_turn_right)
6261 MovDir[x][y] = right_dir;
6262 else if (should_move_on)
6263 MovDir[x][y] = old_move_dir;
6265 else if (can_move_on && rnd > rnd_value / 8)
6266 MovDir[x][y] = old_move_dir;
6267 else if (can_turn_left && can_turn_right)
6268 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6269 else if (can_turn_left && rnd > rnd_value / 8)
6270 MovDir[x][y] = left_dir;
6271 else if (can_turn_right && rnd > rnd_value/8)
6272 MovDir[x][y] = right_dir;
6274 MovDir[x][y] = back_dir;
6276 xx = x + move_xy[MovDir[x][y]].dx;
6277 yy = y + move_xy[MovDir[x][y]].dy;
6279 if (!IN_LEV_FIELD(xx, yy) ||
6280 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6281 MovDir[x][y] = old_move_dir;
6285 else if (element == EL_DRAGON)
6287 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6288 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6289 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6291 int rnd = RND(rnd_value);
6293 if (can_move_on && rnd > rnd_value / 8)
6294 MovDir[x][y] = old_move_dir;
6295 else if (can_turn_left && can_turn_right)
6296 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6297 else if (can_turn_left && rnd > rnd_value / 8)
6298 MovDir[x][y] = left_dir;
6299 else if (can_turn_right && rnd > rnd_value / 8)
6300 MovDir[x][y] = right_dir;
6302 MovDir[x][y] = back_dir;
6304 xx = x + move_xy[MovDir[x][y]].dx;
6305 yy = y + move_xy[MovDir[x][y]].dy;
6307 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6308 MovDir[x][y] = old_move_dir;
6312 else if (element == EL_MOLE)
6314 boolean can_move_on =
6315 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6316 IS_AMOEBOID(Feld[move_x][move_y]) ||
6317 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6320 boolean can_turn_left =
6321 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6322 IS_AMOEBOID(Feld[left_x][left_y])));
6324 boolean can_turn_right =
6325 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6326 IS_AMOEBOID(Feld[right_x][right_y])));
6328 if (can_turn_left && can_turn_right)
6329 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6330 else if (can_turn_left)
6331 MovDir[x][y] = left_dir;
6333 MovDir[x][y] = right_dir;
6336 if (MovDir[x][y] != old_move_dir)
6339 else if (element == EL_BALLOON)
6341 MovDir[x][y] = game.wind_direction;
6344 else if (element == EL_SPRING)
6346 #if USE_NEW_SPRING_BUMPER
6347 if (MovDir[x][y] & MV_HORIZONTAL)
6349 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6350 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6352 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6353 ResetGfxAnimation(move_x, move_y);
6354 DrawLevelField(move_x, move_y);
6356 MovDir[x][y] = back_dir;
6358 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6359 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6360 MovDir[x][y] = MV_NONE;
6363 if (MovDir[x][y] & MV_HORIZONTAL &&
6364 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6365 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6366 MovDir[x][y] = MV_NONE;
6371 else if (element == EL_ROBOT ||
6372 element == EL_SATELLITE ||
6373 element == EL_PENGUIN ||
6374 element == EL_EMC_ANDROID)
6376 int attr_x = -1, attr_y = -1;
6387 for (i = 0; i < MAX_PLAYERS; i++)
6389 struct PlayerInfo *player = &stored_player[i];
6390 int jx = player->jx, jy = player->jy;
6392 if (!player->active)
6396 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6404 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6405 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6406 game.engine_version < VERSION_IDENT(3,1,0,0)))
6412 if (element == EL_PENGUIN)
6415 static int xy[4][2] =
6423 for (i = 0; i < NUM_DIRECTIONS; i++)
6425 int ex = x + xy[i][0];
6426 int ey = y + xy[i][1];
6428 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6429 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6430 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6431 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6440 MovDir[x][y] = MV_NONE;
6442 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6443 else if (attr_x > x)
6444 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6446 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6447 else if (attr_y > y)
6448 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6450 if (element == EL_ROBOT)
6454 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6455 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6456 Moving2Blocked(x, y, &newx, &newy);
6458 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6459 MovDelay[x][y] = 8 + 8 * !RND(3);
6461 MovDelay[x][y] = 16;
6463 else if (element == EL_PENGUIN)
6469 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6471 boolean first_horiz = RND(2);
6472 int new_move_dir = MovDir[x][y];
6475 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6476 Moving2Blocked(x, y, &newx, &newy);
6478 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6482 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6483 Moving2Blocked(x, y, &newx, &newy);
6485 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6488 MovDir[x][y] = old_move_dir;
6492 else if (element == EL_SATELLITE)
6498 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6500 boolean first_horiz = RND(2);
6501 int new_move_dir = MovDir[x][y];
6504 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6505 Moving2Blocked(x, y, &newx, &newy);
6507 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6511 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6512 Moving2Blocked(x, y, &newx, &newy);
6514 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6517 MovDir[x][y] = old_move_dir;
6521 else if (element == EL_EMC_ANDROID)
6523 static int check_pos[16] =
6525 -1, /* 0 => (invalid) */
6526 7, /* 1 => MV_LEFT */
6527 3, /* 2 => MV_RIGHT */
6528 -1, /* 3 => (invalid) */
6530 0, /* 5 => MV_LEFT | MV_UP */
6531 2, /* 6 => MV_RIGHT | MV_UP */
6532 -1, /* 7 => (invalid) */
6533 5, /* 8 => MV_DOWN */
6534 6, /* 9 => MV_LEFT | MV_DOWN */
6535 4, /* 10 => MV_RIGHT | MV_DOWN */
6536 -1, /* 11 => (invalid) */
6537 -1, /* 12 => (invalid) */
6538 -1, /* 13 => (invalid) */
6539 -1, /* 14 => (invalid) */
6540 -1, /* 15 => (invalid) */
6548 { -1, -1, MV_LEFT | MV_UP },
6550 { +1, -1, MV_RIGHT | MV_UP },
6551 { +1, 0, MV_RIGHT },
6552 { +1, +1, MV_RIGHT | MV_DOWN },
6554 { -1, +1, MV_LEFT | MV_DOWN },
6557 int start_pos, check_order;
6558 boolean can_clone = FALSE;
6561 /* check if there is any free field around current position */
6562 for (i = 0; i < 8; i++)
6564 int newx = x + check_xy[i].dx;
6565 int newy = y + check_xy[i].dy;
6567 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6575 if (can_clone) /* randomly find an element to clone */
6579 start_pos = check_pos[RND(8)];
6580 check_order = (RND(2) ? -1 : +1);
6582 for (i = 0; i < 8; i++)
6584 int pos_raw = start_pos + i * check_order;
6585 int pos = (pos_raw + 8) % 8;
6586 int newx = x + check_xy[pos].dx;
6587 int newy = y + check_xy[pos].dy;
6589 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6591 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6592 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6594 Store[x][y] = Feld[newx][newy];
6603 if (can_clone) /* randomly find a direction to move */
6607 start_pos = check_pos[RND(8)];
6608 check_order = (RND(2) ? -1 : +1);
6610 for (i = 0; i < 8; i++)
6612 int pos_raw = start_pos + i * check_order;
6613 int pos = (pos_raw + 8) % 8;
6614 int newx = x + check_xy[pos].dx;
6615 int newy = y + check_xy[pos].dy;
6616 int new_move_dir = check_xy[pos].dir;
6618 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6620 MovDir[x][y] = new_move_dir;
6621 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6630 if (can_clone) /* cloning and moving successful */
6633 /* cannot clone -- try to move towards player */
6635 start_pos = check_pos[MovDir[x][y] & 0x0f];
6636 check_order = (RND(2) ? -1 : +1);
6638 for (i = 0; i < 3; i++)
6640 /* first check start_pos, then previous/next or (next/previous) pos */
6641 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6642 int pos = (pos_raw + 8) % 8;
6643 int newx = x + check_xy[pos].dx;
6644 int newy = y + check_xy[pos].dy;
6645 int new_move_dir = check_xy[pos].dir;
6647 if (IS_PLAYER(newx, newy))
6650 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6652 MovDir[x][y] = new_move_dir;
6653 MovDelay[x][y] = level.android_move_time * 8 + 1;
6660 else if (move_pattern == MV_TURNING_LEFT ||
6661 move_pattern == MV_TURNING_RIGHT ||
6662 move_pattern == MV_TURNING_LEFT_RIGHT ||
6663 move_pattern == MV_TURNING_RIGHT_LEFT ||
6664 move_pattern == MV_TURNING_RANDOM ||
6665 move_pattern == MV_ALL_DIRECTIONS)
6667 boolean can_turn_left =
6668 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6669 boolean can_turn_right =
6670 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6672 if (element_info[element].move_stepsize == 0) /* "not moving" */
6675 if (move_pattern == MV_TURNING_LEFT)
6676 MovDir[x][y] = left_dir;
6677 else if (move_pattern == MV_TURNING_RIGHT)
6678 MovDir[x][y] = right_dir;
6679 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6680 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6681 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6682 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6683 else if (move_pattern == MV_TURNING_RANDOM)
6684 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6685 can_turn_right && !can_turn_left ? right_dir :
6686 RND(2) ? left_dir : right_dir);
6687 else if (can_turn_left && can_turn_right)
6688 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6689 else if (can_turn_left)
6690 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6691 else if (can_turn_right)
6692 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6694 MovDir[x][y] = back_dir;
6696 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6698 else if (move_pattern == MV_HORIZONTAL ||
6699 move_pattern == MV_VERTICAL)
6701 if (move_pattern & old_move_dir)
6702 MovDir[x][y] = back_dir;
6703 else if (move_pattern == MV_HORIZONTAL)
6704 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6705 else if (move_pattern == MV_VERTICAL)
6706 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6708 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6710 else if (move_pattern & MV_ANY_DIRECTION)
6712 MovDir[x][y] = move_pattern;
6713 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6715 else if (move_pattern & MV_WIND_DIRECTION)
6717 MovDir[x][y] = game.wind_direction;
6718 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6720 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6722 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6723 MovDir[x][y] = left_dir;
6724 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6725 MovDir[x][y] = right_dir;
6727 if (MovDir[x][y] != old_move_dir)
6728 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6730 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6732 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6733 MovDir[x][y] = right_dir;
6734 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6735 MovDir[x][y] = left_dir;
6737 if (MovDir[x][y] != old_move_dir)
6738 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6740 else if (move_pattern == MV_TOWARDS_PLAYER ||
6741 move_pattern == MV_AWAY_FROM_PLAYER)
6743 int attr_x = -1, attr_y = -1;
6745 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6756 for (i = 0; i < MAX_PLAYERS; i++)
6758 struct PlayerInfo *player = &stored_player[i];
6759 int jx = player->jx, jy = player->jy;
6761 if (!player->active)
6765 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6773 MovDir[x][y] = MV_NONE;
6775 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6776 else if (attr_x > x)
6777 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6779 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6780 else if (attr_y > y)
6781 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6783 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6785 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6787 boolean first_horiz = RND(2);
6788 int new_move_dir = MovDir[x][y];
6790 if (element_info[element].move_stepsize == 0) /* "not moving" */
6792 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6793 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6799 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6800 Moving2Blocked(x, y, &newx, &newy);
6802 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6806 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6807 Moving2Blocked(x, y, &newx, &newy);
6809 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6812 MovDir[x][y] = old_move_dir;
6815 else if (move_pattern == MV_WHEN_PUSHED ||
6816 move_pattern == MV_WHEN_DROPPED)
6818 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6819 MovDir[x][y] = MV_NONE;
6823 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6825 static int test_xy[7][2] =
6835 static int test_dir[7] =
6845 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6846 int move_preference = -1000000; /* start with very low preference */
6847 int new_move_dir = MV_NONE;
6848 int start_test = RND(4);
6851 for (i = 0; i < NUM_DIRECTIONS; i++)
6853 int move_dir = test_dir[start_test + i];
6854 int move_dir_preference;
6856 xx = x + test_xy[start_test + i][0];
6857 yy = y + test_xy[start_test + i][1];
6859 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6860 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6862 new_move_dir = move_dir;
6867 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6870 move_dir_preference = -1 * RunnerVisit[xx][yy];
6871 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6872 move_dir_preference = PlayerVisit[xx][yy];
6874 if (move_dir_preference > move_preference)
6876 /* prefer field that has not been visited for the longest time */
6877 move_preference = move_dir_preference;
6878 new_move_dir = move_dir;
6880 else if (move_dir_preference == move_preference &&
6881 move_dir == old_move_dir)
6883 /* prefer last direction when all directions are preferred equally */
6884 move_preference = move_dir_preference;
6885 new_move_dir = move_dir;
6889 MovDir[x][y] = new_move_dir;
6890 if (old_move_dir != new_move_dir)
6891 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6895 static void TurnRound(int x, int y)
6897 int direction = MovDir[x][y];
6901 GfxDir[x][y] = MovDir[x][y];
6903 if (direction != MovDir[x][y])
6907 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6909 ResetGfxFrame(x, y, FALSE);
6912 static boolean JustBeingPushed(int x, int y)
6916 for (i = 0; i < MAX_PLAYERS; i++)
6918 struct PlayerInfo *player = &stored_player[i];
6920 if (player->active && player->is_pushing && player->MovPos)
6922 int next_jx = player->jx + (player->jx - player->last_jx);
6923 int next_jy = player->jy + (player->jy - player->last_jy);
6925 if (x == next_jx && y == next_jy)
6933 void StartMoving(int x, int y)
6935 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6936 int element = Feld[x][y];
6941 if (MovDelay[x][y] == 0)
6942 GfxAction[x][y] = ACTION_DEFAULT;
6944 if (CAN_FALL(element) && y < lev_fieldy - 1)
6946 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6947 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6948 if (JustBeingPushed(x, y))
6951 if (element == EL_QUICKSAND_FULL)
6953 if (IS_FREE(x, y + 1))
6955 InitMovingField(x, y, MV_DOWN);
6956 started_moving = TRUE;
6958 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6959 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6960 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6961 Store[x][y] = EL_ROCK;
6963 Store[x][y] = EL_ROCK;
6966 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6968 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6970 if (!MovDelay[x][y])
6971 MovDelay[x][y] = TILEY + 1;
6980 Feld[x][y] = EL_QUICKSAND_EMPTY;
6981 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6982 Store[x][y + 1] = Store[x][y];
6985 PlayLevelSoundAction(x, y, ACTION_FILLING);
6988 else if (element == EL_QUICKSAND_FAST_FULL)
6990 if (IS_FREE(x, y + 1))
6992 InitMovingField(x, y, MV_DOWN);
6993 started_moving = TRUE;
6995 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6996 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6997 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6998 Store[x][y] = EL_ROCK;
7000 Store[x][y] = EL_ROCK;
7003 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7005 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7007 if (!MovDelay[x][y])
7008 MovDelay[x][y] = TILEY + 1;
7017 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7018 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7019 Store[x][y + 1] = Store[x][y];
7022 PlayLevelSoundAction(x, y, ACTION_FILLING);
7025 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7026 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7028 InitMovingField(x, y, MV_DOWN);
7029 started_moving = TRUE;
7031 Feld[x][y] = EL_QUICKSAND_FILLING;
7032 Store[x][y] = element;
7034 PlayLevelSoundAction(x, y, ACTION_FILLING);
7036 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7037 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7039 InitMovingField(x, y, MV_DOWN);
7040 started_moving = TRUE;
7042 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7043 Store[x][y] = element;
7045 PlayLevelSoundAction(x, y, ACTION_FILLING);
7047 else if (element == EL_MAGIC_WALL_FULL)
7049 if (IS_FREE(x, y + 1))
7051 InitMovingField(x, y, MV_DOWN);
7052 started_moving = TRUE;
7054 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7055 Store[x][y] = EL_CHANGED(Store[x][y]);
7057 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7059 if (!MovDelay[x][y])
7060 MovDelay[x][y] = TILEY/4 + 1;
7069 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7070 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7071 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7075 else if (element == EL_BD_MAGIC_WALL_FULL)
7077 if (IS_FREE(x, y + 1))
7079 InitMovingField(x, y, MV_DOWN);
7080 started_moving = TRUE;
7082 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7083 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7085 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7087 if (!MovDelay[x][y])
7088 MovDelay[x][y] = TILEY/4 + 1;
7097 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7098 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7099 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7103 else if (element == EL_DC_MAGIC_WALL_FULL)
7105 if (IS_FREE(x, y + 1))
7107 InitMovingField(x, y, MV_DOWN);
7108 started_moving = TRUE;
7110 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7111 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7113 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7115 if (!MovDelay[x][y])
7116 MovDelay[x][y] = TILEY/4 + 1;
7125 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7126 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7127 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7131 else if ((CAN_PASS_MAGIC_WALL(element) &&
7132 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7133 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7134 (CAN_PASS_DC_MAGIC_WALL(element) &&
7135 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7138 InitMovingField(x, y, MV_DOWN);
7139 started_moving = TRUE;
7142 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7143 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7144 EL_DC_MAGIC_WALL_FILLING);
7145 Store[x][y] = element;
7147 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7149 SplashAcid(x, y + 1);
7151 InitMovingField(x, y, MV_DOWN);
7152 started_moving = TRUE;
7154 Store[x][y] = EL_ACID;
7157 #if USE_FIX_IMPACT_COLLISION
7158 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7159 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7161 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7162 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7164 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7165 CAN_FALL(element) && WasJustFalling[x][y] &&
7166 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7168 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7169 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7170 (Feld[x][y + 1] == EL_BLOCKED)))
7172 /* this is needed for a special case not covered by calling "Impact()"
7173 from "ContinueMoving()": if an element moves to a tile directly below
7174 another element which was just falling on that tile (which was empty
7175 in the previous frame), the falling element above would just stop
7176 instead of smashing the element below (in previous version, the above
7177 element was just checked for "moving" instead of "falling", resulting
7178 in incorrect smashes caused by horizontal movement of the above
7179 element; also, the case of the player being the element to smash was
7180 simply not covered here... :-/ ) */
7182 CheckCollision[x][y] = 0;
7183 CheckImpact[x][y] = 0;
7187 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7189 if (MovDir[x][y] == MV_NONE)
7191 InitMovingField(x, y, MV_DOWN);
7192 started_moving = TRUE;
7195 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7197 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7198 MovDir[x][y] = MV_DOWN;
7200 InitMovingField(x, y, MV_DOWN);
7201 started_moving = TRUE;
7203 else if (element == EL_AMOEBA_DROP)
7205 Feld[x][y] = EL_AMOEBA_GROWING;
7206 Store[x][y] = EL_AMOEBA_WET;
7208 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7209 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7210 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7211 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7213 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7214 (IS_FREE(x - 1, y + 1) ||
7215 Feld[x - 1][y + 1] == EL_ACID));
7216 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7217 (IS_FREE(x + 1, y + 1) ||
7218 Feld[x + 1][y + 1] == EL_ACID));
7219 boolean can_fall_any = (can_fall_left || can_fall_right);
7220 boolean can_fall_both = (can_fall_left && can_fall_right);
7221 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7223 #if USE_NEW_ALL_SLIPPERY
7224 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7226 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7227 can_fall_right = FALSE;
7228 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7229 can_fall_left = FALSE;
7230 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7231 can_fall_right = FALSE;
7232 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7233 can_fall_left = FALSE;
7235 can_fall_any = (can_fall_left || can_fall_right);
7236 can_fall_both = FALSE;
7239 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7241 if (slippery_type == SLIPPERY_ONLY_LEFT)
7242 can_fall_right = FALSE;
7243 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7244 can_fall_left = FALSE;
7245 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7246 can_fall_right = FALSE;
7247 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7248 can_fall_left = FALSE;
7250 can_fall_any = (can_fall_left || can_fall_right);
7251 can_fall_both = (can_fall_left && can_fall_right);
7255 #if USE_NEW_ALL_SLIPPERY
7257 #if USE_NEW_SP_SLIPPERY
7258 /* !!! better use the same properties as for custom elements here !!! */
7259 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7260 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7262 can_fall_right = FALSE; /* slip down on left side */
7263 can_fall_both = FALSE;
7268 #if USE_NEW_ALL_SLIPPERY
7271 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7272 can_fall_right = FALSE; /* slip down on left side */
7274 can_fall_left = !(can_fall_right = RND(2));
7276 can_fall_both = FALSE;
7281 if (game.emulation == EMU_BOULDERDASH ||
7282 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7283 can_fall_right = FALSE; /* slip down on left side */
7285 can_fall_left = !(can_fall_right = RND(2));
7287 can_fall_both = FALSE;
7293 /* if not determined otherwise, prefer left side for slipping down */
7294 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7295 started_moving = TRUE;
7299 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7301 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7304 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7305 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7306 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7307 int belt_dir = game.belt_dir[belt_nr];
7309 if ((belt_dir == MV_LEFT && left_is_free) ||
7310 (belt_dir == MV_RIGHT && right_is_free))
7312 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7314 InitMovingField(x, y, belt_dir);
7315 started_moving = TRUE;
7317 Pushed[x][y] = TRUE;
7318 Pushed[nextx][y] = TRUE;
7320 GfxAction[x][y] = ACTION_DEFAULT;
7324 MovDir[x][y] = 0; /* if element was moving, stop it */
7329 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7331 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7333 if (CAN_MOVE(element) && !started_moving)
7336 int move_pattern = element_info[element].move_pattern;
7341 if (MovDir[x][y] == MV_NONE)
7343 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7344 x, y, element, element_info[element].token_name);
7345 printf("StartMoving(): This should never happen!\n");
7350 Moving2Blocked(x, y, &newx, &newy);
7352 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7355 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7356 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7358 WasJustMoving[x][y] = 0;
7359 CheckCollision[x][y] = 0;
7361 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7363 if (Feld[x][y] != element) /* element has changed */
7367 if (!MovDelay[x][y]) /* start new movement phase */
7369 /* all objects that can change their move direction after each step
7370 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7372 if (element != EL_YAMYAM &&
7373 element != EL_DARK_YAMYAM &&
7374 element != EL_PACMAN &&
7375 !(move_pattern & MV_ANY_DIRECTION) &&
7376 move_pattern != MV_TURNING_LEFT &&
7377 move_pattern != MV_TURNING_RIGHT &&
7378 move_pattern != MV_TURNING_LEFT_RIGHT &&
7379 move_pattern != MV_TURNING_RIGHT_LEFT &&
7380 move_pattern != MV_TURNING_RANDOM)
7384 if (MovDelay[x][y] && (element == EL_BUG ||
7385 element == EL_SPACESHIP ||
7386 element == EL_SP_SNIKSNAK ||
7387 element == EL_SP_ELECTRON ||
7388 element == EL_MOLE))
7389 DrawLevelField(x, y);
7393 if (MovDelay[x][y]) /* wait some time before next movement */
7397 if (element == EL_ROBOT ||
7398 element == EL_YAMYAM ||
7399 element == EL_DARK_YAMYAM)
7401 DrawLevelElementAnimationIfNeeded(x, y, element);
7402 PlayLevelSoundAction(x, y, ACTION_WAITING);
7404 else if (element == EL_SP_ELECTRON)
7405 DrawLevelElementAnimationIfNeeded(x, y, element);
7406 else if (element == EL_DRAGON)
7409 int dir = MovDir[x][y];
7410 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7411 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7412 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7413 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7414 dir == MV_UP ? IMG_FLAMES_1_UP :
7415 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7416 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7418 GfxAction[x][y] = ACTION_ATTACKING;
7420 if (IS_PLAYER(x, y))
7421 DrawPlayerField(x, y);
7423 DrawLevelField(x, y);
7425 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7427 for (i = 1; i <= 3; i++)
7429 int xx = x + i * dx;
7430 int yy = y + i * dy;
7431 int sx = SCREENX(xx);
7432 int sy = SCREENY(yy);
7433 int flame_graphic = graphic + (i - 1);
7435 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7440 int flamed = MovingOrBlocked2Element(xx, yy);
7444 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7446 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7447 RemoveMovingField(xx, yy);
7449 RemoveField(xx, yy);
7451 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7454 RemoveMovingField(xx, yy);
7457 ChangeDelay[xx][yy] = 0;
7459 Feld[xx][yy] = EL_FLAMES;
7461 if (IN_SCR_FIELD(sx, sy))
7463 DrawLevelFieldCrumbledSand(xx, yy);
7464 DrawGraphic(sx, sy, flame_graphic, frame);
7469 if (Feld[xx][yy] == EL_FLAMES)
7470 Feld[xx][yy] = EL_EMPTY;
7471 DrawLevelField(xx, yy);
7476 if (MovDelay[x][y]) /* element still has to wait some time */
7478 PlayLevelSoundAction(x, y, ACTION_WAITING);
7484 /* now make next step */
7486 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7488 if (DONT_COLLIDE_WITH(element) &&
7489 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7490 !PLAYER_ENEMY_PROTECTED(newx, newy))
7492 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7497 else if (CAN_MOVE_INTO_ACID(element) &&
7498 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7499 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7500 (MovDir[x][y] == MV_DOWN ||
7501 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7503 SplashAcid(newx, newy);
7504 Store[x][y] = EL_ACID;
7506 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7508 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7509 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7510 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7511 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7514 DrawLevelField(x, y);
7516 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7517 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7518 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7520 local_player->friends_still_needed--;
7521 if (!local_player->friends_still_needed &&
7522 !local_player->GameOver && AllPlayersGone)
7523 PlayerWins(local_player);
7527 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7529 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7530 DrawLevelField(newx, newy);
7532 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7534 else if (!IS_FREE(newx, newy))
7536 GfxAction[x][y] = ACTION_WAITING;
7538 if (IS_PLAYER(x, y))
7539 DrawPlayerField(x, y);
7541 DrawLevelField(x, y);
7546 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7548 if (IS_FOOD_PIG(Feld[newx][newy]))
7550 if (IS_MOVING(newx, newy))
7551 RemoveMovingField(newx, newy);
7554 Feld[newx][newy] = EL_EMPTY;
7555 DrawLevelField(newx, newy);
7558 PlayLevelSound(x, y, SND_PIG_DIGGING);
7560 else if (!IS_FREE(newx, newy))
7562 if (IS_PLAYER(x, y))
7563 DrawPlayerField(x, y);
7565 DrawLevelField(x, y);
7570 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7572 if (Store[x][y] != EL_EMPTY)
7574 boolean can_clone = FALSE;
7577 /* check if element to clone is still there */
7578 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7580 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7588 /* cannot clone or target field not free anymore -- do not clone */
7589 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7590 Store[x][y] = EL_EMPTY;
7593 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7595 if (IS_MV_DIAGONAL(MovDir[x][y]))
7597 int diagonal_move_dir = MovDir[x][y];
7598 int stored = Store[x][y];
7599 int change_delay = 8;
7602 /* android is moving diagonally */
7604 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7606 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7607 GfxElement[x][y] = EL_EMC_ANDROID;
7608 GfxAction[x][y] = ACTION_SHRINKING;
7609 GfxDir[x][y] = diagonal_move_dir;
7610 ChangeDelay[x][y] = change_delay;
7612 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7615 DrawLevelGraphicAnimation(x, y, graphic);
7616 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7618 if (Feld[newx][newy] == EL_ACID)
7620 SplashAcid(newx, newy);
7625 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7627 Store[newx][newy] = EL_EMC_ANDROID;
7628 GfxElement[newx][newy] = EL_EMC_ANDROID;
7629 GfxAction[newx][newy] = ACTION_GROWING;
7630 GfxDir[newx][newy] = diagonal_move_dir;
7631 ChangeDelay[newx][newy] = change_delay;
7633 graphic = el_act_dir2img(GfxElement[newx][newy],
7634 GfxAction[newx][newy], GfxDir[newx][newy]);
7636 DrawLevelGraphicAnimation(newx, newy, graphic);
7637 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7643 Feld[newx][newy] = EL_EMPTY;
7644 DrawLevelField(newx, newy);
7646 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7649 else if (!IS_FREE(newx, newy))
7652 if (IS_PLAYER(x, y))
7653 DrawPlayerField(x, y);
7655 DrawLevelField(x, y);
7661 else if (IS_CUSTOM_ELEMENT(element) &&
7662 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7664 int new_element = Feld[newx][newy];
7666 if (!IS_FREE(newx, newy))
7668 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7669 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7672 /* no element can dig solid indestructible elements */
7673 if (IS_INDESTRUCTIBLE(new_element) &&
7674 !IS_DIGGABLE(new_element) &&
7675 !IS_COLLECTIBLE(new_element))
7678 if (AmoebaNr[newx][newy] &&
7679 (new_element == EL_AMOEBA_FULL ||
7680 new_element == EL_BD_AMOEBA ||
7681 new_element == EL_AMOEBA_GROWING))
7683 AmoebaCnt[AmoebaNr[newx][newy]]--;
7684 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7687 if (IS_MOVING(newx, newy))
7688 RemoveMovingField(newx, newy);
7691 RemoveField(newx, newy);
7692 DrawLevelField(newx, newy);
7695 /* if digged element was about to explode, prevent the explosion */
7696 ExplodeField[newx][newy] = EX_TYPE_NONE;
7698 PlayLevelSoundAction(x, y, action);
7701 Store[newx][newy] = EL_EMPTY;
7703 /* this makes it possible to leave the removed element again */
7704 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7705 Store[newx][newy] = new_element;
7707 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7709 int move_leave_element = element_info[element].move_leave_element;
7711 /* this makes it possible to leave the removed element again */
7712 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7713 new_element : move_leave_element);
7717 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7719 RunnerVisit[x][y] = FrameCounter;
7720 PlayerVisit[x][y] /= 8; /* expire player visit path */
7723 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7725 if (!IS_FREE(newx, newy))
7727 if (IS_PLAYER(x, y))
7728 DrawPlayerField(x, y);
7730 DrawLevelField(x, y);
7736 boolean wanna_flame = !RND(10);
7737 int dx = newx - x, dy = newy - y;
7738 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7739 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7740 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7741 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7742 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7743 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7746 IS_CLASSIC_ENEMY(element1) ||
7747 IS_CLASSIC_ENEMY(element2)) &&
7748 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7749 element1 != EL_FLAMES && element2 != EL_FLAMES)
7751 ResetGfxAnimation(x, y);
7752 GfxAction[x][y] = ACTION_ATTACKING;
7754 if (IS_PLAYER(x, y))
7755 DrawPlayerField(x, y);
7757 DrawLevelField(x, y);
7759 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7761 MovDelay[x][y] = 50;
7765 RemoveField(newx, newy);
7767 Feld[newx][newy] = EL_FLAMES;
7768 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7771 RemoveField(newx1, newy1);
7773 Feld[newx1][newy1] = EL_FLAMES;
7775 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7778 RemoveField(newx2, newy2);
7780 Feld[newx2][newy2] = EL_FLAMES;
7787 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7788 Feld[newx][newy] == EL_DIAMOND)
7790 if (IS_MOVING(newx, newy))
7791 RemoveMovingField(newx, newy);
7794 Feld[newx][newy] = EL_EMPTY;
7795 DrawLevelField(newx, newy);
7798 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7800 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7801 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7803 if (AmoebaNr[newx][newy])
7805 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7806 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7807 Feld[newx][newy] == EL_BD_AMOEBA)
7808 AmoebaCnt[AmoebaNr[newx][newy]]--;
7813 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7815 RemoveMovingField(newx, newy);
7818 if (IS_MOVING(newx, newy))
7820 RemoveMovingField(newx, newy);
7825 Feld[newx][newy] = EL_EMPTY;
7826 DrawLevelField(newx, newy);
7829 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7831 else if ((element == EL_PACMAN || element == EL_MOLE)
7832 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7834 if (AmoebaNr[newx][newy])
7836 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7837 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7838 Feld[newx][newy] == EL_BD_AMOEBA)
7839 AmoebaCnt[AmoebaNr[newx][newy]]--;
7842 if (element == EL_MOLE)
7844 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7845 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7847 ResetGfxAnimation(x, y);
7848 GfxAction[x][y] = ACTION_DIGGING;
7849 DrawLevelField(x, y);
7851 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7853 return; /* wait for shrinking amoeba */
7855 else /* element == EL_PACMAN */
7857 Feld[newx][newy] = EL_EMPTY;
7858 DrawLevelField(newx, newy);
7859 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7862 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7863 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7864 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7866 /* wait for shrinking amoeba to completely disappear */
7869 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7871 /* object was running against a wall */
7876 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7877 if (move_pattern & MV_ANY_DIRECTION &&
7878 move_pattern == MovDir[x][y])
7880 int blocking_element =
7881 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7883 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7886 element = Feld[x][y]; /* element might have changed */
7890 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7891 DrawLevelElementAnimation(x, y, element);
7893 if (DONT_TOUCH(element))
7894 TestIfBadThingTouchesPlayer(x, y);
7899 InitMovingField(x, y, MovDir[x][y]);
7901 PlayLevelSoundAction(x, y, ACTION_MOVING);
7905 ContinueMoving(x, y);
7908 void ContinueMoving(int x, int y)
7910 int element = Feld[x][y];
7911 struct ElementInfo *ei = &element_info[element];
7912 int direction = MovDir[x][y];
7913 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7914 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7915 int newx = x + dx, newy = y + dy;
7916 int stored = Store[x][y];
7917 int stored_new = Store[newx][newy];
7918 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7919 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7920 boolean last_line = (newy == lev_fieldy - 1);
7922 MovPos[x][y] += getElementMoveStepsize(x, y);
7924 if (pushed_by_player) /* special case: moving object pushed by player */
7925 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7927 if (ABS(MovPos[x][y]) < TILEX)
7930 int ee = Feld[x][y];
7931 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7932 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7934 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7935 x, y, ABS(MovPos[x][y]),
7937 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7940 DrawLevelField(x, y);
7942 return; /* element is still moving */
7945 /* element reached destination field */
7947 Feld[x][y] = EL_EMPTY;
7948 Feld[newx][newy] = element;
7949 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7951 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7953 element = Feld[newx][newy] = EL_ACID;
7955 else if (element == EL_MOLE)
7957 Feld[x][y] = EL_SAND;
7959 DrawLevelFieldCrumbledSandNeighbours(x, y);
7961 else if (element == EL_QUICKSAND_FILLING)
7963 element = Feld[newx][newy] = get_next_element(element);
7964 Store[newx][newy] = Store[x][y];
7966 else if (element == EL_QUICKSAND_EMPTYING)
7968 Feld[x][y] = get_next_element(element);
7969 element = Feld[newx][newy] = Store[x][y];
7971 else if (element == EL_QUICKSAND_FAST_FILLING)
7973 element = Feld[newx][newy] = get_next_element(element);
7974 Store[newx][newy] = Store[x][y];
7976 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7978 Feld[x][y] = get_next_element(element);
7979 element = Feld[newx][newy] = Store[x][y];
7981 else if (element == EL_MAGIC_WALL_FILLING)
7983 element = Feld[newx][newy] = get_next_element(element);
7984 if (!game.magic_wall_active)
7985 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7986 Store[newx][newy] = Store[x][y];
7988 else if (element == EL_MAGIC_WALL_EMPTYING)
7990 Feld[x][y] = get_next_element(element);
7991 if (!game.magic_wall_active)
7992 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7993 element = Feld[newx][newy] = Store[x][y];
7995 #if USE_NEW_CUSTOM_VALUE
7996 InitField(newx, newy, FALSE);
7999 else if (element == EL_BD_MAGIC_WALL_FILLING)
8001 element = Feld[newx][newy] = get_next_element(element);
8002 if (!game.magic_wall_active)
8003 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8004 Store[newx][newy] = Store[x][y];
8006 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8008 Feld[x][y] = get_next_element(element);
8009 if (!game.magic_wall_active)
8010 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8011 element = Feld[newx][newy] = Store[x][y];
8013 #if USE_NEW_CUSTOM_VALUE
8014 InitField(newx, newy, FALSE);
8017 else if (element == EL_DC_MAGIC_WALL_FILLING)
8019 element = Feld[newx][newy] = get_next_element(element);
8020 if (!game.magic_wall_active)
8021 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8022 Store[newx][newy] = Store[x][y];
8024 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8026 Feld[x][y] = get_next_element(element);
8027 if (!game.magic_wall_active)
8028 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8029 element = Feld[newx][newy] = Store[x][y];
8031 #if USE_NEW_CUSTOM_VALUE
8032 InitField(newx, newy, FALSE);
8035 else if (element == EL_AMOEBA_DROPPING)
8037 Feld[x][y] = get_next_element(element);
8038 element = Feld[newx][newy] = Store[x][y];
8040 else if (element == EL_SOKOBAN_OBJECT)
8043 Feld[x][y] = Back[x][y];
8045 if (Back[newx][newy])
8046 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8048 Back[x][y] = Back[newx][newy] = 0;
8051 Store[x][y] = EL_EMPTY;
8056 MovDelay[newx][newy] = 0;
8058 if (CAN_CHANGE_OR_HAS_ACTION(element))
8060 /* copy element change control values to new field */
8061 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8062 ChangePage[newx][newy] = ChangePage[x][y];
8063 ChangeCount[newx][newy] = ChangeCount[x][y];
8064 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8067 #if USE_NEW_CUSTOM_VALUE
8068 CustomValue[newx][newy] = CustomValue[x][y];
8071 ChangeDelay[x][y] = 0;
8072 ChangePage[x][y] = -1;
8073 ChangeCount[x][y] = 0;
8074 ChangeEvent[x][y] = -1;
8076 #if USE_NEW_CUSTOM_VALUE
8077 CustomValue[x][y] = 0;
8080 /* copy animation control values to new field */
8081 GfxFrame[newx][newy] = GfxFrame[x][y];
8082 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8083 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8084 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8086 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8088 /* some elements can leave other elements behind after moving */
8090 if (ei->move_leave_element != EL_EMPTY &&
8091 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8092 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8094 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8095 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8096 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8099 int move_leave_element = ei->move_leave_element;
8103 /* this makes it possible to leave the removed element again */
8104 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8105 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8107 /* this makes it possible to leave the removed element again */
8108 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8109 move_leave_element = stored;
8112 /* this makes it possible to leave the removed element again */
8113 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8114 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8115 move_leave_element = stored;
8118 Feld[x][y] = move_leave_element;
8120 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8121 MovDir[x][y] = direction;
8123 InitField(x, y, FALSE);
8125 if (GFX_CRUMBLED(Feld[x][y]))
8126 DrawLevelFieldCrumbledSandNeighbours(x, y);
8128 if (ELEM_IS_PLAYER(move_leave_element))
8129 RelocatePlayer(x, y, move_leave_element);
8132 /* do this after checking for left-behind element */
8133 ResetGfxAnimation(x, y); /* reset animation values for old field */
8135 if (!CAN_MOVE(element) ||
8136 (CAN_FALL(element) && direction == MV_DOWN &&
8137 (element == EL_SPRING ||
8138 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8139 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8140 GfxDir[x][y] = MovDir[newx][newy] = 0;
8142 DrawLevelField(x, y);
8143 DrawLevelField(newx, newy);
8145 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8147 /* prevent pushed element from moving on in pushed direction */
8148 if (pushed_by_player && CAN_MOVE(element) &&
8149 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8150 !(element_info[element].move_pattern & direction))
8151 TurnRound(newx, newy);
8153 /* prevent elements on conveyor belt from moving on in last direction */
8154 if (pushed_by_conveyor && CAN_FALL(element) &&
8155 direction & MV_HORIZONTAL)
8156 MovDir[newx][newy] = 0;
8158 if (!pushed_by_player)
8160 int nextx = newx + dx, nexty = newy + dy;
8161 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8163 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8165 if (CAN_FALL(element) && direction == MV_DOWN)
8166 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8168 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8169 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8171 #if USE_FIX_IMPACT_COLLISION
8172 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8173 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8177 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8179 TestIfBadThingTouchesPlayer(newx, newy);
8180 TestIfBadThingTouchesFriend(newx, newy);
8182 if (!IS_CUSTOM_ELEMENT(element))
8183 TestIfBadThingTouchesOtherBadThing(newx, newy);
8185 else if (element == EL_PENGUIN)
8186 TestIfFriendTouchesBadThing(newx, newy);
8188 /* give the player one last chance (one more frame) to move away */
8189 if (CAN_FALL(element) && direction == MV_DOWN &&
8190 (last_line || (!IS_FREE(x, newy + 1) &&
8191 (!IS_PLAYER(x, newy + 1) ||
8192 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8195 if (pushed_by_player && !game.use_change_when_pushing_bug)
8197 int push_side = MV_DIR_OPPOSITE(direction);
8198 struct PlayerInfo *player = PLAYERINFO(x, y);
8200 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8201 player->index_bit, push_side);
8202 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8203 player->index_bit, push_side);
8206 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8207 MovDelay[newx][newy] = 1;
8209 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8211 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8214 if (ChangePage[newx][newy] != -1) /* delayed change */
8216 int page = ChangePage[newx][newy];
8217 struct ElementChangeInfo *change = &ei->change_page[page];
8219 ChangePage[newx][newy] = -1;
8221 if (change->can_change)
8223 if (ChangeElement(newx, newy, element, page))
8225 if (change->post_change_function)
8226 change->post_change_function(newx, newy);
8230 if (change->has_action)
8231 ExecuteCustomElementAction(newx, newy, element, page);
8235 TestIfElementHitsCustomElement(newx, newy, direction);
8236 TestIfPlayerTouchesCustomElement(newx, newy);
8237 TestIfElementTouchesCustomElement(newx, newy);
8239 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8240 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8241 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8242 MV_DIR_OPPOSITE(direction));
8245 int AmoebeNachbarNr(int ax, int ay)
8248 int element = Feld[ax][ay];
8250 static int xy[4][2] =
8258 for (i = 0; i < NUM_DIRECTIONS; i++)
8260 int x = ax + xy[i][0];
8261 int y = ay + xy[i][1];
8263 if (!IN_LEV_FIELD(x, y))
8266 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8267 group_nr = AmoebaNr[x][y];
8273 void AmoebenVereinigen(int ax, int ay)
8275 int i, x, y, xx, yy;
8276 int new_group_nr = AmoebaNr[ax][ay];
8277 static int xy[4][2] =
8285 if (new_group_nr == 0)
8288 for (i = 0; i < NUM_DIRECTIONS; i++)
8293 if (!IN_LEV_FIELD(x, y))
8296 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8297 Feld[x][y] == EL_BD_AMOEBA ||
8298 Feld[x][y] == EL_AMOEBA_DEAD) &&
8299 AmoebaNr[x][y] != new_group_nr)
8301 int old_group_nr = AmoebaNr[x][y];
8303 if (old_group_nr == 0)
8306 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8307 AmoebaCnt[old_group_nr] = 0;
8308 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8309 AmoebaCnt2[old_group_nr] = 0;
8311 SCAN_PLAYFIELD(xx, yy)
8313 if (AmoebaNr[xx][yy] == old_group_nr)
8314 AmoebaNr[xx][yy] = new_group_nr;
8320 void AmoebeUmwandeln(int ax, int ay)
8324 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8326 int group_nr = AmoebaNr[ax][ay];
8331 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8332 printf("AmoebeUmwandeln(): This should never happen!\n");
8337 SCAN_PLAYFIELD(x, y)
8339 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8342 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8346 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8347 SND_AMOEBA_TURNING_TO_GEM :
8348 SND_AMOEBA_TURNING_TO_ROCK));
8353 static int xy[4][2] =
8361 for (i = 0; i < NUM_DIRECTIONS; i++)
8366 if (!IN_LEV_FIELD(x, y))
8369 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8371 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8372 SND_AMOEBA_TURNING_TO_GEM :
8373 SND_AMOEBA_TURNING_TO_ROCK));
8380 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8383 int group_nr = AmoebaNr[ax][ay];
8384 boolean done = FALSE;
8389 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8390 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8395 SCAN_PLAYFIELD(x, y)
8397 if (AmoebaNr[x][y] == group_nr &&
8398 (Feld[x][y] == EL_AMOEBA_DEAD ||
8399 Feld[x][y] == EL_BD_AMOEBA ||
8400 Feld[x][y] == EL_AMOEBA_GROWING))
8403 Feld[x][y] = new_element;
8404 InitField(x, y, FALSE);
8405 DrawLevelField(x, y);
8411 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8412 SND_BD_AMOEBA_TURNING_TO_ROCK :
8413 SND_BD_AMOEBA_TURNING_TO_GEM));
8416 void AmoebeWaechst(int x, int y)
8418 static unsigned long sound_delay = 0;
8419 static unsigned long sound_delay_value = 0;
8421 if (!MovDelay[x][y]) /* start new growing cycle */
8425 if (DelayReached(&sound_delay, sound_delay_value))
8427 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8428 sound_delay_value = 30;
8432 if (MovDelay[x][y]) /* wait some time before growing bigger */
8435 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8437 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8438 6 - MovDelay[x][y]);
8440 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8443 if (!MovDelay[x][y])
8445 Feld[x][y] = Store[x][y];
8447 DrawLevelField(x, y);
8452 void AmoebaDisappearing(int x, int y)
8454 static unsigned long sound_delay = 0;
8455 static unsigned long sound_delay_value = 0;
8457 if (!MovDelay[x][y]) /* start new shrinking cycle */
8461 if (DelayReached(&sound_delay, sound_delay_value))
8462 sound_delay_value = 30;
8465 if (MovDelay[x][y]) /* wait some time before shrinking */
8468 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8470 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8471 6 - MovDelay[x][y]);
8473 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8476 if (!MovDelay[x][y])
8478 Feld[x][y] = EL_EMPTY;
8479 DrawLevelField(x, y);
8481 /* don't let mole enter this field in this cycle;
8482 (give priority to objects falling to this field from above) */
8488 void AmoebeAbleger(int ax, int ay)
8491 int element = Feld[ax][ay];
8492 int graphic = el2img(element);
8493 int newax = ax, neway = ay;
8494 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8495 static int xy[4][2] =
8503 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8505 Feld[ax][ay] = EL_AMOEBA_DEAD;
8506 DrawLevelField(ax, ay);
8510 if (IS_ANIMATED(graphic))
8511 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8513 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8514 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8516 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8519 if (MovDelay[ax][ay])
8523 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8526 int x = ax + xy[start][0];
8527 int y = ay + xy[start][1];
8529 if (!IN_LEV_FIELD(x, y))
8532 if (IS_FREE(x, y) ||
8533 CAN_GROW_INTO(Feld[x][y]) ||
8534 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8535 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8541 if (newax == ax && neway == ay)
8544 else /* normal or "filled" (BD style) amoeba */
8547 boolean waiting_for_player = FALSE;
8549 for (i = 0; i < NUM_DIRECTIONS; i++)
8551 int j = (start + i) % 4;
8552 int x = ax + xy[j][0];
8553 int y = ay + xy[j][1];
8555 if (!IN_LEV_FIELD(x, y))
8558 if (IS_FREE(x, y) ||
8559 CAN_GROW_INTO(Feld[x][y]) ||
8560 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8561 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8567 else if (IS_PLAYER(x, y))
8568 waiting_for_player = TRUE;
8571 if (newax == ax && neway == ay) /* amoeba cannot grow */
8573 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8575 Feld[ax][ay] = EL_AMOEBA_DEAD;
8576 DrawLevelField(ax, ay);
8577 AmoebaCnt[AmoebaNr[ax][ay]]--;
8579 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8581 if (element == EL_AMOEBA_FULL)
8582 AmoebeUmwandeln(ax, ay);
8583 else if (element == EL_BD_AMOEBA)
8584 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8589 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8591 /* amoeba gets larger by growing in some direction */
8593 int new_group_nr = AmoebaNr[ax][ay];
8596 if (new_group_nr == 0)
8598 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8599 printf("AmoebeAbleger(): This should never happen!\n");
8604 AmoebaNr[newax][neway] = new_group_nr;
8605 AmoebaCnt[new_group_nr]++;
8606 AmoebaCnt2[new_group_nr]++;
8608 /* if amoeba touches other amoeba(s) after growing, unify them */
8609 AmoebenVereinigen(newax, neway);
8611 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8613 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8619 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8620 (neway == lev_fieldy - 1 && newax != ax))
8622 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8623 Store[newax][neway] = element;
8625 else if (neway == ay || element == EL_EMC_DRIPPER)
8627 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8629 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8633 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8634 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8635 Store[ax][ay] = EL_AMOEBA_DROP;
8636 ContinueMoving(ax, ay);
8640 DrawLevelField(newax, neway);
8643 void Life(int ax, int ay)
8647 int element = Feld[ax][ay];
8648 int graphic = el2img(element);
8649 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8651 boolean changed = FALSE;
8653 if (IS_ANIMATED(graphic))
8654 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8659 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8660 MovDelay[ax][ay] = life_time;
8662 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8665 if (MovDelay[ax][ay])
8669 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8671 int xx = ax+x1, yy = ay+y1;
8674 if (!IN_LEV_FIELD(xx, yy))
8677 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8679 int x = xx+x2, y = yy+y2;
8681 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8684 if (((Feld[x][y] == element ||
8685 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8687 (IS_FREE(x, y) && Stop[x][y]))
8691 if (xx == ax && yy == ay) /* field in the middle */
8693 if (nachbarn < life_parameter[0] ||
8694 nachbarn > life_parameter[1])
8696 Feld[xx][yy] = EL_EMPTY;
8698 DrawLevelField(xx, yy);
8699 Stop[xx][yy] = TRUE;
8703 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8704 { /* free border field */
8705 if (nachbarn >= life_parameter[2] &&
8706 nachbarn <= life_parameter[3])
8708 Feld[xx][yy] = element;
8709 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8711 DrawLevelField(xx, yy);
8712 Stop[xx][yy] = TRUE;
8719 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8720 SND_GAME_OF_LIFE_GROWING);
8723 static void InitRobotWheel(int x, int y)
8725 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8728 static void RunRobotWheel(int x, int y)
8730 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8733 static void StopRobotWheel(int x, int y)
8735 if (ZX == x && ZY == y)
8739 static void InitTimegateWheel(int x, int y)
8741 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8744 static void RunTimegateWheel(int x, int y)
8746 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8749 static void InitMagicBallDelay(int x, int y)
8752 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8754 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8758 static void ActivateMagicBall(int bx, int by)
8762 if (level.ball_random)
8764 int pos_border = RND(8); /* select one of the eight border elements */
8765 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8766 int xx = pos_content % 3;
8767 int yy = pos_content / 3;
8772 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8773 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8777 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8779 int xx = x - bx + 1;
8780 int yy = y - by + 1;
8782 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8783 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8787 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8790 void CheckExit(int x, int y)
8792 if (local_player->gems_still_needed > 0 ||
8793 local_player->sokobanfields_still_needed > 0 ||
8794 local_player->lights_still_needed > 0)
8796 int element = Feld[x][y];
8797 int graphic = el2img(element);
8799 if (IS_ANIMATED(graphic))
8800 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8805 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8808 Feld[x][y] = EL_EXIT_OPENING;
8810 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8813 void CheckExitEM(int x, int y)
8815 if (local_player->gems_still_needed > 0 ||
8816 local_player->sokobanfields_still_needed > 0 ||
8817 local_player->lights_still_needed > 0)
8819 int element = Feld[x][y];
8820 int graphic = el2img(element);
8822 if (IS_ANIMATED(graphic))
8823 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8828 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8831 Feld[x][y] = EL_EM_EXIT_OPENING;
8833 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8836 void CheckExitSteel(int x, int y)
8838 if (local_player->gems_still_needed > 0 ||
8839 local_player->sokobanfields_still_needed > 0 ||
8840 local_player->lights_still_needed > 0)
8842 int element = Feld[x][y];
8843 int graphic = el2img(element);
8845 if (IS_ANIMATED(graphic))
8846 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8851 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8854 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8856 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8859 void CheckExitSteelEM(int x, int y)
8861 if (local_player->gems_still_needed > 0 ||
8862 local_player->sokobanfields_still_needed > 0 ||
8863 local_player->lights_still_needed > 0)
8865 int element = Feld[x][y];
8866 int graphic = el2img(element);
8868 if (IS_ANIMATED(graphic))
8869 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8874 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8877 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8879 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8882 void CheckExitSP(int x, int y)
8884 if (local_player->gems_still_needed > 0)
8886 int element = Feld[x][y];
8887 int graphic = el2img(element);
8889 if (IS_ANIMATED(graphic))
8890 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8895 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8898 Feld[x][y] = EL_SP_EXIT_OPENING;
8900 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8903 static void CloseAllOpenTimegates()
8907 SCAN_PLAYFIELD(x, y)
8909 int element = Feld[x][y];
8911 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8913 Feld[x][y] = EL_TIMEGATE_CLOSING;
8915 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8920 void DrawTwinkleOnField(int x, int y)
8922 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8925 if (Feld[x][y] == EL_BD_DIAMOND)
8928 if (MovDelay[x][y] == 0) /* next animation frame */
8929 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8931 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8935 if (setup.direct_draw && MovDelay[x][y])
8936 SetDrawtoField(DRAW_BUFFERED);
8938 DrawLevelElementAnimation(x, y, Feld[x][y]);
8940 if (MovDelay[x][y] != 0)
8942 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8943 10 - MovDelay[x][y]);
8945 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8947 if (setup.direct_draw)
8951 dest_x = FX + SCREENX(x) * TILEX;
8952 dest_y = FY + SCREENY(y) * TILEY;
8954 BlitBitmap(drawto_field, window,
8955 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8956 SetDrawtoField(DRAW_DIRECT);
8962 void MauerWaechst(int x, int y)
8966 if (!MovDelay[x][y]) /* next animation frame */
8967 MovDelay[x][y] = 3 * delay;
8969 if (MovDelay[x][y]) /* wait some time before next frame */
8973 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8975 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8976 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8978 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8981 if (!MovDelay[x][y])
8983 if (MovDir[x][y] == MV_LEFT)
8985 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8986 DrawLevelField(x - 1, y);
8988 else if (MovDir[x][y] == MV_RIGHT)
8990 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8991 DrawLevelField(x + 1, y);
8993 else if (MovDir[x][y] == MV_UP)
8995 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8996 DrawLevelField(x, y - 1);
9000 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9001 DrawLevelField(x, y + 1);
9004 Feld[x][y] = Store[x][y];
9006 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9007 DrawLevelField(x, y);
9012 void MauerAbleger(int ax, int ay)
9014 int element = Feld[ax][ay];
9015 int graphic = el2img(element);
9016 boolean oben_frei = FALSE, unten_frei = FALSE;
9017 boolean links_frei = FALSE, rechts_frei = FALSE;
9018 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9019 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9020 boolean new_wall = FALSE;
9022 if (IS_ANIMATED(graphic))
9023 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9025 if (!MovDelay[ax][ay]) /* start building new wall */
9026 MovDelay[ax][ay] = 6;
9028 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9031 if (MovDelay[ax][ay])
9035 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9037 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9039 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9041 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9044 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9045 element == EL_EXPANDABLE_WALL_ANY)
9049 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9050 Store[ax][ay-1] = element;
9051 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9052 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9053 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9054 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9059 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9060 Store[ax][ay+1] = element;
9061 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9062 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9063 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9064 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9069 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9070 element == EL_EXPANDABLE_WALL_ANY ||
9071 element == EL_EXPANDABLE_WALL ||
9072 element == EL_BD_EXPANDABLE_WALL)
9076 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9077 Store[ax-1][ay] = element;
9078 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9079 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9080 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9081 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9087 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9088 Store[ax+1][ay] = element;
9089 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9090 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9091 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9092 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9097 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9098 DrawLevelField(ax, ay);
9100 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9102 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9103 unten_massiv = TRUE;
9104 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9105 links_massiv = TRUE;
9106 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9107 rechts_massiv = TRUE;
9109 if (((oben_massiv && unten_massiv) ||
9110 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9111 element == EL_EXPANDABLE_WALL) &&
9112 ((links_massiv && rechts_massiv) ||
9113 element == EL_EXPANDABLE_WALL_VERTICAL))
9114 Feld[ax][ay] = EL_WALL;
9117 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9120 void MauerAblegerStahl(int ax, int ay)
9122 int element = Feld[ax][ay];
9123 int graphic = el2img(element);
9124 boolean oben_frei = FALSE, unten_frei = FALSE;
9125 boolean links_frei = FALSE, rechts_frei = FALSE;
9126 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9127 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9128 boolean new_wall = FALSE;
9130 if (IS_ANIMATED(graphic))
9131 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9133 if (!MovDelay[ax][ay]) /* start building new wall */
9134 MovDelay[ax][ay] = 6;
9136 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9139 if (MovDelay[ax][ay])
9143 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9145 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9147 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9149 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9152 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9153 element == EL_EXPANDABLE_STEELWALL_ANY)
9157 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9158 Store[ax][ay-1] = element;
9159 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9160 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9161 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9162 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9167 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9168 Store[ax][ay+1] = element;
9169 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9170 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9171 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9172 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9177 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9178 element == EL_EXPANDABLE_STEELWALL_ANY)
9182 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9183 Store[ax-1][ay] = element;
9184 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9185 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9186 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9187 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9193 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9194 Store[ax+1][ay] = element;
9195 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9196 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9197 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9198 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9203 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9205 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9206 unten_massiv = TRUE;
9207 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9208 links_massiv = TRUE;
9209 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9210 rechts_massiv = TRUE;
9212 if (((oben_massiv && unten_massiv) ||
9213 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9214 ((links_massiv && rechts_massiv) ||
9215 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9216 Feld[ax][ay] = EL_WALL;
9219 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9222 void CheckForDragon(int x, int y)
9225 boolean dragon_found = FALSE;
9226 static int xy[4][2] =
9234 for (i = 0; i < NUM_DIRECTIONS; i++)
9236 for (j = 0; j < 4; j++)
9238 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9240 if (IN_LEV_FIELD(xx, yy) &&
9241 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9243 if (Feld[xx][yy] == EL_DRAGON)
9244 dragon_found = TRUE;
9253 for (i = 0; i < NUM_DIRECTIONS; i++)
9255 for (j = 0; j < 3; j++)
9257 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9259 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9261 Feld[xx][yy] = EL_EMPTY;
9262 DrawLevelField(xx, yy);
9271 static void InitBuggyBase(int x, int y)
9273 int element = Feld[x][y];
9274 int activating_delay = FRAMES_PER_SECOND / 4;
9277 (element == EL_SP_BUGGY_BASE ?
9278 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9279 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9281 element == EL_SP_BUGGY_BASE_ACTIVE ?
9282 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9285 static void WarnBuggyBase(int x, int y)
9288 static int xy[4][2] =
9296 for (i = 0; i < NUM_DIRECTIONS; i++)
9298 int xx = x + xy[i][0];
9299 int yy = y + xy[i][1];
9301 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9303 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9310 static void InitTrap(int x, int y)
9312 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9315 static void ActivateTrap(int x, int y)
9317 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9320 static void ChangeActiveTrap(int x, int y)
9322 int graphic = IMG_TRAP_ACTIVE;
9324 /* if new animation frame was drawn, correct crumbled sand border */
9325 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9326 DrawLevelFieldCrumbledSand(x, y);
9329 static int getSpecialActionElement(int element, int number, int base_element)
9331 return (element != EL_EMPTY ? element :
9332 number != -1 ? base_element + number - 1 :
9336 static int getModifiedActionNumber(int value_old, int operator, int operand,
9337 int value_min, int value_max)
9339 int value_new = (operator == CA_MODE_SET ? operand :
9340 operator == CA_MODE_ADD ? value_old + operand :
9341 operator == CA_MODE_SUBTRACT ? value_old - operand :
9342 operator == CA_MODE_MULTIPLY ? value_old * operand :
9343 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9344 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9347 return (value_new < value_min ? value_min :
9348 value_new > value_max ? value_max :
9352 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9354 struct ElementInfo *ei = &element_info[element];
9355 struct ElementChangeInfo *change = &ei->change_page[page];
9356 int target_element = change->target_element;
9357 int action_type = change->action_type;
9358 int action_mode = change->action_mode;
9359 int action_arg = change->action_arg;
9362 if (!change->has_action)
9365 /* ---------- determine action paramater values -------------------------- */
9367 int level_time_value =
9368 (level.time > 0 ? TimeLeft :
9371 int action_arg_element =
9372 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9373 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9374 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9377 int action_arg_direction =
9378 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9379 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9380 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9381 change->actual_trigger_side :
9382 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9383 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9386 int action_arg_number_min =
9387 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9390 int action_arg_number_max =
9391 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9392 action_type == CA_SET_LEVEL_GEMS ? 999 :
9393 action_type == CA_SET_LEVEL_TIME ? 9999 :
9394 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9395 action_type == CA_SET_CE_VALUE ? 9999 :
9396 action_type == CA_SET_CE_SCORE ? 9999 :
9399 int action_arg_number_reset =
9400 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9401 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9402 action_type == CA_SET_LEVEL_TIME ? level.time :
9403 action_type == CA_SET_LEVEL_SCORE ? 0 :
9404 #if USE_NEW_CUSTOM_VALUE
9405 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9407 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9409 action_type == CA_SET_CE_SCORE ? 0 :
9412 int action_arg_number =
9413 (action_arg <= CA_ARG_MAX ? action_arg :
9414 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9415 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9416 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9417 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9418 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9419 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9420 #if USE_NEW_CUSTOM_VALUE
9421 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9423 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9425 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9426 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9427 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9428 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9429 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9430 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9431 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9432 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9433 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9434 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9435 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9438 int action_arg_number_old =
9439 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9440 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9441 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9442 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9443 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9446 int action_arg_number_new =
9447 getModifiedActionNumber(action_arg_number_old,
9448 action_mode, action_arg_number,
9449 action_arg_number_min, action_arg_number_max);
9451 int trigger_player_bits =
9452 (change->actual_trigger_player >= EL_PLAYER_1 &&
9453 change->actual_trigger_player <= EL_PLAYER_4 ?
9454 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9457 int action_arg_player_bits =
9458 (action_arg >= CA_ARG_PLAYER_1 &&
9459 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9460 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9463 /* ---------- execute action -------------------------------------------- */
9465 switch (action_type)
9472 /* ---------- level actions ------------------------------------------- */
9474 case CA_RESTART_LEVEL:
9476 game.restart_level = TRUE;
9481 case CA_SHOW_ENVELOPE:
9483 int element = getSpecialActionElement(action_arg_element,
9484 action_arg_number, EL_ENVELOPE_1);
9486 if (IS_ENVELOPE(element))
9487 local_player->show_envelope = element;
9492 case CA_SET_LEVEL_TIME:
9494 if (level.time > 0) /* only modify limited time value */
9496 TimeLeft = action_arg_number_new;
9499 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9501 DisplayGameControlValues();
9503 DrawGameValue_Time(TimeLeft);
9506 if (!TimeLeft && setup.time_limit)
9507 for (i = 0; i < MAX_PLAYERS; i++)
9508 KillPlayer(&stored_player[i]);
9514 case CA_SET_LEVEL_SCORE:
9516 local_player->score = action_arg_number_new;
9519 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9521 DisplayGameControlValues();
9523 DrawGameValue_Score(local_player->score);
9529 case CA_SET_LEVEL_GEMS:
9531 local_player->gems_still_needed = action_arg_number_new;
9534 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9536 DisplayGameControlValues();
9538 DrawGameValue_Emeralds(local_player->gems_still_needed);
9544 #if !USE_PLAYER_GRAVITY
9545 case CA_SET_LEVEL_GRAVITY:
9547 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9548 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9549 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9555 case CA_SET_LEVEL_WIND:
9557 game.wind_direction = action_arg_direction;
9562 /* ---------- player actions ------------------------------------------ */
9564 case CA_MOVE_PLAYER:
9566 /* automatically move to the next field in specified direction */
9567 for (i = 0; i < MAX_PLAYERS; i++)
9568 if (trigger_player_bits & (1 << i))
9569 stored_player[i].programmed_action = action_arg_direction;
9574 case CA_EXIT_PLAYER:
9576 for (i = 0; i < MAX_PLAYERS; i++)
9577 if (action_arg_player_bits & (1 << i))
9578 PlayerWins(&stored_player[i]);
9583 case CA_KILL_PLAYER:
9585 for (i = 0; i < MAX_PLAYERS; i++)
9586 if (action_arg_player_bits & (1 << i))
9587 KillPlayer(&stored_player[i]);
9592 case CA_SET_PLAYER_KEYS:
9594 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9595 int element = getSpecialActionElement(action_arg_element,
9596 action_arg_number, EL_KEY_1);
9598 if (IS_KEY(element))
9600 for (i = 0; i < MAX_PLAYERS; i++)
9602 if (trigger_player_bits & (1 << i))
9604 stored_player[i].key[KEY_NR(element)] = key_state;
9606 DrawGameDoorValues();
9614 case CA_SET_PLAYER_SPEED:
9616 for (i = 0; i < MAX_PLAYERS; i++)
9618 if (trigger_player_bits & (1 << i))
9620 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9622 if (action_arg == CA_ARG_SPEED_FASTER &&
9623 stored_player[i].cannot_move)
9625 action_arg_number = STEPSIZE_VERY_SLOW;
9627 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9628 action_arg == CA_ARG_SPEED_FASTER)
9630 action_arg_number = 2;
9631 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9634 else if (action_arg == CA_ARG_NUMBER_RESET)
9636 action_arg_number = level.initial_player_stepsize[i];
9640 getModifiedActionNumber(move_stepsize,
9643 action_arg_number_min,
9644 action_arg_number_max);
9646 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9653 case CA_SET_PLAYER_SHIELD:
9655 for (i = 0; i < MAX_PLAYERS; i++)
9657 if (trigger_player_bits & (1 << i))
9659 if (action_arg == CA_ARG_SHIELD_OFF)
9661 stored_player[i].shield_normal_time_left = 0;
9662 stored_player[i].shield_deadly_time_left = 0;
9664 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9666 stored_player[i].shield_normal_time_left = 999999;
9668 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9670 stored_player[i].shield_normal_time_left = 999999;
9671 stored_player[i].shield_deadly_time_left = 999999;
9679 #if USE_PLAYER_GRAVITY
9680 case CA_SET_PLAYER_GRAVITY:
9682 for (i = 0; i < MAX_PLAYERS; i++)
9684 if (trigger_player_bits & (1 << i))
9686 stored_player[i].gravity =
9687 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9688 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9689 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9690 stored_player[i].gravity);
9698 case CA_SET_PLAYER_ARTWORK:
9700 for (i = 0; i < MAX_PLAYERS; i++)
9702 if (trigger_player_bits & (1 << i))
9704 int artwork_element = action_arg_element;
9706 if (action_arg == CA_ARG_ELEMENT_RESET)
9708 (level.use_artwork_element[i] ? level.artwork_element[i] :
9709 stored_player[i].element_nr);
9711 #if USE_GFX_RESET_PLAYER_ARTWORK
9712 if (stored_player[i].artwork_element != artwork_element)
9713 stored_player[i].Frame = 0;
9716 stored_player[i].artwork_element = artwork_element;
9718 SetPlayerWaiting(&stored_player[i], FALSE);
9720 /* set number of special actions for bored and sleeping animation */
9721 stored_player[i].num_special_action_bored =
9722 get_num_special_action(artwork_element,
9723 ACTION_BORING_1, ACTION_BORING_LAST);
9724 stored_player[i].num_special_action_sleeping =
9725 get_num_special_action(artwork_element,
9726 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9733 /* ---------- CE actions ---------------------------------------------- */
9735 case CA_SET_CE_VALUE:
9737 #if USE_NEW_CUSTOM_VALUE
9738 int last_ce_value = CustomValue[x][y];
9740 CustomValue[x][y] = action_arg_number_new;
9742 if (CustomValue[x][y] != last_ce_value)
9744 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9745 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9747 if (CustomValue[x][y] == 0)
9749 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9750 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9758 case CA_SET_CE_SCORE:
9760 #if USE_NEW_CUSTOM_VALUE
9761 int last_ce_score = ei->collect_score;
9763 ei->collect_score = action_arg_number_new;
9765 if (ei->collect_score != last_ce_score)
9767 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9768 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9770 if (ei->collect_score == 0)
9774 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9775 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9778 This is a very special case that seems to be a mixture between
9779 CheckElementChange() and CheckTriggeredElementChange(): while
9780 the first one only affects single elements that are triggered
9781 directly, the second one affects multiple elements in the playfield
9782 that are triggered indirectly by another element. This is a third
9783 case: Changing the CE score always affects multiple identical CEs,
9784 so every affected CE must be checked, not only the single CE for
9785 which the CE score was changed in the first place (as every instance
9786 of that CE shares the same CE score, and therefore also can change)!
9788 SCAN_PLAYFIELD(xx, yy)
9790 if (Feld[xx][yy] == element)
9791 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9792 CE_SCORE_GETS_ZERO);
9801 /* ---------- engine actions ------------------------------------------ */
9803 case CA_SET_ENGINE_SCAN_MODE:
9805 InitPlayfieldScanMode(action_arg);
9815 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9817 int old_element = Feld[x][y];
9818 int new_element = GetElementFromGroupElement(element);
9819 int previous_move_direction = MovDir[x][y];
9820 #if USE_NEW_CUSTOM_VALUE
9821 int last_ce_value = CustomValue[x][y];
9823 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9824 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9825 boolean add_player_onto_element = (new_element_is_player &&
9826 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9827 /* this breaks SnakeBite when a snake is
9828 halfway through a door that closes */
9829 /* NOW FIXED AT LEVEL INIT IN files.c */
9830 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9832 IS_WALKABLE(old_element));
9835 /* check if element under the player changes from accessible to unaccessible
9836 (needed for special case of dropping element which then changes) */
9837 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9838 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9846 if (!add_player_onto_element)
9848 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9849 RemoveMovingField(x, y);
9853 Feld[x][y] = new_element;
9855 #if !USE_GFX_RESET_GFX_ANIMATION
9856 ResetGfxAnimation(x, y);
9857 ResetRandomAnimationValue(x, y);
9860 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9861 MovDir[x][y] = previous_move_direction;
9863 #if USE_NEW_CUSTOM_VALUE
9864 if (element_info[new_element].use_last_ce_value)
9865 CustomValue[x][y] = last_ce_value;
9868 InitField_WithBug1(x, y, FALSE);
9870 new_element = Feld[x][y]; /* element may have changed */
9872 #if USE_GFX_RESET_GFX_ANIMATION
9873 ResetGfxAnimation(x, y);
9874 ResetRandomAnimationValue(x, y);
9877 DrawLevelField(x, y);
9879 if (GFX_CRUMBLED(new_element))
9880 DrawLevelFieldCrumbledSandNeighbours(x, y);
9884 /* check if element under the player changes from accessible to unaccessible
9885 (needed for special case of dropping element which then changes) */
9886 /* (must be checked after creating new element for walkable group elements) */
9887 #if USE_FIX_KILLED_BY_NON_WALKABLE
9888 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9889 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9896 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9897 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9906 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9907 if (new_element_is_player)
9908 RelocatePlayer(x, y, new_element);
9911 ChangeCount[x][y]++; /* count number of changes in the same frame */
9913 TestIfBadThingTouchesPlayer(x, y);
9914 TestIfPlayerTouchesCustomElement(x, y);
9915 TestIfElementTouchesCustomElement(x, y);
9918 static void CreateField(int x, int y, int element)
9920 CreateFieldExt(x, y, element, FALSE);
9923 static void CreateElementFromChange(int x, int y, int element)
9925 element = GET_VALID_RUNTIME_ELEMENT(element);
9927 #if USE_STOP_CHANGED_ELEMENTS
9928 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9930 int old_element = Feld[x][y];
9932 /* prevent changed element from moving in same engine frame
9933 unless both old and new element can either fall or move */
9934 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9935 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9940 CreateFieldExt(x, y, element, TRUE);
9943 static boolean ChangeElement(int x, int y, int element, int page)
9945 struct ElementInfo *ei = &element_info[element];
9946 struct ElementChangeInfo *change = &ei->change_page[page];
9947 int ce_value = CustomValue[x][y];
9948 int ce_score = ei->collect_score;
9950 int old_element = Feld[x][y];
9952 /* always use default change event to prevent running into a loop */
9953 if (ChangeEvent[x][y] == -1)
9954 ChangeEvent[x][y] = CE_DELAY;
9956 if (ChangeEvent[x][y] == CE_DELAY)
9958 /* reset actual trigger element, trigger player and action element */
9959 change->actual_trigger_element = EL_EMPTY;
9960 change->actual_trigger_player = EL_PLAYER_1;
9961 change->actual_trigger_side = CH_SIDE_NONE;
9962 change->actual_trigger_ce_value = 0;
9963 change->actual_trigger_ce_score = 0;
9966 /* do not change elements more than a specified maximum number of changes */
9967 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9970 ChangeCount[x][y]++; /* count number of changes in the same frame */
9972 if (change->explode)
9979 if (change->use_target_content)
9981 boolean complete_replace = TRUE;
9982 boolean can_replace[3][3];
9985 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9988 boolean is_walkable;
9989 boolean is_diggable;
9990 boolean is_collectible;
9991 boolean is_removable;
9992 boolean is_destructible;
9993 int ex = x + xx - 1;
9994 int ey = y + yy - 1;
9995 int content_element = change->target_content.e[xx][yy];
9998 can_replace[xx][yy] = TRUE;
10000 if (ex == x && ey == y) /* do not check changing element itself */
10003 if (content_element == EL_EMPTY_SPACE)
10005 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10010 if (!IN_LEV_FIELD(ex, ey))
10012 can_replace[xx][yy] = FALSE;
10013 complete_replace = FALSE;
10020 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10021 e = MovingOrBlocked2Element(ex, ey);
10023 is_empty = (IS_FREE(ex, ey) ||
10024 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10026 is_walkable = (is_empty || IS_WALKABLE(e));
10027 is_diggable = (is_empty || IS_DIGGABLE(e));
10028 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10029 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10030 is_removable = (is_diggable || is_collectible);
10032 can_replace[xx][yy] =
10033 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10034 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10035 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10036 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10037 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10038 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10039 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10041 if (!can_replace[xx][yy])
10042 complete_replace = FALSE;
10045 if (!change->only_if_complete || complete_replace)
10047 boolean something_has_changed = FALSE;
10049 if (change->only_if_complete && change->use_random_replace &&
10050 RND(100) < change->random_percentage)
10053 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10055 int ex = x + xx - 1;
10056 int ey = y + yy - 1;
10057 int content_element;
10059 if (can_replace[xx][yy] && (!change->use_random_replace ||
10060 RND(100) < change->random_percentage))
10062 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10063 RemoveMovingField(ex, ey);
10065 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10067 content_element = change->target_content.e[xx][yy];
10068 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10069 ce_value, ce_score);
10071 CreateElementFromChange(ex, ey, target_element);
10073 something_has_changed = TRUE;
10075 /* for symmetry reasons, freeze newly created border elements */
10076 if (ex != x || ey != y)
10077 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10081 if (something_has_changed)
10083 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10084 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10090 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10091 ce_value, ce_score);
10093 if (element == EL_DIAGONAL_GROWING ||
10094 element == EL_DIAGONAL_SHRINKING)
10096 target_element = Store[x][y];
10098 Store[x][y] = EL_EMPTY;
10101 CreateElementFromChange(x, y, target_element);
10103 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10104 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10107 /* this uses direct change before indirect change */
10108 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10113 #if USE_NEW_DELAYED_ACTION
10115 static void HandleElementChange(int x, int y, int page)
10117 int element = MovingOrBlocked2Element(x, y);
10118 struct ElementInfo *ei = &element_info[element];
10119 struct ElementChangeInfo *change = &ei->change_page[page];
10122 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10123 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10126 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10127 x, y, element, element_info[element].token_name);
10128 printf("HandleElementChange(): This should never happen!\n");
10133 /* this can happen with classic bombs on walkable, changing elements */
10134 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10137 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10138 ChangeDelay[x][y] = 0;
10144 if (ChangeDelay[x][y] == 0) /* initialize element change */
10146 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10148 if (change->can_change)
10151 /* !!! not clear why graphic animation should be reset at all here !!! */
10152 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10153 #if USE_GFX_RESET_WHEN_NOT_MOVING
10154 /* when a custom element is about to change (for example by change delay),
10155 do not reset graphic animation when the custom element is moving */
10156 if (!IS_MOVING(x, y))
10159 ResetGfxAnimation(x, y);
10160 ResetRandomAnimationValue(x, y);
10164 if (change->pre_change_function)
10165 change->pre_change_function(x, y);
10169 ChangeDelay[x][y]--;
10171 if (ChangeDelay[x][y] != 0) /* continue element change */
10173 if (change->can_change)
10175 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10177 if (IS_ANIMATED(graphic))
10178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10180 if (change->change_function)
10181 change->change_function(x, y);
10184 else /* finish element change */
10186 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10188 page = ChangePage[x][y];
10189 ChangePage[x][y] = -1;
10191 change = &ei->change_page[page];
10194 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10196 ChangeDelay[x][y] = 1; /* try change after next move step */
10197 ChangePage[x][y] = page; /* remember page to use for change */
10202 if (change->can_change)
10204 if (ChangeElement(x, y, element, page))
10206 if (change->post_change_function)
10207 change->post_change_function(x, y);
10211 if (change->has_action)
10212 ExecuteCustomElementAction(x, y, element, page);
10218 static void HandleElementChange(int x, int y, int page)
10220 int element = MovingOrBlocked2Element(x, y);
10221 struct ElementInfo *ei = &element_info[element];
10222 struct ElementChangeInfo *change = &ei->change_page[page];
10225 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10228 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10229 x, y, element, element_info[element].token_name);
10230 printf("HandleElementChange(): This should never happen!\n");
10235 /* this can happen with classic bombs on walkable, changing elements */
10236 if (!CAN_CHANGE(element))
10239 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10240 ChangeDelay[x][y] = 0;
10246 if (ChangeDelay[x][y] == 0) /* initialize element change */
10248 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10250 ResetGfxAnimation(x, y);
10251 ResetRandomAnimationValue(x, y);
10253 if (change->pre_change_function)
10254 change->pre_change_function(x, y);
10257 ChangeDelay[x][y]--;
10259 if (ChangeDelay[x][y] != 0) /* continue element change */
10261 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10263 if (IS_ANIMATED(graphic))
10264 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10266 if (change->change_function)
10267 change->change_function(x, y);
10269 else /* finish element change */
10271 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10273 page = ChangePage[x][y];
10274 ChangePage[x][y] = -1;
10276 change = &ei->change_page[page];
10279 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10281 ChangeDelay[x][y] = 1; /* try change after next move step */
10282 ChangePage[x][y] = page; /* remember page to use for change */
10287 if (ChangeElement(x, y, element, page))
10289 if (change->post_change_function)
10290 change->post_change_function(x, y);
10297 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10298 int trigger_element,
10300 int trigger_player,
10304 boolean change_done_any = FALSE;
10305 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10308 if (!(trigger_events[trigger_element][trigger_event]))
10312 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10313 trigger_event, recursion_loop_depth, recursion_loop_detected,
10314 recursion_loop_element, EL_NAME(recursion_loop_element));
10317 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10319 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10321 int element = EL_CUSTOM_START + i;
10322 boolean change_done = FALSE;
10325 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10326 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10329 for (p = 0; p < element_info[element].num_change_pages; p++)
10331 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10333 if (change->can_change_or_has_action &&
10334 change->has_event[trigger_event] &&
10335 change->trigger_side & trigger_side &&
10336 change->trigger_player & trigger_player &&
10337 change->trigger_page & trigger_page_bits &&
10338 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10340 change->actual_trigger_element = trigger_element;
10341 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10342 change->actual_trigger_side = trigger_side;
10343 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10344 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10346 if ((change->can_change && !change_done) || change->has_action)
10350 SCAN_PLAYFIELD(x, y)
10352 if (Feld[x][y] == element)
10354 if (change->can_change && !change_done)
10356 ChangeDelay[x][y] = 1;
10357 ChangeEvent[x][y] = trigger_event;
10359 HandleElementChange(x, y, p);
10361 #if USE_NEW_DELAYED_ACTION
10362 else if (change->has_action)
10364 ExecuteCustomElementAction(x, y, element, p);
10365 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10368 if (change->has_action)
10370 ExecuteCustomElementAction(x, y, element, p);
10371 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10377 if (change->can_change)
10379 change_done = TRUE;
10380 change_done_any = TRUE;
10387 RECURSION_LOOP_DETECTION_END();
10389 return change_done_any;
10392 static boolean CheckElementChangeExt(int x, int y,
10394 int trigger_element,
10396 int trigger_player,
10399 boolean change_done = FALSE;
10402 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10403 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10406 if (Feld[x][y] == EL_BLOCKED)
10408 Blocked2Moving(x, y, &x, &y);
10409 element = Feld[x][y];
10413 /* check if element has already changed */
10414 if (Feld[x][y] != element)
10417 /* check if element has already changed or is about to change after moving */
10418 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10419 Feld[x][y] != element) ||
10421 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10422 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10423 ChangePage[x][y] != -1)))
10428 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10429 trigger_event, recursion_loop_depth, recursion_loop_detected,
10430 recursion_loop_element, EL_NAME(recursion_loop_element));
10433 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10435 for (p = 0; p < element_info[element].num_change_pages; p++)
10437 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10439 /* check trigger element for all events where the element that is checked
10440 for changing interacts with a directly adjacent element -- this is
10441 different to element changes that affect other elements to change on the
10442 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10443 boolean check_trigger_element =
10444 (trigger_event == CE_TOUCHING_X ||
10445 trigger_event == CE_HITTING_X ||
10446 trigger_event == CE_HIT_BY_X ||
10448 /* this one was forgotten until 3.2.3 */
10449 trigger_event == CE_DIGGING_X);
10452 if (change->can_change_or_has_action &&
10453 change->has_event[trigger_event] &&
10454 change->trigger_side & trigger_side &&
10455 change->trigger_player & trigger_player &&
10456 (!check_trigger_element ||
10457 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10459 change->actual_trigger_element = trigger_element;
10460 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10461 change->actual_trigger_side = trigger_side;
10462 change->actual_trigger_ce_value = CustomValue[x][y];
10463 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10465 /* special case: trigger element not at (x,y) position for some events */
10466 if (check_trigger_element)
10478 { 0, 0 }, { 0, 0 }, { 0, 0 },
10482 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10483 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10485 change->actual_trigger_ce_value = CustomValue[xx][yy];
10486 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10489 if (change->can_change && !change_done)
10491 ChangeDelay[x][y] = 1;
10492 ChangeEvent[x][y] = trigger_event;
10494 HandleElementChange(x, y, p);
10496 change_done = TRUE;
10498 #if USE_NEW_DELAYED_ACTION
10499 else if (change->has_action)
10501 ExecuteCustomElementAction(x, y, element, p);
10502 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10505 if (change->has_action)
10507 ExecuteCustomElementAction(x, y, element, p);
10508 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10514 RECURSION_LOOP_DETECTION_END();
10516 return change_done;
10519 static void PlayPlayerSound(struct PlayerInfo *player)
10521 int jx = player->jx, jy = player->jy;
10522 int sound_element = player->artwork_element;
10523 int last_action = player->last_action_waiting;
10524 int action = player->action_waiting;
10526 if (player->is_waiting)
10528 if (action != last_action)
10529 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10531 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10535 if (action != last_action)
10536 StopSound(element_info[sound_element].sound[last_action]);
10538 if (last_action == ACTION_SLEEPING)
10539 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10543 static void PlayAllPlayersSound()
10547 for (i = 0; i < MAX_PLAYERS; i++)
10548 if (stored_player[i].active)
10549 PlayPlayerSound(&stored_player[i]);
10552 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10554 boolean last_waiting = player->is_waiting;
10555 int move_dir = player->MovDir;
10557 player->dir_waiting = move_dir;
10558 player->last_action_waiting = player->action_waiting;
10562 if (!last_waiting) /* not waiting -> waiting */
10564 player->is_waiting = TRUE;
10566 player->frame_counter_bored =
10568 game.player_boring_delay_fixed +
10569 GetSimpleRandom(game.player_boring_delay_random);
10570 player->frame_counter_sleeping =
10572 game.player_sleeping_delay_fixed +
10573 GetSimpleRandom(game.player_sleeping_delay_random);
10575 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10578 if (game.player_sleeping_delay_fixed +
10579 game.player_sleeping_delay_random > 0 &&
10580 player->anim_delay_counter == 0 &&
10581 player->post_delay_counter == 0 &&
10582 FrameCounter >= player->frame_counter_sleeping)
10583 player->is_sleeping = TRUE;
10584 else if (game.player_boring_delay_fixed +
10585 game.player_boring_delay_random > 0 &&
10586 FrameCounter >= player->frame_counter_bored)
10587 player->is_bored = TRUE;
10589 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10590 player->is_bored ? ACTION_BORING :
10593 if (player->is_sleeping && player->use_murphy)
10595 /* special case for sleeping Murphy when leaning against non-free tile */
10597 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10598 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10599 !IS_MOVING(player->jx - 1, player->jy)))
10600 move_dir = MV_LEFT;
10601 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10602 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10603 !IS_MOVING(player->jx + 1, player->jy)))
10604 move_dir = MV_RIGHT;
10606 player->is_sleeping = FALSE;
10608 player->dir_waiting = move_dir;
10611 if (player->is_sleeping)
10613 if (player->num_special_action_sleeping > 0)
10615 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10617 int last_special_action = player->special_action_sleeping;
10618 int num_special_action = player->num_special_action_sleeping;
10619 int special_action =
10620 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10621 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10622 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10623 last_special_action + 1 : ACTION_SLEEPING);
10624 int special_graphic =
10625 el_act_dir2img(player->artwork_element, special_action, move_dir);
10627 player->anim_delay_counter =
10628 graphic_info[special_graphic].anim_delay_fixed +
10629 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10630 player->post_delay_counter =
10631 graphic_info[special_graphic].post_delay_fixed +
10632 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10634 player->special_action_sleeping = special_action;
10637 if (player->anim_delay_counter > 0)
10639 player->action_waiting = player->special_action_sleeping;
10640 player->anim_delay_counter--;
10642 else if (player->post_delay_counter > 0)
10644 player->post_delay_counter--;
10648 else if (player->is_bored)
10650 if (player->num_special_action_bored > 0)
10652 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10654 int special_action =
10655 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10656 int special_graphic =
10657 el_act_dir2img(player->artwork_element, special_action, move_dir);
10659 player->anim_delay_counter =
10660 graphic_info[special_graphic].anim_delay_fixed +
10661 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10662 player->post_delay_counter =
10663 graphic_info[special_graphic].post_delay_fixed +
10664 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10666 player->special_action_bored = special_action;
10669 if (player->anim_delay_counter > 0)
10671 player->action_waiting = player->special_action_bored;
10672 player->anim_delay_counter--;
10674 else if (player->post_delay_counter > 0)
10676 player->post_delay_counter--;
10681 else if (last_waiting) /* waiting -> not waiting */
10683 player->is_waiting = FALSE;
10684 player->is_bored = FALSE;
10685 player->is_sleeping = FALSE;
10687 player->frame_counter_bored = -1;
10688 player->frame_counter_sleeping = -1;
10690 player->anim_delay_counter = 0;
10691 player->post_delay_counter = 0;
10693 player->dir_waiting = player->MovDir;
10694 player->action_waiting = ACTION_DEFAULT;
10696 player->special_action_bored = ACTION_DEFAULT;
10697 player->special_action_sleeping = ACTION_DEFAULT;
10701 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10703 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10704 int left = player_action & JOY_LEFT;
10705 int right = player_action & JOY_RIGHT;
10706 int up = player_action & JOY_UP;
10707 int down = player_action & JOY_DOWN;
10708 int button1 = player_action & JOY_BUTTON_1;
10709 int button2 = player_action & JOY_BUTTON_2;
10710 int dx = (left ? -1 : right ? 1 : 0);
10711 int dy = (up ? -1 : down ? 1 : 0);
10713 if (!player->active || tape.pausing)
10719 snapped = SnapField(player, dx, dy);
10723 dropped = DropElement(player);
10725 moved = MovePlayer(player, dx, dy);
10728 if (tape.single_step && tape.recording && !tape.pausing)
10730 if (button1 || (dropped && !moved))
10732 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10733 SnapField(player, 0, 0); /* stop snapping */
10737 SetPlayerWaiting(player, FALSE);
10739 return player_action;
10743 /* no actions for this player (no input at player's configured device) */
10745 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10746 SnapField(player, 0, 0);
10747 CheckGravityMovementWhenNotMoving(player);
10749 if (player->MovPos == 0)
10750 SetPlayerWaiting(player, TRUE);
10752 if (player->MovPos == 0) /* needed for tape.playing */
10753 player->is_moving = FALSE;
10755 player->is_dropping = FALSE;
10756 player->is_dropping_pressed = FALSE;
10757 player->drop_pressed_delay = 0;
10763 static void CheckLevelTime()
10767 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10769 if (level.native_em_level->lev->home == 0) /* all players at home */
10771 PlayerWins(local_player);
10773 AllPlayersGone = TRUE;
10775 level.native_em_level->lev->home = -1;
10778 if (level.native_em_level->ply[0]->alive == 0 &&
10779 level.native_em_level->ply[1]->alive == 0 &&
10780 level.native_em_level->ply[2]->alive == 0 &&
10781 level.native_em_level->ply[3]->alive == 0) /* all dead */
10782 AllPlayersGone = TRUE;
10785 if (TimeFrames >= FRAMES_PER_SECOND)
10790 for (i = 0; i < MAX_PLAYERS; i++)
10792 struct PlayerInfo *player = &stored_player[i];
10794 if (SHIELD_ON(player))
10796 player->shield_normal_time_left--;
10798 if (player->shield_deadly_time_left > 0)
10799 player->shield_deadly_time_left--;
10803 if (!local_player->LevelSolved && !level.use_step_counter)
10811 if (TimeLeft <= 10 && setup.time_limit)
10812 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10815 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10817 DisplayGameControlValues();
10819 DrawGameValue_Time(TimeLeft);
10822 if (!TimeLeft && setup.time_limit)
10824 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10825 level.native_em_level->lev->killed_out_of_time = TRUE;
10827 for (i = 0; i < MAX_PLAYERS; i++)
10828 KillPlayer(&stored_player[i]);
10832 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10834 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10836 DisplayGameControlValues();
10839 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10840 DrawGameValue_Time(TimePlayed);
10843 level.native_em_level->lev->time =
10844 (level.time == 0 ? TimePlayed : TimeLeft);
10847 if (tape.recording || tape.playing)
10848 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10851 DrawGameDoorValues();
10854 void AdvanceFrameAndPlayerCounters(int player_nr)
10858 /* advance frame counters (global frame counter and time frame counter) */
10862 /* advance player counters (counters for move delay, move animation etc.) */
10863 for (i = 0; i < MAX_PLAYERS; i++)
10865 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10866 int move_delay_value = stored_player[i].move_delay_value;
10867 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10869 if (!advance_player_counters) /* not all players may be affected */
10872 #if USE_NEW_PLAYER_ANIM
10873 if (move_frames == 0) /* less than one move per game frame */
10875 int stepsize = TILEX / move_delay_value;
10876 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10877 int count = (stored_player[i].is_moving ?
10878 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10880 if (count % delay == 0)
10885 stored_player[i].Frame += move_frames;
10887 if (stored_player[i].MovPos != 0)
10888 stored_player[i].StepFrame += move_frames;
10890 if (stored_player[i].move_delay > 0)
10891 stored_player[i].move_delay--;
10893 /* due to bugs in previous versions, counter must count up, not down */
10894 if (stored_player[i].push_delay != -1)
10895 stored_player[i].push_delay++;
10897 if (stored_player[i].drop_delay > 0)
10898 stored_player[i].drop_delay--;
10900 if (stored_player[i].is_dropping_pressed)
10901 stored_player[i].drop_pressed_delay++;
10905 void StartGameActions(boolean init_network_game, boolean record_tape,
10908 unsigned long new_random_seed = InitRND(random_seed);
10911 TapeStartRecording(new_random_seed);
10913 #if defined(NETWORK_AVALIABLE)
10914 if (init_network_game)
10916 SendToServer_StartPlaying();
10927 static unsigned long game_frame_delay = 0;
10928 unsigned long game_frame_delay_value;
10929 byte *recorded_player_action;
10930 byte summarized_player_action = 0;
10931 byte tape_action[MAX_PLAYERS];
10934 /* detect endless loops, caused by custom element programming */
10935 if (recursion_loop_detected && recursion_loop_depth == 0)
10937 char *message = getStringCat3("Internal Error ! Element ",
10938 EL_NAME(recursion_loop_element),
10939 " caused endless loop ! Quit the game ?");
10941 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10942 EL_NAME(recursion_loop_element));
10944 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10946 recursion_loop_detected = FALSE; /* if game should be continued */
10953 if (game.restart_level)
10954 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10956 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10958 if (level.native_em_level->lev->home == 0) /* all players at home */
10960 PlayerWins(local_player);
10962 AllPlayersGone = TRUE;
10964 level.native_em_level->lev->home = -1;
10967 if (level.native_em_level->ply[0]->alive == 0 &&
10968 level.native_em_level->ply[1]->alive == 0 &&
10969 level.native_em_level->ply[2]->alive == 0 &&
10970 level.native_em_level->ply[3]->alive == 0) /* all dead */
10971 AllPlayersGone = TRUE;
10974 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10977 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10980 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10983 game_frame_delay_value =
10984 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10986 if (tape.playing && tape.warp_forward && !tape.pausing)
10987 game_frame_delay_value = 0;
10989 /* ---------- main game synchronization point ---------- */
10991 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10993 if (network_playing && !network_player_action_received)
10995 /* try to get network player actions in time */
10997 #if defined(NETWORK_AVALIABLE)
10998 /* last chance to get network player actions without main loop delay */
10999 HandleNetworking();
11002 /* game was quit by network peer */
11003 if (game_status != GAME_MODE_PLAYING)
11006 if (!network_player_action_received)
11007 return; /* failed to get network player actions in time */
11009 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11015 /* at this point we know that we really continue executing the game */
11017 network_player_action_received = FALSE;
11019 /* when playing tape, read previously recorded player input from tape data */
11020 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11023 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11028 if (tape.set_centered_player)
11030 game.centered_player_nr_next = tape.centered_player_nr_next;
11031 game.set_centered_player = TRUE;
11034 for (i = 0; i < MAX_PLAYERS; i++)
11036 summarized_player_action |= stored_player[i].action;
11038 if (!network_playing)
11039 stored_player[i].effective_action = stored_player[i].action;
11042 #if defined(NETWORK_AVALIABLE)
11043 if (network_playing)
11044 SendToServer_MovePlayer(summarized_player_action);
11047 if (!options.network && !setup.team_mode)
11048 local_player->effective_action = summarized_player_action;
11050 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11052 for (i = 0; i < MAX_PLAYERS; i++)
11053 stored_player[i].effective_action =
11054 (i == game.centered_player_nr ? summarized_player_action : 0);
11057 if (recorded_player_action != NULL)
11058 for (i = 0; i < MAX_PLAYERS; i++)
11059 stored_player[i].effective_action = recorded_player_action[i];
11061 for (i = 0; i < MAX_PLAYERS; i++)
11063 tape_action[i] = stored_player[i].effective_action;
11065 /* (this can only happen in the R'n'D game engine) */
11066 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11067 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11070 /* only record actions from input devices, but not programmed actions */
11071 if (tape.recording)
11072 TapeRecordAction(tape_action);
11074 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11076 GameActions_EM_Main();
11084 void GameActions_EM_Main()
11086 byte effective_action[MAX_PLAYERS];
11087 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11090 for (i = 0; i < MAX_PLAYERS; i++)
11091 effective_action[i] = stored_player[i].effective_action;
11093 GameActions_EM(effective_action, warp_mode);
11097 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11100 void GameActions_RND()
11102 int magic_wall_x = 0, magic_wall_y = 0;
11103 int i, x, y, element, graphic;
11105 InitPlayfieldScanModeVars();
11107 #if USE_ONE_MORE_CHANGE_PER_FRAME
11108 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11110 SCAN_PLAYFIELD(x, y)
11112 ChangeCount[x][y] = 0;
11113 ChangeEvent[x][y] = -1;
11118 if (game.set_centered_player)
11120 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11122 /* switching to "all players" only possible if all players fit to screen */
11123 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11125 game.centered_player_nr_next = game.centered_player_nr;
11126 game.set_centered_player = FALSE;
11129 /* do not switch focus to non-existing (or non-active) player */
11130 if (game.centered_player_nr_next >= 0 &&
11131 !stored_player[game.centered_player_nr_next].active)
11133 game.centered_player_nr_next = game.centered_player_nr;
11134 game.set_centered_player = FALSE;
11138 if (game.set_centered_player &&
11139 ScreenMovPos == 0) /* screen currently aligned at tile position */
11143 if (game.centered_player_nr_next == -1)
11145 setScreenCenteredToAllPlayers(&sx, &sy);
11149 sx = stored_player[game.centered_player_nr_next].jx;
11150 sy = stored_player[game.centered_player_nr_next].jy;
11153 game.centered_player_nr = game.centered_player_nr_next;
11154 game.set_centered_player = FALSE;
11156 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11157 DrawGameDoorValues();
11160 for (i = 0; i < MAX_PLAYERS; i++)
11162 int actual_player_action = stored_player[i].effective_action;
11165 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11166 - rnd_equinox_tetrachloride 048
11167 - rnd_equinox_tetrachloride_ii 096
11168 - rnd_emanuel_schmieg 002
11169 - doctor_sloan_ww 001, 020
11171 if (stored_player[i].MovPos == 0)
11172 CheckGravityMovement(&stored_player[i]);
11175 /* overwrite programmed action with tape action */
11176 if (stored_player[i].programmed_action)
11177 actual_player_action = stored_player[i].programmed_action;
11179 PlayerActions(&stored_player[i], actual_player_action);
11181 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11184 ScrollScreen(NULL, SCROLL_GO_ON);
11186 /* for backwards compatibility, the following code emulates a fixed bug that
11187 occured when pushing elements (causing elements that just made their last
11188 pushing step to already (if possible) make their first falling step in the
11189 same game frame, which is bad); this code is also needed to use the famous
11190 "spring push bug" which is used in older levels and might be wanted to be
11191 used also in newer levels, but in this case the buggy pushing code is only
11192 affecting the "spring" element and no other elements */
11194 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11196 for (i = 0; i < MAX_PLAYERS; i++)
11198 struct PlayerInfo *player = &stored_player[i];
11199 int x = player->jx;
11200 int y = player->jy;
11202 if (player->active && player->is_pushing && player->is_moving &&
11204 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11205 Feld[x][y] == EL_SPRING))
11207 ContinueMoving(x, y);
11209 /* continue moving after pushing (this is actually a bug) */
11210 if (!IS_MOVING(x, y))
11211 Stop[x][y] = FALSE;
11217 debug_print_timestamp(0, "start main loop profiling");
11220 SCAN_PLAYFIELD(x, y)
11222 ChangeCount[x][y] = 0;
11223 ChangeEvent[x][y] = -1;
11225 /* this must be handled before main playfield loop */
11226 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11229 if (MovDelay[x][y] <= 0)
11233 #if USE_NEW_SNAP_DELAY
11234 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11237 if (MovDelay[x][y] <= 0)
11240 DrawLevelField(x, y);
11242 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11248 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11250 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11251 printf("GameActions(): This should never happen!\n");
11253 ChangePage[x][y] = -1;
11257 Stop[x][y] = FALSE;
11258 if (WasJustMoving[x][y] > 0)
11259 WasJustMoving[x][y]--;
11260 if (WasJustFalling[x][y] > 0)
11261 WasJustFalling[x][y]--;
11262 if (CheckCollision[x][y] > 0)
11263 CheckCollision[x][y]--;
11264 if (CheckImpact[x][y] > 0)
11265 CheckImpact[x][y]--;
11269 /* reset finished pushing action (not done in ContinueMoving() to allow
11270 continuous pushing animation for elements with zero push delay) */
11271 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11273 ResetGfxAnimation(x, y);
11274 DrawLevelField(x, y);
11278 if (IS_BLOCKED(x, y))
11282 Blocked2Moving(x, y, &oldx, &oldy);
11283 if (!IS_MOVING(oldx, oldy))
11285 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11286 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11287 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11288 printf("GameActions(): This should never happen!\n");
11295 debug_print_timestamp(0, "- time for pre-main loop:");
11298 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11299 SCAN_PLAYFIELD(x, y)
11301 element = Feld[x][y];
11302 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11307 int element2 = element;
11308 int graphic2 = graphic;
11310 int element2 = Feld[x][y];
11311 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11313 int last_gfx_frame = GfxFrame[x][y];
11315 if (graphic_info[graphic2].anim_global_sync)
11316 GfxFrame[x][y] = FrameCounter;
11317 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11318 GfxFrame[x][y] = CustomValue[x][y];
11319 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11320 GfxFrame[x][y] = element_info[element2].collect_score;
11321 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11322 GfxFrame[x][y] = ChangeDelay[x][y];
11324 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11325 DrawLevelGraphicAnimation(x, y, graphic2);
11328 ResetGfxFrame(x, y, TRUE);
11332 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11333 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11334 ResetRandomAnimationValue(x, y);
11338 SetRandomAnimationValue(x, y);
11342 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11345 #endif // -------------------- !!! TEST ONLY !!! --------------------
11348 debug_print_timestamp(0, "- time for TEST loop: -->");
11351 SCAN_PLAYFIELD(x, y)
11353 element = Feld[x][y];
11354 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11356 ResetGfxFrame(x, y, TRUE);
11358 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11359 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11360 ResetRandomAnimationValue(x, y);
11362 SetRandomAnimationValue(x, y);
11364 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11366 if (IS_INACTIVE(element))
11368 if (IS_ANIMATED(graphic))
11369 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11374 /* this may take place after moving, so 'element' may have changed */
11375 if (IS_CHANGING(x, y) &&
11376 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11378 int page = element_info[element].event_page_nr[CE_DELAY];
11381 HandleElementChange(x, y, page);
11383 if (CAN_CHANGE(element))
11384 HandleElementChange(x, y, page);
11386 if (HAS_ACTION(element))
11387 ExecuteCustomElementAction(x, y, element, page);
11390 element = Feld[x][y];
11391 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11394 #if 0 // ---------------------------------------------------------------------
11396 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11400 element = Feld[x][y];
11401 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11403 if (IS_ANIMATED(graphic) &&
11404 !IS_MOVING(x, y) &&
11406 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11408 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11409 DrawTwinkleOnField(x, y);
11411 else if (IS_MOVING(x, y))
11412 ContinueMoving(x, y);
11419 case EL_EM_EXIT_OPEN:
11420 case EL_SP_EXIT_OPEN:
11421 case EL_STEEL_EXIT_OPEN:
11422 case EL_EM_STEEL_EXIT_OPEN:
11423 case EL_SP_TERMINAL:
11424 case EL_SP_TERMINAL_ACTIVE:
11425 case EL_EXTRA_TIME:
11426 case EL_SHIELD_NORMAL:
11427 case EL_SHIELD_DEADLY:
11428 if (IS_ANIMATED(graphic))
11429 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11432 case EL_DYNAMITE_ACTIVE:
11433 case EL_EM_DYNAMITE_ACTIVE:
11434 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11435 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11436 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11437 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11438 case EL_SP_DISK_RED_ACTIVE:
11439 CheckDynamite(x, y);
11442 case EL_AMOEBA_GROWING:
11443 AmoebeWaechst(x, y);
11446 case EL_AMOEBA_SHRINKING:
11447 AmoebaDisappearing(x, y);
11450 #if !USE_NEW_AMOEBA_CODE
11451 case EL_AMOEBA_WET:
11452 case EL_AMOEBA_DRY:
11453 case EL_AMOEBA_FULL:
11455 case EL_EMC_DRIPPER:
11456 AmoebeAbleger(x, y);
11460 case EL_GAME_OF_LIFE:
11465 case EL_EXIT_CLOSED:
11469 case EL_EM_EXIT_CLOSED:
11473 case EL_STEEL_EXIT_CLOSED:
11474 CheckExitSteel(x, y);
11477 case EL_EM_STEEL_EXIT_CLOSED:
11478 CheckExitSteelEM(x, y);
11481 case EL_SP_EXIT_CLOSED:
11485 case EL_EXPANDABLE_WALL_GROWING:
11486 case EL_EXPANDABLE_STEELWALL_GROWING:
11487 MauerWaechst(x, y);
11490 case EL_EXPANDABLE_WALL:
11491 case EL_EXPANDABLE_WALL_HORIZONTAL:
11492 case EL_EXPANDABLE_WALL_VERTICAL:
11493 case EL_EXPANDABLE_WALL_ANY:
11494 case EL_BD_EXPANDABLE_WALL:
11495 MauerAbleger(x, y);
11498 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11499 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11500 case EL_EXPANDABLE_STEELWALL_ANY:
11501 MauerAblegerStahl(x, y);
11505 CheckForDragon(x, y);
11511 case EL_ELEMENT_SNAPPING:
11512 case EL_DIAGONAL_SHRINKING:
11513 case EL_DIAGONAL_GROWING:
11516 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11518 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11523 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11524 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11529 #else // ---------------------------------------------------------------------
11531 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11535 element = Feld[x][y];
11536 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11538 if (IS_ANIMATED(graphic) &&
11539 !IS_MOVING(x, y) &&
11541 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11543 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11544 DrawTwinkleOnField(x, y);
11546 else if ((element == EL_ACID ||
11547 element == EL_EXIT_OPEN ||
11548 element == EL_EM_EXIT_OPEN ||
11549 element == EL_SP_EXIT_OPEN ||
11550 element == EL_STEEL_EXIT_OPEN ||
11551 element == EL_EM_STEEL_EXIT_OPEN ||
11552 element == EL_SP_TERMINAL ||
11553 element == EL_SP_TERMINAL_ACTIVE ||
11554 element == EL_EXTRA_TIME ||
11555 element == EL_SHIELD_NORMAL ||
11556 element == EL_SHIELD_DEADLY) &&
11557 IS_ANIMATED(graphic))
11558 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11559 else if (IS_MOVING(x, y))
11560 ContinueMoving(x, y);
11561 else if (IS_ACTIVE_BOMB(element))
11562 CheckDynamite(x, y);
11563 else if (element == EL_AMOEBA_GROWING)
11564 AmoebeWaechst(x, y);
11565 else if (element == EL_AMOEBA_SHRINKING)
11566 AmoebaDisappearing(x, y);
11568 #if !USE_NEW_AMOEBA_CODE
11569 else if (IS_AMOEBALIVE(element))
11570 AmoebeAbleger(x, y);
11573 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11575 else if (element == EL_EXIT_CLOSED)
11577 else if (element == EL_EM_EXIT_CLOSED)
11579 else if (element == EL_STEEL_EXIT_CLOSED)
11580 CheckExitSteel(x, y);
11581 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11582 CheckExitSteelEM(x, y);
11583 else if (element == EL_SP_EXIT_CLOSED)
11585 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11586 element == EL_EXPANDABLE_STEELWALL_GROWING)
11587 MauerWaechst(x, y);
11588 else if (element == EL_EXPANDABLE_WALL ||
11589 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11590 element == EL_EXPANDABLE_WALL_VERTICAL ||
11591 element == EL_EXPANDABLE_WALL_ANY ||
11592 element == EL_BD_EXPANDABLE_WALL)
11593 MauerAbleger(x, y);
11594 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11595 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11596 element == EL_EXPANDABLE_STEELWALL_ANY)
11597 MauerAblegerStahl(x, y);
11598 else if (element == EL_FLAMES)
11599 CheckForDragon(x, y);
11600 else if (element == EL_EXPLOSION)
11601 ; /* drawing of correct explosion animation is handled separately */
11602 else if (element == EL_ELEMENT_SNAPPING ||
11603 element == EL_DIAGONAL_SHRINKING ||
11604 element == EL_DIAGONAL_GROWING)
11606 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11608 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11610 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11611 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11613 #endif // ---------------------------------------------------------------------
11615 if (IS_BELT_ACTIVE(element))
11616 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11618 if (game.magic_wall_active)
11620 int jx = local_player->jx, jy = local_player->jy;
11622 /* play the element sound at the position nearest to the player */
11623 if ((element == EL_MAGIC_WALL_FULL ||
11624 element == EL_MAGIC_WALL_ACTIVE ||
11625 element == EL_MAGIC_WALL_EMPTYING ||
11626 element == EL_BD_MAGIC_WALL_FULL ||
11627 element == EL_BD_MAGIC_WALL_ACTIVE ||
11628 element == EL_BD_MAGIC_WALL_EMPTYING ||
11629 element == EL_DC_MAGIC_WALL_FULL ||
11630 element == EL_DC_MAGIC_WALL_ACTIVE ||
11631 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11632 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11641 debug_print_timestamp(0, "- time for MAIN loop: -->");
11644 #if USE_NEW_AMOEBA_CODE
11645 /* new experimental amoeba growth stuff */
11646 if (!(FrameCounter % 8))
11648 static unsigned long random = 1684108901;
11650 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11652 x = RND(lev_fieldx);
11653 y = RND(lev_fieldy);
11654 element = Feld[x][y];
11656 if (!IS_PLAYER(x,y) &&
11657 (element == EL_EMPTY ||
11658 CAN_GROW_INTO(element) ||
11659 element == EL_QUICKSAND_EMPTY ||
11660 element == EL_QUICKSAND_FAST_EMPTY ||
11661 element == EL_ACID_SPLASH_LEFT ||
11662 element == EL_ACID_SPLASH_RIGHT))
11664 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11665 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11666 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11667 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11668 Feld[x][y] = EL_AMOEBA_DROP;
11671 random = random * 129 + 1;
11677 if (game.explosions_delayed)
11680 game.explosions_delayed = FALSE;
11682 SCAN_PLAYFIELD(x, y)
11684 element = Feld[x][y];
11686 if (ExplodeField[x][y])
11687 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11688 else if (element == EL_EXPLOSION)
11689 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11691 ExplodeField[x][y] = EX_TYPE_NONE;
11694 game.explosions_delayed = TRUE;
11697 if (game.magic_wall_active)
11699 if (!(game.magic_wall_time_left % 4))
11701 int element = Feld[magic_wall_x][magic_wall_y];
11703 if (element == EL_BD_MAGIC_WALL_FULL ||
11704 element == EL_BD_MAGIC_WALL_ACTIVE ||
11705 element == EL_BD_MAGIC_WALL_EMPTYING)
11706 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11707 else if (element == EL_DC_MAGIC_WALL_FULL ||
11708 element == EL_DC_MAGIC_WALL_ACTIVE ||
11709 element == EL_DC_MAGIC_WALL_EMPTYING)
11710 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11712 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11715 if (game.magic_wall_time_left > 0)
11717 game.magic_wall_time_left--;
11718 if (!game.magic_wall_time_left)
11720 SCAN_PLAYFIELD(x, y)
11722 element = Feld[x][y];
11724 if (element == EL_MAGIC_WALL_ACTIVE ||
11725 element == EL_MAGIC_WALL_FULL)
11727 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11728 DrawLevelField(x, y);
11730 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11731 element == EL_BD_MAGIC_WALL_FULL)
11733 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11734 DrawLevelField(x, y);
11736 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11737 element == EL_DC_MAGIC_WALL_FULL)
11739 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11740 DrawLevelField(x, y);
11744 game.magic_wall_active = FALSE;
11749 if (game.light_time_left > 0)
11751 game.light_time_left--;
11753 if (game.light_time_left == 0)
11754 RedrawAllLightSwitchesAndInvisibleElements();
11757 if (game.timegate_time_left > 0)
11759 game.timegate_time_left--;
11761 if (game.timegate_time_left == 0)
11762 CloseAllOpenTimegates();
11765 if (game.lenses_time_left > 0)
11767 game.lenses_time_left--;
11769 if (game.lenses_time_left == 0)
11770 RedrawAllInvisibleElementsForLenses();
11773 if (game.magnify_time_left > 0)
11775 game.magnify_time_left--;
11777 if (game.magnify_time_left == 0)
11778 RedrawAllInvisibleElementsForMagnifier();
11781 for (i = 0; i < MAX_PLAYERS; i++)
11783 struct PlayerInfo *player = &stored_player[i];
11785 if (SHIELD_ON(player))
11787 if (player->shield_deadly_time_left)
11788 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11789 else if (player->shield_normal_time_left)
11790 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11797 PlayAllPlayersSound();
11799 if (options.debug) /* calculate frames per second */
11801 static unsigned long fps_counter = 0;
11802 static int fps_frames = 0;
11803 unsigned long fps_delay_ms = Counter() - fps_counter;
11807 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11809 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11812 fps_counter = Counter();
11815 redraw_mask |= REDRAW_FPS;
11818 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11820 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11822 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11824 local_player->show_envelope = 0;
11828 debug_print_timestamp(0, "stop main loop profiling ");
11829 printf("----------------------------------------------------------\n");
11832 /* use random number generator in every frame to make it less predictable */
11833 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11837 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11839 int min_x = x, min_y = y, max_x = x, max_y = y;
11842 for (i = 0; i < MAX_PLAYERS; i++)
11844 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11846 if (!stored_player[i].active || &stored_player[i] == player)
11849 min_x = MIN(min_x, jx);
11850 min_y = MIN(min_y, jy);
11851 max_x = MAX(max_x, jx);
11852 max_y = MAX(max_y, jy);
11855 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11858 static boolean AllPlayersInVisibleScreen()
11862 for (i = 0; i < MAX_PLAYERS; i++)
11864 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11866 if (!stored_player[i].active)
11869 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11876 void ScrollLevel(int dx, int dy)
11879 static Bitmap *bitmap_db_field2 = NULL;
11880 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11887 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11888 /* only horizontal XOR vertical scroll direction allowed */
11889 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11894 if (bitmap_db_field2 == NULL)
11895 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11897 /* needed when blitting directly to same bitmap -- should not be needed with
11898 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11899 BlitBitmap(drawto_field, bitmap_db_field2,
11900 FX + TILEX * (dx == -1) - softscroll_offset,
11901 FY + TILEY * (dy == -1) - softscroll_offset,
11902 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11903 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11904 FX + TILEX * (dx == 1) - softscroll_offset,
11905 FY + TILEY * (dy == 1) - softscroll_offset);
11906 BlitBitmap(bitmap_db_field2, drawto_field,
11907 FX + TILEX * (dx == 1) - softscroll_offset,
11908 FY + TILEY * (dy == 1) - softscroll_offset,
11909 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11910 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11911 FX + TILEX * (dx == 1) - softscroll_offset,
11912 FY + TILEY * (dy == 1) - softscroll_offset);
11917 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11918 int xsize = (BX2 - BX1 + 1);
11919 int ysize = (BY2 - BY1 + 1);
11920 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11921 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11922 int step = (start < end ? +1 : -1);
11924 for (i = start; i != end; i += step)
11926 BlitBitmap(drawto_field, drawto_field,
11927 FX + TILEX * (dx != 0 ? i + step : 0),
11928 FY + TILEY * (dy != 0 ? i + step : 0),
11929 TILEX * (dx != 0 ? 1 : xsize),
11930 TILEY * (dy != 0 ? 1 : ysize),
11931 FX + TILEX * (dx != 0 ? i : 0),
11932 FY + TILEY * (dy != 0 ? i : 0));
11937 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11939 BlitBitmap(drawto_field, drawto_field,
11940 FX + TILEX * (dx == -1) - softscroll_offset,
11941 FY + TILEY * (dy == -1) - softscroll_offset,
11942 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11943 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11944 FX + TILEX * (dx == 1) - softscroll_offset,
11945 FY + TILEY * (dy == 1) - softscroll_offset);
11951 x = (dx == 1 ? BX1 : BX2);
11952 for (y = BY1; y <= BY2; y++)
11953 DrawScreenField(x, y);
11958 y = (dy == 1 ? BY1 : BY2);
11959 for (x = BX1; x <= BX2; x++)
11960 DrawScreenField(x, y);
11963 redraw_mask |= REDRAW_FIELD;
11966 static boolean canFallDown(struct PlayerInfo *player)
11968 int jx = player->jx, jy = player->jy;
11970 return (IN_LEV_FIELD(jx, jy + 1) &&
11971 (IS_FREE(jx, jy + 1) ||
11972 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11973 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11974 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11977 static boolean canPassField(int x, int y, int move_dir)
11979 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11980 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11981 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11982 int nextx = x + dx;
11983 int nexty = y + dy;
11984 int element = Feld[x][y];
11986 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11987 !CAN_MOVE(element) &&
11988 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11989 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11990 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11993 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11995 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11996 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11997 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12001 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12002 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12003 (IS_DIGGABLE(Feld[newx][newy]) ||
12004 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12005 canPassField(newx, newy, move_dir)));
12008 static void CheckGravityMovement(struct PlayerInfo *player)
12010 #if USE_PLAYER_GRAVITY
12011 if (player->gravity && !player->programmed_action)
12013 if (game.gravity && !player->programmed_action)
12016 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12017 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12018 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12019 int jx = player->jx, jy = player->jy;
12020 boolean player_is_moving_to_valid_field =
12021 (!player_is_snapping &&
12022 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12023 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12024 boolean player_can_fall_down = canFallDown(player);
12026 if (player_can_fall_down &&
12027 !player_is_moving_to_valid_field)
12028 player->programmed_action = MV_DOWN;
12032 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12034 return CheckGravityMovement(player);
12036 #if USE_PLAYER_GRAVITY
12037 if (player->gravity && !player->programmed_action)
12039 if (game.gravity && !player->programmed_action)
12042 int jx = player->jx, jy = player->jy;
12043 boolean field_under_player_is_free =
12044 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12045 boolean player_is_standing_on_valid_field =
12046 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12047 (IS_WALKABLE(Feld[jx][jy]) &&
12048 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12050 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12051 player->programmed_action = MV_DOWN;
12056 MovePlayerOneStep()
12057 -----------------------------------------------------------------------------
12058 dx, dy: direction (non-diagonal) to try to move the player to
12059 real_dx, real_dy: direction as read from input device (can be diagonal)
12062 boolean MovePlayerOneStep(struct PlayerInfo *player,
12063 int dx, int dy, int real_dx, int real_dy)
12065 int jx = player->jx, jy = player->jy;
12066 int new_jx = jx + dx, new_jy = jy + dy;
12067 #if !USE_FIXED_DONT_RUN_INTO
12071 boolean player_can_move = !player->cannot_move;
12073 if (!player->active || (!dx && !dy))
12074 return MP_NO_ACTION;
12076 player->MovDir = (dx < 0 ? MV_LEFT :
12077 dx > 0 ? MV_RIGHT :
12079 dy > 0 ? MV_DOWN : MV_NONE);
12081 if (!IN_LEV_FIELD(new_jx, new_jy))
12082 return MP_NO_ACTION;
12084 if (!player_can_move)
12086 if (player->MovPos == 0)
12088 player->is_moving = FALSE;
12089 player->is_digging = FALSE;
12090 player->is_collecting = FALSE;
12091 player->is_snapping = FALSE;
12092 player->is_pushing = FALSE;
12097 if (!options.network && game.centered_player_nr == -1 &&
12098 !AllPlayersInSight(player, new_jx, new_jy))
12099 return MP_NO_ACTION;
12101 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12102 return MP_NO_ACTION;
12105 #if !USE_FIXED_DONT_RUN_INTO
12106 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12108 /* (moved to DigField()) */
12109 if (player_can_move && DONT_RUN_INTO(element))
12111 if (element == EL_ACID && dx == 0 && dy == 1)
12113 SplashAcid(new_jx, new_jy);
12114 Feld[jx][jy] = EL_PLAYER_1;
12115 InitMovingField(jx, jy, MV_DOWN);
12116 Store[jx][jy] = EL_ACID;
12117 ContinueMoving(jx, jy);
12118 BuryPlayer(player);
12121 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12127 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12128 if (can_move != MP_MOVING)
12131 /* check if DigField() has caused relocation of the player */
12132 if (player->jx != jx || player->jy != jy)
12133 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12135 StorePlayer[jx][jy] = 0;
12136 player->last_jx = jx;
12137 player->last_jy = jy;
12138 player->jx = new_jx;
12139 player->jy = new_jy;
12140 StorePlayer[new_jx][new_jy] = player->element_nr;
12142 if (player->move_delay_value_next != -1)
12144 player->move_delay_value = player->move_delay_value_next;
12145 player->move_delay_value_next = -1;
12149 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12151 player->step_counter++;
12153 PlayerVisit[jx][jy] = FrameCounter;
12155 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12156 player->is_moving = TRUE;
12160 /* should better be called in MovePlayer(), but this breaks some tapes */
12161 ScrollPlayer(player, SCROLL_INIT);
12167 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12169 int jx = player->jx, jy = player->jy;
12170 int old_jx = jx, old_jy = jy;
12171 int moved = MP_NO_ACTION;
12173 if (!player->active)
12178 if (player->MovPos == 0)
12180 player->is_moving = FALSE;
12181 player->is_digging = FALSE;
12182 player->is_collecting = FALSE;
12183 player->is_snapping = FALSE;
12184 player->is_pushing = FALSE;
12190 if (player->move_delay > 0)
12193 player->move_delay = -1; /* set to "uninitialized" value */
12195 /* store if player is automatically moved to next field */
12196 player->is_auto_moving = (player->programmed_action != MV_NONE);
12198 /* remove the last programmed player action */
12199 player->programmed_action = 0;
12201 if (player->MovPos)
12203 /* should only happen if pre-1.2 tape recordings are played */
12204 /* this is only for backward compatibility */
12206 int original_move_delay_value = player->move_delay_value;
12209 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12213 /* scroll remaining steps with finest movement resolution */
12214 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12216 while (player->MovPos)
12218 ScrollPlayer(player, SCROLL_GO_ON);
12219 ScrollScreen(NULL, SCROLL_GO_ON);
12221 AdvanceFrameAndPlayerCounters(player->index_nr);
12227 player->move_delay_value = original_move_delay_value;
12230 player->is_active = FALSE;
12232 if (player->last_move_dir & MV_HORIZONTAL)
12234 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12235 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12239 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12240 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12243 #if USE_FIXED_BORDER_RUNNING_GFX
12244 if (!moved && !player->is_active)
12246 player->is_moving = FALSE;
12247 player->is_digging = FALSE;
12248 player->is_collecting = FALSE;
12249 player->is_snapping = FALSE;
12250 player->is_pushing = FALSE;
12258 if (moved & MP_MOVING && !ScreenMovPos &&
12259 (player->index_nr == game.centered_player_nr ||
12260 game.centered_player_nr == -1))
12262 if (moved & MP_MOVING && !ScreenMovPos &&
12263 (player == local_player || !options.network))
12266 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12267 int offset = game.scroll_delay_value;
12269 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12271 /* actual player has left the screen -- scroll in that direction */
12272 if (jx != old_jx) /* player has moved horizontally */
12273 scroll_x += (jx - old_jx);
12274 else /* player has moved vertically */
12275 scroll_y += (jy - old_jy);
12279 if (jx != old_jx) /* player has moved horizontally */
12281 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12282 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12283 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12285 /* don't scroll over playfield boundaries */
12286 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12287 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12289 /* don't scroll more than one field at a time */
12290 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12292 /* don't scroll against the player's moving direction */
12293 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12294 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12295 scroll_x = old_scroll_x;
12297 else /* player has moved vertically */
12299 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12300 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12301 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12303 /* don't scroll over playfield boundaries */
12304 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12305 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12307 /* don't scroll more than one field at a time */
12308 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12310 /* don't scroll against the player's moving direction */
12311 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12312 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12313 scroll_y = old_scroll_y;
12317 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12320 if (!options.network && game.centered_player_nr == -1 &&
12321 !AllPlayersInVisibleScreen())
12323 scroll_x = old_scroll_x;
12324 scroll_y = old_scroll_y;
12328 if (!options.network && !AllPlayersInVisibleScreen())
12330 scroll_x = old_scroll_x;
12331 scroll_y = old_scroll_y;
12336 ScrollScreen(player, SCROLL_INIT);
12337 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12342 player->StepFrame = 0;
12344 if (moved & MP_MOVING)
12346 if (old_jx != jx && old_jy == jy)
12347 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12348 else if (old_jx == jx && old_jy != jy)
12349 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12351 DrawLevelField(jx, jy); /* for "crumbled sand" */
12353 player->last_move_dir = player->MovDir;
12354 player->is_moving = TRUE;
12355 player->is_snapping = FALSE;
12356 player->is_switching = FALSE;
12357 player->is_dropping = FALSE;
12358 player->is_dropping_pressed = FALSE;
12359 player->drop_pressed_delay = 0;
12362 /* should better be called here than above, but this breaks some tapes */
12363 ScrollPlayer(player, SCROLL_INIT);
12368 CheckGravityMovementWhenNotMoving(player);
12370 player->is_moving = FALSE;
12372 /* at this point, the player is allowed to move, but cannot move right now
12373 (e.g. because of something blocking the way) -- ensure that the player
12374 is also allowed to move in the next frame (in old versions before 3.1.1,
12375 the player was forced to wait again for eight frames before next try) */
12377 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12378 player->move_delay = 0; /* allow direct movement in the next frame */
12381 if (player->move_delay == -1) /* not yet initialized by DigField() */
12382 player->move_delay = player->move_delay_value;
12384 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12386 TestIfPlayerTouchesBadThing(jx, jy);
12387 TestIfPlayerTouchesCustomElement(jx, jy);
12390 if (!player->active)
12391 RemovePlayer(player);
12396 void ScrollPlayer(struct PlayerInfo *player, int mode)
12398 int jx = player->jx, jy = player->jy;
12399 int last_jx = player->last_jx, last_jy = player->last_jy;
12400 int move_stepsize = TILEX / player->move_delay_value;
12402 #if USE_NEW_PLAYER_SPEED
12403 if (!player->active)
12406 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12409 if (!player->active || player->MovPos == 0)
12413 if (mode == SCROLL_INIT)
12415 player->actual_frame_counter = FrameCounter;
12416 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12418 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12419 Feld[last_jx][last_jy] == EL_EMPTY)
12421 int last_field_block_delay = 0; /* start with no blocking at all */
12422 int block_delay_adjustment = player->block_delay_adjustment;
12424 /* if player blocks last field, add delay for exactly one move */
12425 if (player->block_last_field)
12427 last_field_block_delay += player->move_delay_value;
12429 /* when blocking enabled, prevent moving up despite gravity */
12430 #if USE_PLAYER_GRAVITY
12431 if (player->gravity && player->MovDir == MV_UP)
12432 block_delay_adjustment = -1;
12434 if (game.gravity && player->MovDir == MV_UP)
12435 block_delay_adjustment = -1;
12439 /* add block delay adjustment (also possible when not blocking) */
12440 last_field_block_delay += block_delay_adjustment;
12442 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12443 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12446 #if USE_NEW_PLAYER_SPEED
12447 if (player->MovPos != 0) /* player has not yet reached destination */
12453 else if (!FrameReached(&player->actual_frame_counter, 1))
12456 #if USE_NEW_PLAYER_SPEED
12457 if (player->MovPos != 0)
12459 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12460 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12462 /* before DrawPlayer() to draw correct player graphic for this case */
12463 if (player->MovPos == 0)
12464 CheckGravityMovement(player);
12467 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12468 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12470 /* before DrawPlayer() to draw correct player graphic for this case */
12471 if (player->MovPos == 0)
12472 CheckGravityMovement(player);
12475 if (player->MovPos == 0) /* player reached destination field */
12477 if (player->move_delay_reset_counter > 0)
12479 player->move_delay_reset_counter--;
12481 if (player->move_delay_reset_counter == 0)
12483 /* continue with normal speed after quickly moving through gate */
12484 HALVE_PLAYER_SPEED(player);
12486 /* be able to make the next move without delay */
12487 player->move_delay = 0;
12491 player->last_jx = jx;
12492 player->last_jy = jy;
12494 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12495 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12496 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12497 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12498 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12499 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12501 DrawPlayer(player); /* needed here only to cleanup last field */
12502 RemovePlayer(player);
12504 if (local_player->friends_still_needed == 0 ||
12505 IS_SP_ELEMENT(Feld[jx][jy]))
12506 PlayerWins(player);
12509 /* this breaks one level: "machine", level 000 */
12511 int move_direction = player->MovDir;
12512 int enter_side = MV_DIR_OPPOSITE(move_direction);
12513 int leave_side = move_direction;
12514 int old_jx = last_jx;
12515 int old_jy = last_jy;
12516 int old_element = Feld[old_jx][old_jy];
12517 int new_element = Feld[jx][jy];
12519 if (IS_CUSTOM_ELEMENT(old_element))
12520 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12522 player->index_bit, leave_side);
12524 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12525 CE_PLAYER_LEAVES_X,
12526 player->index_bit, leave_side);
12528 if (IS_CUSTOM_ELEMENT(new_element))
12529 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12530 player->index_bit, enter_side);
12532 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12533 CE_PLAYER_ENTERS_X,
12534 player->index_bit, enter_side);
12536 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12537 CE_MOVE_OF_X, move_direction);
12540 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12542 TestIfPlayerTouchesBadThing(jx, jy);
12543 TestIfPlayerTouchesCustomElement(jx, jy);
12545 /* needed because pushed element has not yet reached its destination,
12546 so it would trigger a change event at its previous field location */
12547 if (!player->is_pushing)
12548 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12550 if (!player->active)
12551 RemovePlayer(player);
12554 if (!local_player->LevelSolved && level.use_step_counter)
12564 if (TimeLeft <= 10 && setup.time_limit)
12565 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12568 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12570 DisplayGameControlValues();
12572 DrawGameValue_Time(TimeLeft);
12575 if (!TimeLeft && setup.time_limit)
12576 for (i = 0; i < MAX_PLAYERS; i++)
12577 KillPlayer(&stored_player[i]);
12580 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12582 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12584 DisplayGameControlValues();
12587 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12588 DrawGameValue_Time(TimePlayed);
12592 if (tape.single_step && tape.recording && !tape.pausing &&
12593 !player->programmed_action)
12594 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12598 void ScrollScreen(struct PlayerInfo *player, int mode)
12600 static unsigned long screen_frame_counter = 0;
12602 if (mode == SCROLL_INIT)
12604 /* set scrolling step size according to actual player's moving speed */
12605 ScrollStepSize = TILEX / player->move_delay_value;
12607 screen_frame_counter = FrameCounter;
12608 ScreenMovDir = player->MovDir;
12609 ScreenMovPos = player->MovPos;
12610 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12613 else if (!FrameReached(&screen_frame_counter, 1))
12618 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12619 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12620 redraw_mask |= REDRAW_FIELD;
12623 ScreenMovDir = MV_NONE;
12626 void TestIfPlayerTouchesCustomElement(int x, int y)
12628 static int xy[4][2] =
12635 static int trigger_sides[4][2] =
12637 /* center side border side */
12638 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12639 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12640 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12641 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12643 static int touch_dir[4] =
12645 MV_LEFT | MV_RIGHT,
12650 int center_element = Feld[x][y]; /* should always be non-moving! */
12653 for (i = 0; i < NUM_DIRECTIONS; i++)
12655 int xx = x + xy[i][0];
12656 int yy = y + xy[i][1];
12657 int center_side = trigger_sides[i][0];
12658 int border_side = trigger_sides[i][1];
12659 int border_element;
12661 if (!IN_LEV_FIELD(xx, yy))
12664 if (IS_PLAYER(x, y))
12666 struct PlayerInfo *player = PLAYERINFO(x, y);
12668 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12669 border_element = Feld[xx][yy]; /* may be moving! */
12670 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12671 border_element = Feld[xx][yy];
12672 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12673 border_element = MovingOrBlocked2Element(xx, yy);
12675 continue; /* center and border element do not touch */
12677 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12678 player->index_bit, border_side);
12679 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12680 CE_PLAYER_TOUCHES_X,
12681 player->index_bit, border_side);
12683 else if (IS_PLAYER(xx, yy))
12685 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12687 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12689 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12690 continue; /* center and border element do not touch */
12693 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12694 player->index_bit, center_side);
12695 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12696 CE_PLAYER_TOUCHES_X,
12697 player->index_bit, center_side);
12703 #if USE_ELEMENT_TOUCHING_BUGFIX
12705 void TestIfElementTouchesCustomElement(int x, int y)
12707 static int xy[4][2] =
12714 static int trigger_sides[4][2] =
12716 /* center side border side */
12717 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12718 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12719 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12720 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12722 static int touch_dir[4] =
12724 MV_LEFT | MV_RIGHT,
12729 boolean change_center_element = FALSE;
12730 int center_element = Feld[x][y]; /* should always be non-moving! */
12731 int border_element_old[NUM_DIRECTIONS];
12734 for (i = 0; i < NUM_DIRECTIONS; i++)
12736 int xx = x + xy[i][0];
12737 int yy = y + xy[i][1];
12738 int border_element;
12740 border_element_old[i] = -1;
12742 if (!IN_LEV_FIELD(xx, yy))
12745 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12746 border_element = Feld[xx][yy]; /* may be moving! */
12747 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12748 border_element = Feld[xx][yy];
12749 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12750 border_element = MovingOrBlocked2Element(xx, yy);
12752 continue; /* center and border element do not touch */
12754 border_element_old[i] = border_element;
12757 for (i = 0; i < NUM_DIRECTIONS; i++)
12759 int xx = x + xy[i][0];
12760 int yy = y + xy[i][1];
12761 int center_side = trigger_sides[i][0];
12762 int border_element = border_element_old[i];
12764 if (border_element == -1)
12767 /* check for change of border element */
12768 CheckElementChangeBySide(xx, yy, border_element, center_element,
12769 CE_TOUCHING_X, center_side);
12772 for (i = 0; i < NUM_DIRECTIONS; i++)
12774 int border_side = trigger_sides[i][1];
12775 int border_element = border_element_old[i];
12777 if (border_element == -1)
12780 /* check for change of center element (but change it only once) */
12781 if (!change_center_element)
12782 change_center_element =
12783 CheckElementChangeBySide(x, y, center_element, border_element,
12784 CE_TOUCHING_X, border_side);
12790 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12792 static int xy[4][2] =
12799 static int trigger_sides[4][2] =
12801 /* center side border side */
12802 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12803 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12804 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12805 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12807 static int touch_dir[4] =
12809 MV_LEFT | MV_RIGHT,
12814 boolean change_center_element = FALSE;
12815 int center_element = Feld[x][y]; /* should always be non-moving! */
12818 for (i = 0; i < NUM_DIRECTIONS; i++)
12820 int xx = x + xy[i][0];
12821 int yy = y + xy[i][1];
12822 int center_side = trigger_sides[i][0];
12823 int border_side = trigger_sides[i][1];
12824 int border_element;
12826 if (!IN_LEV_FIELD(xx, yy))
12829 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12830 border_element = Feld[xx][yy]; /* may be moving! */
12831 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12832 border_element = Feld[xx][yy];
12833 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12834 border_element = MovingOrBlocked2Element(xx, yy);
12836 continue; /* center and border element do not touch */
12838 /* check for change of center element (but change it only once) */
12839 if (!change_center_element)
12840 change_center_element =
12841 CheckElementChangeBySide(x, y, center_element, border_element,
12842 CE_TOUCHING_X, border_side);
12844 /* check for change of border element */
12845 CheckElementChangeBySide(xx, yy, border_element, center_element,
12846 CE_TOUCHING_X, center_side);
12852 void TestIfElementHitsCustomElement(int x, int y, int direction)
12854 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12855 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12856 int hitx = x + dx, hity = y + dy;
12857 int hitting_element = Feld[x][y];
12858 int touched_element;
12860 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12863 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12864 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12866 if (IN_LEV_FIELD(hitx, hity))
12868 int opposite_direction = MV_DIR_OPPOSITE(direction);
12869 int hitting_side = direction;
12870 int touched_side = opposite_direction;
12871 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12872 MovDir[hitx][hity] != direction ||
12873 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12879 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12880 CE_HITTING_X, touched_side);
12882 CheckElementChangeBySide(hitx, hity, touched_element,
12883 hitting_element, CE_HIT_BY_X, hitting_side);
12885 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12886 CE_HIT_BY_SOMETHING, opposite_direction);
12890 /* "hitting something" is also true when hitting the playfield border */
12891 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12892 CE_HITTING_SOMETHING, direction);
12896 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12898 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12899 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12900 int hitx = x + dx, hity = y + dy;
12901 int hitting_element = Feld[x][y];
12902 int touched_element;
12904 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12905 !IS_FREE(hitx, hity) &&
12906 (!IS_MOVING(hitx, hity) ||
12907 MovDir[hitx][hity] != direction ||
12908 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12911 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12915 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12919 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12920 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12922 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12923 EP_CAN_SMASH_EVERYTHING, direction);
12925 if (IN_LEV_FIELD(hitx, hity))
12927 int opposite_direction = MV_DIR_OPPOSITE(direction);
12928 int hitting_side = direction;
12929 int touched_side = opposite_direction;
12931 int touched_element = MovingOrBlocked2Element(hitx, hity);
12934 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12935 MovDir[hitx][hity] != direction ||
12936 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12945 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12946 CE_SMASHED_BY_SOMETHING, opposite_direction);
12948 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12949 CE_OTHER_IS_SMASHING, touched_side);
12951 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12952 CE_OTHER_GETS_SMASHED, hitting_side);
12958 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12960 int i, kill_x = -1, kill_y = -1;
12962 int bad_element = -1;
12963 static int test_xy[4][2] =
12970 static int test_dir[4] =
12978 for (i = 0; i < NUM_DIRECTIONS; i++)
12980 int test_x, test_y, test_move_dir, test_element;
12982 test_x = good_x + test_xy[i][0];
12983 test_y = good_y + test_xy[i][1];
12985 if (!IN_LEV_FIELD(test_x, test_y))
12989 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12991 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12993 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12994 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12996 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12997 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13001 bad_element = test_element;
13007 if (kill_x != -1 || kill_y != -1)
13009 if (IS_PLAYER(good_x, good_y))
13011 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13013 if (player->shield_deadly_time_left > 0 &&
13014 !IS_INDESTRUCTIBLE(bad_element))
13015 Bang(kill_x, kill_y);
13016 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13017 KillPlayer(player);
13020 Bang(good_x, good_y);
13024 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13026 int i, kill_x = -1, kill_y = -1;
13027 int bad_element = Feld[bad_x][bad_y];
13028 static int test_xy[4][2] =
13035 static int touch_dir[4] =
13037 MV_LEFT | MV_RIGHT,
13042 static int test_dir[4] =
13050 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13053 for (i = 0; i < NUM_DIRECTIONS; i++)
13055 int test_x, test_y, test_move_dir, test_element;
13057 test_x = bad_x + test_xy[i][0];
13058 test_y = bad_y + test_xy[i][1];
13059 if (!IN_LEV_FIELD(test_x, test_y))
13063 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13065 test_element = Feld[test_x][test_y];
13067 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13068 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13070 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13071 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13073 /* good thing is player or penguin that does not move away */
13074 if (IS_PLAYER(test_x, test_y))
13076 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13078 if (bad_element == EL_ROBOT && player->is_moving)
13079 continue; /* robot does not kill player if he is moving */
13081 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13083 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13084 continue; /* center and border element do not touch */
13091 else if (test_element == EL_PENGUIN)
13100 if (kill_x != -1 || kill_y != -1)
13102 if (IS_PLAYER(kill_x, kill_y))
13104 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13106 if (player->shield_deadly_time_left > 0 &&
13107 !IS_INDESTRUCTIBLE(bad_element))
13108 Bang(bad_x, bad_y);
13109 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13110 KillPlayer(player);
13113 Bang(kill_x, kill_y);
13117 void TestIfPlayerTouchesBadThing(int x, int y)
13119 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13122 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13124 TestIfGoodThingHitsBadThing(x, y, move_dir);
13127 void TestIfBadThingTouchesPlayer(int x, int y)
13129 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13132 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13134 TestIfBadThingHitsGoodThing(x, y, move_dir);
13137 void TestIfFriendTouchesBadThing(int x, int y)
13139 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13142 void TestIfBadThingTouchesFriend(int x, int y)
13144 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13147 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13149 int i, kill_x = bad_x, kill_y = bad_y;
13150 static int xy[4][2] =
13158 for (i = 0; i < NUM_DIRECTIONS; i++)
13162 x = bad_x + xy[i][0];
13163 y = bad_y + xy[i][1];
13164 if (!IN_LEV_FIELD(x, y))
13167 element = Feld[x][y];
13168 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13169 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13177 if (kill_x != bad_x || kill_y != bad_y)
13178 Bang(bad_x, bad_y);
13181 void KillPlayer(struct PlayerInfo *player)
13183 int jx = player->jx, jy = player->jy;
13185 if (!player->active)
13188 /* the following code was introduced to prevent an infinite loop when calling
13190 -> CheckTriggeredElementChangeExt()
13191 -> ExecuteCustomElementAction()
13193 -> (infinitely repeating the above sequence of function calls)
13194 which occurs when killing the player while having a CE with the setting
13195 "kill player X when explosion of <player X>"; the solution using a new
13196 field "player->killed" was chosen for backwards compatibility, although
13197 clever use of the fields "player->active" etc. would probably also work */
13199 if (player->killed)
13203 player->killed = TRUE;
13205 /* remove accessible field at the player's position */
13206 Feld[jx][jy] = EL_EMPTY;
13208 /* deactivate shield (else Bang()/Explode() would not work right) */
13209 player->shield_normal_time_left = 0;
13210 player->shield_deadly_time_left = 0;
13213 BuryPlayer(player);
13216 static void KillPlayerUnlessEnemyProtected(int x, int y)
13218 if (!PLAYER_ENEMY_PROTECTED(x, y))
13219 KillPlayer(PLAYERINFO(x, y));
13222 static void KillPlayerUnlessExplosionProtected(int x, int y)
13224 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13225 KillPlayer(PLAYERINFO(x, y));
13228 void BuryPlayer(struct PlayerInfo *player)
13230 int jx = player->jx, jy = player->jy;
13232 if (!player->active)
13235 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13236 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13238 player->GameOver = TRUE;
13239 RemovePlayer(player);
13242 void RemovePlayer(struct PlayerInfo *player)
13244 int jx = player->jx, jy = player->jy;
13245 int i, found = FALSE;
13247 player->present = FALSE;
13248 player->active = FALSE;
13250 if (!ExplodeField[jx][jy])
13251 StorePlayer[jx][jy] = 0;
13253 if (player->is_moving)
13254 DrawLevelField(player->last_jx, player->last_jy);
13256 for (i = 0; i < MAX_PLAYERS; i++)
13257 if (stored_player[i].active)
13261 AllPlayersGone = TRUE;
13267 #if USE_NEW_SNAP_DELAY
13268 static void setFieldForSnapping(int x, int y, int element, int direction)
13270 struct ElementInfo *ei = &element_info[element];
13271 int direction_bit = MV_DIR_TO_BIT(direction);
13272 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13273 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13274 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13276 Feld[x][y] = EL_ELEMENT_SNAPPING;
13277 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13279 ResetGfxAnimation(x, y);
13281 GfxElement[x][y] = element;
13282 GfxAction[x][y] = action;
13283 GfxDir[x][y] = direction;
13284 GfxFrame[x][y] = -1;
13289 =============================================================================
13290 checkDiagonalPushing()
13291 -----------------------------------------------------------------------------
13292 check if diagonal input device direction results in pushing of object
13293 (by checking if the alternative direction is walkable, diggable, ...)
13294 =============================================================================
13297 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13298 int x, int y, int real_dx, int real_dy)
13300 int jx, jy, dx, dy, xx, yy;
13302 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13305 /* diagonal direction: check alternative direction */
13310 xx = jx + (dx == 0 ? real_dx : 0);
13311 yy = jy + (dy == 0 ? real_dy : 0);
13313 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13317 =============================================================================
13319 -----------------------------------------------------------------------------
13320 x, y: field next to player (non-diagonal) to try to dig to
13321 real_dx, real_dy: direction as read from input device (can be diagonal)
13322 =============================================================================
13325 int DigField(struct PlayerInfo *player,
13326 int oldx, int oldy, int x, int y,
13327 int real_dx, int real_dy, int mode)
13329 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13330 boolean player_was_pushing = player->is_pushing;
13331 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13332 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13333 int jx = oldx, jy = oldy;
13334 int dx = x - jx, dy = y - jy;
13335 int nextx = x + dx, nexty = y + dy;
13336 int move_direction = (dx == -1 ? MV_LEFT :
13337 dx == +1 ? MV_RIGHT :
13339 dy == +1 ? MV_DOWN : MV_NONE);
13340 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13341 int dig_side = MV_DIR_OPPOSITE(move_direction);
13342 int old_element = Feld[jx][jy];
13343 #if USE_FIXED_DONT_RUN_INTO
13344 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13350 if (is_player) /* function can also be called by EL_PENGUIN */
13352 if (player->MovPos == 0)
13354 player->is_digging = FALSE;
13355 player->is_collecting = FALSE;
13358 if (player->MovPos == 0) /* last pushing move finished */
13359 player->is_pushing = FALSE;
13361 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13363 player->is_switching = FALSE;
13364 player->push_delay = -1;
13366 return MP_NO_ACTION;
13370 #if !USE_FIXED_DONT_RUN_INTO
13371 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13372 return MP_NO_ACTION;
13375 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13376 old_element = Back[jx][jy];
13378 /* in case of element dropped at player position, check background */
13379 else if (Back[jx][jy] != EL_EMPTY &&
13380 game.engine_version >= VERSION_IDENT(2,2,0,0))
13381 old_element = Back[jx][jy];
13383 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13384 return MP_NO_ACTION; /* field has no opening in this direction */
13386 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13387 return MP_NO_ACTION; /* field has no opening in this direction */
13389 #if USE_FIXED_DONT_RUN_INTO
13390 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13394 Feld[jx][jy] = player->artwork_element;
13395 InitMovingField(jx, jy, MV_DOWN);
13396 Store[jx][jy] = EL_ACID;
13397 ContinueMoving(jx, jy);
13398 BuryPlayer(player);
13400 return MP_DONT_RUN_INTO;
13404 #if USE_FIXED_DONT_RUN_INTO
13405 if (player_can_move && DONT_RUN_INTO(element))
13407 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13409 return MP_DONT_RUN_INTO;
13413 #if USE_FIXED_DONT_RUN_INTO
13414 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13415 return MP_NO_ACTION;
13418 #if !USE_FIXED_DONT_RUN_INTO
13419 element = Feld[x][y];
13422 collect_count = element_info[element].collect_count_initial;
13424 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13425 return MP_NO_ACTION;
13427 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13428 player_can_move = player_can_move_or_snap;
13430 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13431 game.engine_version >= VERSION_IDENT(2,2,0,0))
13433 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13434 player->index_bit, dig_side);
13435 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13436 player->index_bit, dig_side);
13438 if (element == EL_DC_LANDMINE)
13441 if (Feld[x][y] != element) /* field changed by snapping */
13444 return MP_NO_ACTION;
13447 #if USE_PLAYER_GRAVITY
13448 if (player->gravity && is_player && !player->is_auto_moving &&
13449 canFallDown(player) && move_direction != MV_DOWN &&
13450 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13451 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13453 if (game.gravity && is_player && !player->is_auto_moving &&
13454 canFallDown(player) && move_direction != MV_DOWN &&
13455 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13456 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13459 if (player_can_move &&
13460 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13462 int sound_element = SND_ELEMENT(element);
13463 int sound_action = ACTION_WALKING;
13465 if (IS_RND_GATE(element))
13467 if (!player->key[RND_GATE_NR(element)])
13468 return MP_NO_ACTION;
13470 else if (IS_RND_GATE_GRAY(element))
13472 if (!player->key[RND_GATE_GRAY_NR(element)])
13473 return MP_NO_ACTION;
13475 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13477 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13478 return MP_NO_ACTION;
13480 else if (element == EL_EXIT_OPEN ||
13481 element == EL_EM_EXIT_OPEN ||
13482 element == EL_STEEL_EXIT_OPEN ||
13483 element == EL_EM_STEEL_EXIT_OPEN ||
13484 element == EL_SP_EXIT_OPEN ||
13485 element == EL_SP_EXIT_OPENING)
13487 sound_action = ACTION_PASSING; /* player is passing exit */
13489 else if (element == EL_EMPTY)
13491 sound_action = ACTION_MOVING; /* nothing to walk on */
13494 /* play sound from background or player, whatever is available */
13495 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13496 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13498 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13500 else if (player_can_move &&
13501 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13503 if (!ACCESS_FROM(element, opposite_direction))
13504 return MP_NO_ACTION; /* field not accessible from this direction */
13506 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13507 return MP_NO_ACTION;
13509 if (IS_EM_GATE(element))
13511 if (!player->key[EM_GATE_NR(element)])
13512 return MP_NO_ACTION;
13514 else if (IS_EM_GATE_GRAY(element))
13516 if (!player->key[EM_GATE_GRAY_NR(element)])
13517 return MP_NO_ACTION;
13519 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13521 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13522 return MP_NO_ACTION;
13524 else if (IS_EMC_GATE(element))
13526 if (!player->key[EMC_GATE_NR(element)])
13527 return MP_NO_ACTION;
13529 else if (IS_EMC_GATE_GRAY(element))
13531 if (!player->key[EMC_GATE_GRAY_NR(element)])
13532 return MP_NO_ACTION;
13534 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13536 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13537 return MP_NO_ACTION;
13539 else if (element == EL_DC_GATE_WHITE ||
13540 element == EL_DC_GATE_WHITE_GRAY ||
13541 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13543 if (player->num_white_keys == 0)
13544 return MP_NO_ACTION;
13546 player->num_white_keys--;
13548 else if (IS_SP_PORT(element))
13550 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13551 element == EL_SP_GRAVITY_PORT_RIGHT ||
13552 element == EL_SP_GRAVITY_PORT_UP ||
13553 element == EL_SP_GRAVITY_PORT_DOWN)
13554 #if USE_PLAYER_GRAVITY
13555 player->gravity = !player->gravity;
13557 game.gravity = !game.gravity;
13559 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13560 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13561 element == EL_SP_GRAVITY_ON_PORT_UP ||
13562 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13563 #if USE_PLAYER_GRAVITY
13564 player->gravity = TRUE;
13566 game.gravity = TRUE;
13568 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13569 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13570 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13571 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13572 #if USE_PLAYER_GRAVITY
13573 player->gravity = FALSE;
13575 game.gravity = FALSE;
13579 /* automatically move to the next field with double speed */
13580 player->programmed_action = move_direction;
13582 if (player->move_delay_reset_counter == 0)
13584 player->move_delay_reset_counter = 2; /* two double speed steps */
13586 DOUBLE_PLAYER_SPEED(player);
13589 PlayLevelSoundAction(x, y, ACTION_PASSING);
13591 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13595 if (mode != DF_SNAP)
13597 GfxElement[x][y] = GFX_ELEMENT(element);
13598 player->is_digging = TRUE;
13601 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13603 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13604 player->index_bit, dig_side);
13606 if (mode == DF_SNAP)
13608 #if USE_NEW_SNAP_DELAY
13609 if (level.block_snap_field)
13610 setFieldForSnapping(x, y, element, move_direction);
13612 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13614 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13617 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13618 player->index_bit, dig_side);
13621 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13625 if (is_player && mode != DF_SNAP)
13627 GfxElement[x][y] = element;
13628 player->is_collecting = TRUE;
13631 if (element == EL_SPEED_PILL)
13633 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13635 else if (element == EL_EXTRA_TIME && level.time > 0)
13637 TimeLeft += level.extra_time;
13640 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13642 DisplayGameControlValues();
13644 DrawGameValue_Time(TimeLeft);
13647 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13649 player->shield_normal_time_left += level.shield_normal_time;
13650 if (element == EL_SHIELD_DEADLY)
13651 player->shield_deadly_time_left += level.shield_deadly_time;
13653 else if (element == EL_DYNAMITE ||
13654 element == EL_EM_DYNAMITE ||
13655 element == EL_SP_DISK_RED)
13657 if (player->inventory_size < MAX_INVENTORY_SIZE)
13658 player->inventory_element[player->inventory_size++] = element;
13660 DrawGameDoorValues();
13662 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13664 player->dynabomb_count++;
13665 player->dynabombs_left++;
13667 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13669 player->dynabomb_size++;
13671 else if (element == EL_DYNABOMB_INCREASE_POWER)
13673 player->dynabomb_xl = TRUE;
13675 else if (IS_KEY(element))
13677 player->key[KEY_NR(element)] = TRUE;
13679 DrawGameDoorValues();
13681 else if (element == EL_DC_KEY_WHITE)
13683 player->num_white_keys++;
13685 /* display white keys? */
13686 /* DrawGameDoorValues(); */
13688 else if (IS_ENVELOPE(element))
13690 player->show_envelope = element;
13692 else if (element == EL_EMC_LENSES)
13694 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13696 RedrawAllInvisibleElementsForLenses();
13698 else if (element == EL_EMC_MAGNIFIER)
13700 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13702 RedrawAllInvisibleElementsForMagnifier();
13704 else if (IS_DROPPABLE(element) ||
13705 IS_THROWABLE(element)) /* can be collected and dropped */
13709 if (collect_count == 0)
13710 player->inventory_infinite_element = element;
13712 for (i = 0; i < collect_count; i++)
13713 if (player->inventory_size < MAX_INVENTORY_SIZE)
13714 player->inventory_element[player->inventory_size++] = element;
13716 DrawGameDoorValues();
13718 else if (collect_count > 0)
13720 local_player->gems_still_needed -= collect_count;
13721 if (local_player->gems_still_needed < 0)
13722 local_player->gems_still_needed = 0;
13725 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13727 DisplayGameControlValues();
13729 DrawGameValue_Emeralds(local_player->gems_still_needed);
13733 RaiseScoreElement(element);
13734 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13737 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13738 player->index_bit, dig_side);
13740 if (mode == DF_SNAP)
13742 #if USE_NEW_SNAP_DELAY
13743 if (level.block_snap_field)
13744 setFieldForSnapping(x, y, element, move_direction);
13746 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13748 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13751 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13752 player->index_bit, dig_side);
13755 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13757 if (mode == DF_SNAP && element != EL_BD_ROCK)
13758 return MP_NO_ACTION;
13760 if (CAN_FALL(element) && dy)
13761 return MP_NO_ACTION;
13763 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13764 !(element == EL_SPRING && level.use_spring_bug))
13765 return MP_NO_ACTION;
13767 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13768 ((move_direction & MV_VERTICAL &&
13769 ((element_info[element].move_pattern & MV_LEFT &&
13770 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13771 (element_info[element].move_pattern & MV_RIGHT &&
13772 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13773 (move_direction & MV_HORIZONTAL &&
13774 ((element_info[element].move_pattern & MV_UP &&
13775 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13776 (element_info[element].move_pattern & MV_DOWN &&
13777 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13778 return MP_NO_ACTION;
13780 /* do not push elements already moving away faster than player */
13781 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13782 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13783 return MP_NO_ACTION;
13785 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13787 if (player->push_delay_value == -1 || !player_was_pushing)
13788 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13790 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13792 if (player->push_delay_value == -1)
13793 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13795 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13797 if (!player->is_pushing)
13798 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13801 player->is_pushing = TRUE;
13802 player->is_active = TRUE;
13804 if (!(IN_LEV_FIELD(nextx, nexty) &&
13805 (IS_FREE(nextx, nexty) ||
13806 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13807 IS_SB_ELEMENT(element)))))
13808 return MP_NO_ACTION;
13810 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13811 return MP_NO_ACTION;
13813 if (player->push_delay == -1) /* new pushing; restart delay */
13814 player->push_delay = 0;
13816 if (player->push_delay < player->push_delay_value &&
13817 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13818 element != EL_SPRING && element != EL_BALLOON)
13820 /* make sure that there is no move delay before next try to push */
13821 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13822 player->move_delay = 0;
13824 return MP_NO_ACTION;
13827 if (IS_SB_ELEMENT(element))
13829 if (element == EL_SOKOBAN_FIELD_FULL)
13831 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13832 local_player->sokobanfields_still_needed++;
13835 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13837 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13838 local_player->sokobanfields_still_needed--;
13841 Feld[x][y] = EL_SOKOBAN_OBJECT;
13843 if (Back[x][y] == Back[nextx][nexty])
13844 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13845 else if (Back[x][y] != 0)
13846 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13849 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13852 if (local_player->sokobanfields_still_needed == 0 &&
13853 game.emulation == EMU_SOKOBAN)
13855 PlayerWins(player);
13857 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13861 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13863 InitMovingField(x, y, move_direction);
13864 GfxAction[x][y] = ACTION_PUSHING;
13866 if (mode == DF_SNAP)
13867 ContinueMoving(x, y);
13869 MovPos[x][y] = (dx != 0 ? dx : dy);
13871 Pushed[x][y] = TRUE;
13872 Pushed[nextx][nexty] = TRUE;
13874 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13875 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13877 player->push_delay_value = -1; /* get new value later */
13879 /* check for element change _after_ element has been pushed */
13880 if (game.use_change_when_pushing_bug)
13882 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13883 player->index_bit, dig_side);
13884 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13885 player->index_bit, dig_side);
13888 else if (IS_SWITCHABLE(element))
13890 if (PLAYER_SWITCHING(player, x, y))
13892 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13893 player->index_bit, dig_side);
13898 player->is_switching = TRUE;
13899 player->switch_x = x;
13900 player->switch_y = y;
13902 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13904 if (element == EL_ROBOT_WHEEL)
13906 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13910 DrawLevelField(x, y);
13912 else if (element == EL_SP_TERMINAL)
13916 SCAN_PLAYFIELD(xx, yy)
13918 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13920 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13921 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13924 else if (IS_BELT_SWITCH(element))
13926 ToggleBeltSwitch(x, y);
13928 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13929 element == EL_SWITCHGATE_SWITCH_DOWN ||
13930 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13931 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13933 ToggleSwitchgateSwitch(x, y);
13935 else if (element == EL_LIGHT_SWITCH ||
13936 element == EL_LIGHT_SWITCH_ACTIVE)
13938 ToggleLightSwitch(x, y);
13940 else if (element == EL_TIMEGATE_SWITCH ||
13941 element == EL_DC_TIMEGATE_SWITCH)
13943 ActivateTimegateSwitch(x, y);
13945 else if (element == EL_BALLOON_SWITCH_LEFT ||
13946 element == EL_BALLOON_SWITCH_RIGHT ||
13947 element == EL_BALLOON_SWITCH_UP ||
13948 element == EL_BALLOON_SWITCH_DOWN ||
13949 element == EL_BALLOON_SWITCH_NONE ||
13950 element == EL_BALLOON_SWITCH_ANY)
13952 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13953 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13954 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13955 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13956 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13959 else if (element == EL_LAMP)
13961 Feld[x][y] = EL_LAMP_ACTIVE;
13962 local_player->lights_still_needed--;
13964 ResetGfxAnimation(x, y);
13965 DrawLevelField(x, y);
13967 else if (element == EL_TIME_ORB_FULL)
13969 Feld[x][y] = EL_TIME_ORB_EMPTY;
13971 if (level.time > 0 || level.use_time_orb_bug)
13973 TimeLeft += level.time_orb_time;
13976 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13978 DisplayGameControlValues();
13980 DrawGameValue_Time(TimeLeft);
13984 ResetGfxAnimation(x, y);
13985 DrawLevelField(x, y);
13987 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13988 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13992 game.ball_state = !game.ball_state;
13994 SCAN_PLAYFIELD(xx, yy)
13996 int e = Feld[xx][yy];
13998 if (game.ball_state)
14000 if (e == EL_EMC_MAGIC_BALL)
14001 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14002 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14003 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14007 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14008 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14009 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14010 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14015 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14016 player->index_bit, dig_side);
14018 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14019 player->index_bit, dig_side);
14021 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14022 player->index_bit, dig_side);
14028 if (!PLAYER_SWITCHING(player, x, y))
14030 player->is_switching = TRUE;
14031 player->switch_x = x;
14032 player->switch_y = y;
14034 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14035 player->index_bit, dig_side);
14036 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14037 player->index_bit, dig_side);
14039 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14040 player->index_bit, dig_side);
14041 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14042 player->index_bit, dig_side);
14045 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14046 player->index_bit, dig_side);
14047 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14048 player->index_bit, dig_side);
14050 return MP_NO_ACTION;
14053 player->push_delay = -1;
14055 if (is_player) /* function can also be called by EL_PENGUIN */
14057 if (Feld[x][y] != element) /* really digged/collected something */
14059 player->is_collecting = !player->is_digging;
14060 player->is_active = TRUE;
14067 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14069 int jx = player->jx, jy = player->jy;
14070 int x = jx + dx, y = jy + dy;
14071 int snap_direction = (dx == -1 ? MV_LEFT :
14072 dx == +1 ? MV_RIGHT :
14074 dy == +1 ? MV_DOWN : MV_NONE);
14075 boolean can_continue_snapping = (level.continuous_snapping &&
14076 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14078 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14081 if (!player->active || !IN_LEV_FIELD(x, y))
14089 if (player->MovPos == 0)
14090 player->is_pushing = FALSE;
14092 player->is_snapping = FALSE;
14094 if (player->MovPos == 0)
14096 player->is_moving = FALSE;
14097 player->is_digging = FALSE;
14098 player->is_collecting = FALSE;
14104 #if USE_NEW_CONTINUOUS_SNAPPING
14105 /* prevent snapping with already pressed snap key when not allowed */
14106 if (player->is_snapping && !can_continue_snapping)
14109 if (player->is_snapping)
14113 player->MovDir = snap_direction;
14115 if (player->MovPos == 0)
14117 player->is_moving = FALSE;
14118 player->is_digging = FALSE;
14119 player->is_collecting = FALSE;
14122 player->is_dropping = FALSE;
14123 player->is_dropping_pressed = FALSE;
14124 player->drop_pressed_delay = 0;
14126 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14129 player->is_snapping = TRUE;
14130 player->is_active = TRUE;
14132 if (player->MovPos == 0)
14134 player->is_moving = FALSE;
14135 player->is_digging = FALSE;
14136 player->is_collecting = FALSE;
14139 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14140 DrawLevelField(player->last_jx, player->last_jy);
14142 DrawLevelField(x, y);
14147 boolean DropElement(struct PlayerInfo *player)
14149 int old_element, new_element;
14150 int dropx = player->jx, dropy = player->jy;
14151 int drop_direction = player->MovDir;
14152 int drop_side = drop_direction;
14154 int drop_element = get_next_drop_element(player);
14156 int drop_element = (player->inventory_size > 0 ?
14157 player->inventory_element[player->inventory_size - 1] :
14158 player->inventory_infinite_element != EL_UNDEFINED ?
14159 player->inventory_infinite_element :
14160 player->dynabombs_left > 0 ?
14161 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14165 player->is_dropping_pressed = TRUE;
14167 /* do not drop an element on top of another element; when holding drop key
14168 pressed without moving, dropped element must move away before the next
14169 element can be dropped (this is especially important if the next element
14170 is dynamite, which can be placed on background for historical reasons) */
14171 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14174 if (IS_THROWABLE(drop_element))
14176 dropx += GET_DX_FROM_DIR(drop_direction);
14177 dropy += GET_DY_FROM_DIR(drop_direction);
14179 if (!IN_LEV_FIELD(dropx, dropy))
14183 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14184 new_element = drop_element; /* default: no change when dropping */
14186 /* check if player is active, not moving and ready to drop */
14187 if (!player->active || player->MovPos || player->drop_delay > 0)
14190 /* check if player has anything that can be dropped */
14191 if (new_element == EL_UNDEFINED)
14194 /* check if drop key was pressed long enough for EM style dynamite */
14195 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14198 /* check if anything can be dropped at the current position */
14199 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14202 /* collected custom elements can only be dropped on empty fields */
14203 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14206 if (old_element != EL_EMPTY)
14207 Back[dropx][dropy] = old_element; /* store old element on this field */
14209 ResetGfxAnimation(dropx, dropy);
14210 ResetRandomAnimationValue(dropx, dropy);
14212 if (player->inventory_size > 0 ||
14213 player->inventory_infinite_element != EL_UNDEFINED)
14215 if (player->inventory_size > 0)
14217 player->inventory_size--;
14219 DrawGameDoorValues();
14221 if (new_element == EL_DYNAMITE)
14222 new_element = EL_DYNAMITE_ACTIVE;
14223 else if (new_element == EL_EM_DYNAMITE)
14224 new_element = EL_EM_DYNAMITE_ACTIVE;
14225 else if (new_element == EL_SP_DISK_RED)
14226 new_element = EL_SP_DISK_RED_ACTIVE;
14229 Feld[dropx][dropy] = new_element;
14231 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14232 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14233 el2img(Feld[dropx][dropy]), 0);
14235 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14237 /* needed if previous element just changed to "empty" in the last frame */
14238 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14240 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14241 player->index_bit, drop_side);
14242 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14244 player->index_bit, drop_side);
14246 TestIfElementTouchesCustomElement(dropx, dropy);
14248 else /* player is dropping a dyna bomb */
14250 player->dynabombs_left--;
14252 Feld[dropx][dropy] = new_element;
14254 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14255 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14256 el2img(Feld[dropx][dropy]), 0);
14258 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14261 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14262 InitField_WithBug1(dropx, dropy, FALSE);
14264 new_element = Feld[dropx][dropy]; /* element might have changed */
14266 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14267 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14269 int move_direction, nextx, nexty;
14271 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14272 MovDir[dropx][dropy] = drop_direction;
14274 move_direction = MovDir[dropx][dropy];
14275 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14276 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14278 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14280 #if USE_FIX_IMPACT_COLLISION
14281 /* do not cause impact style collision by dropping elements that can fall */
14282 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14284 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14288 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14289 player->is_dropping = TRUE;
14291 player->drop_pressed_delay = 0;
14292 player->is_dropping_pressed = FALSE;
14294 player->drop_x = dropx;
14295 player->drop_y = dropy;
14300 /* ------------------------------------------------------------------------- */
14301 /* game sound playing functions */
14302 /* ------------------------------------------------------------------------- */
14304 static int *loop_sound_frame = NULL;
14305 static int *loop_sound_volume = NULL;
14307 void InitPlayLevelSound()
14309 int num_sounds = getSoundListSize();
14311 checked_free(loop_sound_frame);
14312 checked_free(loop_sound_volume);
14314 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14315 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14318 static void PlayLevelSound(int x, int y, int nr)
14320 int sx = SCREENX(x), sy = SCREENY(y);
14321 int volume, stereo_position;
14322 int max_distance = 8;
14323 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14325 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14326 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14329 if (!IN_LEV_FIELD(x, y) ||
14330 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14331 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14334 volume = SOUND_MAX_VOLUME;
14336 if (!IN_SCR_FIELD(sx, sy))
14338 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14339 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14341 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14344 stereo_position = (SOUND_MAX_LEFT +
14345 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14346 (SCR_FIELDX + 2 * max_distance));
14348 if (IS_LOOP_SOUND(nr))
14350 /* This assures that quieter loop sounds do not overwrite louder ones,
14351 while restarting sound volume comparison with each new game frame. */
14353 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14356 loop_sound_volume[nr] = volume;
14357 loop_sound_frame[nr] = FrameCounter;
14360 PlaySoundExt(nr, volume, stereo_position, type);
14363 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14365 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14366 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14367 y < LEVELY(BY1) ? LEVELY(BY1) :
14368 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14372 static void PlayLevelSoundAction(int x, int y, int action)
14374 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14377 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14379 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14381 if (sound_effect != SND_UNDEFINED)
14382 PlayLevelSound(x, y, sound_effect);
14385 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14388 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14390 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14391 PlayLevelSound(x, y, sound_effect);
14394 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14396 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14398 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14399 PlayLevelSound(x, y, sound_effect);
14402 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14404 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14406 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14407 StopSound(sound_effect);
14410 static void PlayLevelMusic()
14412 if (levelset.music[level_nr] != MUS_UNDEFINED)
14413 PlayMusic(levelset.music[level_nr]); /* from config file */
14415 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14418 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14420 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14421 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14422 int x = xx - 1 - offset;
14423 int y = yy - 1 - offset;
14428 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14432 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14436 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14440 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14444 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14448 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14452 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14455 case SAMPLE_android_clone:
14456 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14459 case SAMPLE_android_move:
14460 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14463 case SAMPLE_spring:
14464 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14468 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14472 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14475 case SAMPLE_eater_eat:
14476 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14480 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14483 case SAMPLE_collect:
14484 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14487 case SAMPLE_diamond:
14488 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14491 case SAMPLE_squash:
14492 /* !!! CHECK THIS !!! */
14494 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14496 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14500 case SAMPLE_wonderfall:
14501 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14505 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14509 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14513 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14517 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14521 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14525 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14528 case SAMPLE_wonder:
14529 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14533 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14536 case SAMPLE_exit_open:
14537 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14540 case SAMPLE_exit_leave:
14541 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14544 case SAMPLE_dynamite:
14545 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14549 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14553 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14557 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14561 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14565 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14569 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14573 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14579 void ChangeTime(int value)
14581 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14585 /* EMC game engine uses value from time counter of RND game engine */
14586 level.native_em_level->lev->time = *time;
14588 DrawGameValue_Time(*time);
14591 void RaiseScore(int value)
14593 /* EMC game engine and RND game engine have separate score counters */
14594 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14595 &level.native_em_level->lev->score : &local_player->score);
14599 DrawGameValue_Score(*score);
14603 void RaiseScore(int value)
14605 local_player->score += value;
14608 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14610 DisplayGameControlValues();
14612 DrawGameValue_Score(local_player->score);
14616 void RaiseScoreElement(int element)
14621 case EL_BD_DIAMOND:
14622 case EL_EMERALD_YELLOW:
14623 case EL_EMERALD_RED:
14624 case EL_EMERALD_PURPLE:
14625 case EL_SP_INFOTRON:
14626 RaiseScore(level.score[SC_EMERALD]);
14629 RaiseScore(level.score[SC_DIAMOND]);
14632 RaiseScore(level.score[SC_CRYSTAL]);
14635 RaiseScore(level.score[SC_PEARL]);
14638 case EL_BD_BUTTERFLY:
14639 case EL_SP_ELECTRON:
14640 RaiseScore(level.score[SC_BUG]);
14643 case EL_BD_FIREFLY:
14644 case EL_SP_SNIKSNAK:
14645 RaiseScore(level.score[SC_SPACESHIP]);
14648 case EL_DARK_YAMYAM:
14649 RaiseScore(level.score[SC_YAMYAM]);
14652 RaiseScore(level.score[SC_ROBOT]);
14655 RaiseScore(level.score[SC_PACMAN]);
14658 RaiseScore(level.score[SC_NUT]);
14661 case EL_EM_DYNAMITE:
14662 case EL_SP_DISK_RED:
14663 case EL_DYNABOMB_INCREASE_NUMBER:
14664 case EL_DYNABOMB_INCREASE_SIZE:
14665 case EL_DYNABOMB_INCREASE_POWER:
14666 RaiseScore(level.score[SC_DYNAMITE]);
14668 case EL_SHIELD_NORMAL:
14669 case EL_SHIELD_DEADLY:
14670 RaiseScore(level.score[SC_SHIELD]);
14672 case EL_EXTRA_TIME:
14673 RaiseScore(level.extra_time_score);
14687 case EL_DC_KEY_WHITE:
14688 RaiseScore(level.score[SC_KEY]);
14691 RaiseScore(element_info[element].collect_score);
14696 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14698 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14700 #if defined(NETWORK_AVALIABLE)
14701 if (options.network)
14702 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14711 FadeSkipNextFadeIn();
14713 fading = fading_none;
14717 OpenDoor(DOOR_CLOSE_1);
14720 game_status = GAME_MODE_MAIN;
14723 DrawAndFadeInMainMenu(REDRAW_FIELD);
14731 FadeOut(REDRAW_FIELD);
14734 game_status = GAME_MODE_MAIN;
14736 DrawAndFadeInMainMenu(REDRAW_FIELD);
14740 else /* continue playing the game */
14742 if (tape.playing && tape.deactivate_display)
14743 TapeDeactivateDisplayOff(TRUE);
14745 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14747 if (tape.playing && tape.deactivate_display)
14748 TapeDeactivateDisplayOn();
14752 void RequestQuitGame(boolean ask_if_really_quit)
14754 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14755 boolean skip_request = AllPlayersGone || quick_quit;
14757 RequestQuitGameExt(skip_request, quick_quit,
14758 "Do you really want to quit the game ?");
14762 /* ------------------------------------------------------------------------- */
14763 /* random generator functions */
14764 /* ------------------------------------------------------------------------- */
14766 unsigned int InitEngineRandom_RND(long seed)
14768 game.num_random_calls = 0;
14771 unsigned int rnd_seed = InitEngineRandom(seed);
14773 printf("::: START RND: %d\n", rnd_seed);
14778 return InitEngineRandom(seed);
14784 unsigned int RND(int max)
14788 game.num_random_calls++;
14790 return GetEngineRandom(max);
14797 /* ------------------------------------------------------------------------- */
14798 /* game engine snapshot handling functions */
14799 /* ------------------------------------------------------------------------- */
14801 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14803 struct EngineSnapshotInfo
14805 /* runtime values for custom element collect score */
14806 int collect_score[NUM_CUSTOM_ELEMENTS];
14808 /* runtime values for group element choice position */
14809 int choice_pos[NUM_GROUP_ELEMENTS];
14811 /* runtime values for belt position animations */
14812 int belt_graphic[4 * NUM_BELT_PARTS];
14813 int belt_anim_mode[4 * NUM_BELT_PARTS];
14816 struct EngineSnapshotNodeInfo
14823 static struct EngineSnapshotInfo engine_snapshot_rnd;
14824 static ListNode *engine_snapshot_list = NULL;
14825 static char *snapshot_level_identifier = NULL;
14826 static int snapshot_level_nr = -1;
14828 void FreeEngineSnapshot()
14830 while (engine_snapshot_list != NULL)
14831 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14834 setString(&snapshot_level_identifier, NULL);
14835 snapshot_level_nr = -1;
14838 static void SaveEngineSnapshotValues_RND()
14840 static int belt_base_active_element[4] =
14842 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14843 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14844 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14845 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14849 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14851 int element = EL_CUSTOM_START + i;
14853 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14856 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14858 int element = EL_GROUP_START + i;
14860 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14863 for (i = 0; i < 4; i++)
14865 for (j = 0; j < NUM_BELT_PARTS; j++)
14867 int element = belt_base_active_element[i] + j;
14868 int graphic = el2img(element);
14869 int anim_mode = graphic_info[graphic].anim_mode;
14871 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14872 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14877 static void LoadEngineSnapshotValues_RND()
14879 unsigned long num_random_calls = game.num_random_calls;
14882 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14884 int element = EL_CUSTOM_START + i;
14886 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14889 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14891 int element = EL_GROUP_START + i;
14893 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14896 for (i = 0; i < 4; i++)
14898 for (j = 0; j < NUM_BELT_PARTS; j++)
14900 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14901 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14903 graphic_info[graphic].anim_mode = anim_mode;
14907 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14909 InitRND(tape.random_seed);
14910 for (i = 0; i < num_random_calls; i++)
14914 if (game.num_random_calls != num_random_calls)
14916 Error(ERR_INFO, "number of random calls out of sync");
14917 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14918 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14919 Error(ERR_EXIT, "this should not happen -- please debug");
14923 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14925 struct EngineSnapshotNodeInfo *bi =
14926 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14928 bi->buffer_orig = buffer;
14929 bi->buffer_copy = checked_malloc(size);
14932 memcpy(bi->buffer_copy, buffer, size);
14934 addNodeToList(&engine_snapshot_list, NULL, bi);
14937 void SaveEngineSnapshot()
14939 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14941 if (level_editor_test_game) /* do not save snapshots from editor */
14944 /* copy some special values to a structure better suited for the snapshot */
14946 SaveEngineSnapshotValues_RND();
14947 SaveEngineSnapshotValues_EM();
14949 /* save values stored in special snapshot structure */
14951 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14952 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14954 /* save further RND engine values */
14956 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14957 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14958 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14960 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14961 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14962 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14963 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14965 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14966 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14967 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14968 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14969 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14971 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14972 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14973 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14975 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14977 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14979 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14980 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14982 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14983 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14984 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14985 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14986 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14987 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14988 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14989 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14990 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14991 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14992 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14993 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14994 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14995 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14996 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14997 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14998 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14999 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15001 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15002 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15004 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15005 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15006 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15008 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15009 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15011 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15012 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15013 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15014 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15015 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15017 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15018 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15020 /* save level identification information */
15022 setString(&snapshot_level_identifier, leveldir_current->identifier);
15023 snapshot_level_nr = level_nr;
15026 ListNode *node = engine_snapshot_list;
15029 while (node != NULL)
15031 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15036 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15040 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15042 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15045 void LoadEngineSnapshot()
15047 ListNode *node = engine_snapshot_list;
15049 if (engine_snapshot_list == NULL)
15052 while (node != NULL)
15054 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15059 /* restore special values from snapshot structure */
15061 LoadEngineSnapshotValues_RND();
15062 LoadEngineSnapshotValues_EM();
15065 boolean CheckEngineSnapshot()
15067 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15068 snapshot_level_nr == level_nr);
15072 /* ---------- new game button stuff ---------------------------------------- */
15074 /* graphic position values for game buttons */
15075 #define GAME_BUTTON_XSIZE 30
15076 #define GAME_BUTTON_YSIZE 30
15077 #define GAME_BUTTON_XPOS 5
15078 #define GAME_BUTTON_YPOS 215
15079 #define SOUND_BUTTON_XPOS 5
15080 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15082 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15083 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15084 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15085 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15086 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15087 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15095 } gamebutton_info[NUM_GAME_BUTTONS] =
15099 &game.button.stop.x, &game.button.stop.y,
15100 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15105 &game.button.pause.x, &game.button.pause.y,
15106 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15107 GAME_CTRL_ID_PAUSE,
15111 &game.button.play.x, &game.button.play.y,
15112 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15117 &game.button.sound_music.x, &game.button.sound_music.y,
15118 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15119 SOUND_CTRL_ID_MUSIC,
15120 "background music on/off"
15123 &game.button.sound_loops.x, &game.button.sound_loops.y,
15124 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15125 SOUND_CTRL_ID_LOOPS,
15126 "sound loops on/off"
15129 &game.button.sound_simple.x,&game.button.sound_simple.y,
15130 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15131 SOUND_CTRL_ID_SIMPLE,
15132 "normal sounds on/off"
15136 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15141 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15142 GAME_CTRL_ID_PAUSE,
15146 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15151 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15152 SOUND_CTRL_ID_MUSIC,
15153 "background music on/off"
15156 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15157 SOUND_CTRL_ID_LOOPS,
15158 "sound loops on/off"
15161 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15162 SOUND_CTRL_ID_SIMPLE,
15163 "normal sounds on/off"
15168 void CreateGameButtons()
15172 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15174 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15175 struct GadgetInfo *gi;
15178 unsigned long event_mask;
15180 int gd_xoffset, gd_yoffset;
15181 int gd_x1, gd_x2, gd_y1, gd_y2;
15184 x = DX + *gamebutton_info[i].x;
15185 y = DY + *gamebutton_info[i].y;
15186 gd_xoffset = gamebutton_info[i].gd_x;
15187 gd_yoffset = gamebutton_info[i].gd_y;
15188 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15189 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15191 if (id == GAME_CTRL_ID_STOP ||
15192 id == GAME_CTRL_ID_PAUSE ||
15193 id == GAME_CTRL_ID_PLAY)
15195 button_type = GD_TYPE_NORMAL_BUTTON;
15197 event_mask = GD_EVENT_RELEASED;
15198 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15199 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15203 button_type = GD_TYPE_CHECK_BUTTON;
15205 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15206 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15207 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15208 event_mask = GD_EVENT_PRESSED;
15209 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15210 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15213 gi = CreateGadget(GDI_CUSTOM_ID, id,
15214 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15219 GDI_X, DX + gd_xoffset,
15220 GDI_Y, DY + gd_yoffset,
15222 GDI_WIDTH, GAME_BUTTON_XSIZE,
15223 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15224 GDI_TYPE, button_type,
15225 GDI_STATE, GD_BUTTON_UNPRESSED,
15226 GDI_CHECKED, checked,
15227 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15228 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15229 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15230 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15231 GDI_EVENT_MASK, event_mask,
15232 GDI_CALLBACK_ACTION, HandleGameButtons,
15236 Error(ERR_EXIT, "cannot create gadget");
15238 game_gadget[id] = gi;
15242 void FreeGameButtons()
15246 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15247 FreeGadget(game_gadget[i]);
15250 static void MapGameButtons()
15254 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15255 MapGadget(game_gadget[i]);
15258 void UnmapGameButtons()
15262 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15263 UnmapGadget(game_gadget[i]);
15266 static void HandleGameButtons(struct GadgetInfo *gi)
15268 int id = gi->custom_id;
15270 if (game_status != GAME_MODE_PLAYING)
15275 case GAME_CTRL_ID_STOP:
15279 RequestQuitGame(TRUE);
15282 case GAME_CTRL_ID_PAUSE:
15283 if (options.network)
15285 #if defined(NETWORK_AVALIABLE)
15287 SendToServer_ContinuePlaying();
15289 SendToServer_PausePlaying();
15293 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15296 case GAME_CTRL_ID_PLAY:
15299 #if defined(NETWORK_AVALIABLE)
15300 if (options.network)
15301 SendToServer_ContinuePlaying();
15305 tape.pausing = FALSE;
15306 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15311 case SOUND_CTRL_ID_MUSIC:
15312 if (setup.sound_music)
15314 setup.sound_music = FALSE;
15317 else if (audio.music_available)
15319 setup.sound = setup.sound_music = TRUE;
15321 SetAudioMode(setup.sound);
15327 case SOUND_CTRL_ID_LOOPS:
15328 if (setup.sound_loops)
15329 setup.sound_loops = FALSE;
15330 else if (audio.loops_available)
15332 setup.sound = setup.sound_loops = TRUE;
15333 SetAudioMode(setup.sound);
15337 case SOUND_CTRL_ID_SIMPLE:
15338 if (setup.sound_simple)
15339 setup.sound_simple = FALSE;
15340 else if (audio.sound_available)
15342 setup.sound = setup.sound_simple = TRUE;
15343 SetAudioMode(setup.sound);